org.hibernate.HibernateException:没有活动事务@scheduled,createQuery无效

时间:2014-05-11 16:53:52

标签: java spring hibernate jpa transactions

我正在使用计划任务更新我的数据库,如下所示:

    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");
    }
}

我像这样配置了持久性:

@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;
    }
}

然而,当我到达时:

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

在上面HibernateUserRatingDAO我得到一个例外:

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)

如何配置将我的预定任务包含在交易中?

编辑:

以下是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

启用Spring事务将打印以下日志:

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

3 个答案:

答案 0 :(得分:4)

在这种情况下发生的事情是,由于您在配置中使用userRatingManager()(实际调度的方法存在),Spring为处理UserRatingUpdate的事务管理而创建的代理不是被使用。

我建议你做以下事情:

public interface WhateverService {

   void executeScheduled();
}

@Service
public class WhateverServiceImpl {

   private final UserRatingManager userRatingManager;

   @Autowired
   public WhateverServiceImpl(UserRatingManager userRatingManager) {
      this.userRatingManager = userRatingManager;
   }

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

还要将您的事务管理器配置代码更改为:

    @Bean(name = "txName")
    @Autowired
    public HibernateTransactionManager runnableTransactionManager(SessionFactory sessionFactory) {
        HibernateTransactionManager htm = new HibernateTransactionManager();
        htm.setSessionFactory(sessionFactory);
        return htm;
    }

并从factoryBean.afterPropertiesSet();

中删除createBaseSessionFactory

答案 1 :(得分:3)

正如我已经提到的,我使用了您的代码并创建了一个适合我的small sample。根据使用的类来判断,我假设您正在使用Hibernate Generic DAO Framework。它是一个独立的示例,main()类是Main。运行它可以在日志中看到与事务相关的DEBUG消息,这些消息显示启动和提交事务的时间。您可以比较我的设置,使用的罐子版本以及是否有任何突出的东西。

另外,正如我已经建议的那样,您可能希望查看日志以查看是否正在使用正确的事务行为,并将其与我的示例创建的日志进行比较。

答案 2 :(得分:2)

我尝试复制您的问题,因此我将其集成到GitHub上的Hibernate示例中:

您可以运行我的CompanySchedulerTest并看到它正常运行,这就是我运行它所做的:

  1. 我确保应用程序上下文知道我们的调度程序

    <task:annotation-driven/>    
    
  2. 调度程序在其自己的bean中定义:

    @Service
    public class CompanyScheduler implements DisposableBean {
    
    private static final Logger LOG = LoggerFactory.getLogger(CompanyScheduler.class);
    
    @Autowired
    private CompanyManager companyManager;
    
    private volatile boolean enabled = true;
    
    @Override
    public void destroy() throws Exception {
        enabled = false;
    }
    
    @Scheduled(fixedRate = 100)
    public void run() {
        if (enabled) {
            LOG.info("Run scheduler");
            companyManager.updateAllUsers();
        }
    }
    }
    
  3. 我的JPA / Hibernate配置在applicationContext-test.xml中,根据Spring框架指示为JPA配置它们,所以你可能也需要仔细检查你的Hibernate设置。