org.hibernate.HibernateException: createQuery is not valid without active transaction @scheduled

I am using scheduled task to update my database like this:

    public interface UserRatingManager {
        public void updateAllUsers();
    }

    @Service
    public class DefaultUserRatingManager implements UserRatingManager {

        @Autowired
        UserRatingDAO userRatingDAO;

            @Override
        @Transactional("txName")
        public void updateAllUsers() {
            List<String> userIds = userRatingDAO.getAllUserIds();
            for (String userId : userIds) {
                updateUserRating(userId);
            }
        }
    }

public interface UserRatingDAO extends GenericDAO<UserRating, String> {
    public void deleteAll();
    public List<String> getAllUserIds();
}

@Repository
public class HibernateUserRatingDAO extends BaseDAO<UserRating, String> implements UserRatingDAO {

    @Override
    public List<String> getAllUserIds() {
        List<String> result = new ArrayList<String>();
        Query q1 = getSession().createQuery("Select userId from UserRating");
    }
}

I configured the persistence like this:

@Configuration
@ComponentScan({ "com.estartup" })
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement
@EnableScheduling
public class PersistenceConfig {

    @Autowired
    Environment env;

    @Scheduled(fixedRate = 5000)
    public void run() {
        userRatingManager().updateAllUsers();
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(env.getProperty("connection.url"), env.getProperty("connection.username"), env.getProperty("connection.password"));
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return driverManagerDataSource;
    }

    public PersistenceConfig() {
        super();
    }


    @Bean
    public UserRatingUpdate userRatingUpdate() {
        return new UserRatingUpdate();
    }

    @Bean
    public UserRatingManager userRatingManager() {
        return new DefaultUserRatingManager();
    }

    @Bean
    public LocalSessionFactoryBean runnableSessionFactory() {
        LocalSessionFactoryBean factoryBean = null;
        try {
            factoryBean = createBaseSessionFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factoryBean;
    }


    private LocalSessionFactoryBean createBaseSessionFactory() throws IOException {
        LocalSessionFactoryBean factoryBean;
        factoryBean = new LocalSessionFactoryBean();
        Properties pp = new Properties();
        pp.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
        pp.setProperty("hibernate.max_fetch_depth", "3");
        pp.setProperty("hibernate.show_sql", "false");
        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan(new String[] { "com.estartup.*" });
        factoryBean.setHibernateProperties(pp);
        factoryBean.afterPropertiesSet();
        return factoryBean;
    }

    @Bean(name = "txName")
    public HibernateTransactionManager runnableTransactionManager() {
        HibernateTransactionManager htm = new HibernateTransactionManager(runnableSessionFactory().getObject());
        return htm;
    }
}

However, when I get to:

Query q1 = getSession().createQuery("Select userId from UserRating"); 

in the above HibernateUserRatingDAO I get an exception:

org.hibernate.HibernateException: createQuery is not valid without active transaction
    at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:352)
    at com.sun.proxy.$Proxy63.createQuery(Unknown Source)
    at com.estartup.dao.impl.HibernateUserRatingDAO.getAllUserIds(HibernateUserRatingDAO.java:36)

How can I configure to include my scheduled tasks in transactions ?

EDITED:

Here is the code for BaseDAO

@Repository
public class BaseDAO<T, ID extends Serializable> extends GenericDAOImpl<T, ID> {

    private static final Logger logger = LoggerFactory.getLogger(BaseDAO.class);

    @Autowired
    @Override
    public void setSessionFactory(SessionFactory sessionFactory) {
        super.setSessionFactory(sessionFactory);
    }

    public void setTopAndForUpdate(int top, Query query){
        query.setLockOptions(LockOptions.UPGRADE);
        query.setFirstResult(0);
        query.setMaxResults(top);
    }

EDITED

Enabling Spring transaction prints the following log:

DEBUG [pool-1-thread-1] org.springframework.transaction.annotation.AnnotationTransactionAttributeSource - Adding transactional method 'updateAllUsers' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; 'txName'

Answer

As I already mentioned, I used your code and created a small sample that works for me. Judging by the classes used, I assumed you are using Hibernate Generic DAO Framework. It’s a standalone sample, the main() class is Main. Running it you can see the transactional related DEBUG messages in logs that show when a transaction is initiated and committed. You can compare my settings, jars versions used with what you have and see if anything stands out.

Also, as I already suggested you might want to look in the logs to see if proper transactional behavior is being used and compare that with the logs my sample creates.

Leave a Reply

Your email address will not be published. Required fields are marked *