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


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

    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

启用S​​pring事务将打印以下日志:

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

6543
2018-05-11 16:53


起源

在配置中包含预定方法可能不是一个好主意。你为什么不尝试创建一个新的Spring服务 UserRatingUpdate 作为依赖和使用 @Scheduled 关于那个新类的方法 - geoand
我从他们的文件中拿走了它 - docs.spring.io/spring/docs/3.1.x/javadoc-api/org/... 他们展示了一种在配置中声明@schedule的方法 - Dejell
是的,但正如我在我的回答中解释的那样,当需要为其中一个依赖项使用代理时,该工作正常 - geoand
还能请你发布代码吗? BaseDAO? - geoand
@geoand我刚发布了它 - Dejell


答案:


正如我已经提到的,我使用了你的代码并创建了一个 小样本 这对我行得通。从使用的类来判断,我假设你正在使用 Hibernate通用DAO框架。这是一个独立的样本, main() 班级是主要的。运行它可以在日志中看到与事务相关的DEBUG消息,这些消息显示启动和提交事务的时间。您可以比较我的设置,使用的罐子版本以及是否有任何突出的东西。

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


3
2018-05-15 09:28





在这种情况下发生的事情是,因为你正在使用 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


4
2018-05-11 17:28



谢谢我试过,我看到任务正在安排,但它不起作用 - 我得到同样的错误 - Dejell
检查我的更新答案 - geoand
htm.setSessionFactory(runnableSessionFactory());无法编译。我应该把它投射到(SessionFactory)吗? - Dejell
我早些时候犯了一个错误,抱歉。检查更新的代码 - geoand
我试过但我又得到了同样的错误:( - Dejell


我试图复制你的问题所以我把它集成在我的Hibernate示例中 GitHub上

您可以运行我的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设置。


2
2018-05-15 08:41



嗨!关于我确保应用程序上下文知道我们的调度程序 - 如果我使用@configuration类,我还需要吗? - Dejell
以来 春季3.1 支持通过基于java的配置设置调度程序设置。 - Vlad Mihalcea