【问题标题】:Hibernate SessionFactory and could not obtain transaction-synchronized session for current threadHibernate SessionFactory 无法获取当前线程的事务同步会话
【发布时间】:2015-08-27 16:32:13
【问题描述】:

我知道之前有人问过这个问题,但是没有一个解决方案对我有用。

我正在尝试使用控制器来填充索引。当我尝试在数据库中搜索更新时会出现问题。

这是我正在处理的类:

配置:

@Configuration
@EnableTransactionManagement
public class WebApplication implements WebApplicationContextInitializer, ApplicationContextAware {

    @Bean(name="dataSource")
    public DataSource getDataSource() throws IOException {
        InitialContext initialContext = new Context();
        return (DataSource) initialContext.lookup("java:comp/env/jdbc/myDataSource");
    }

    @Bean(name="sessionFactory")
    public SessionFactory getSessionFactory() throws IOException {
        LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(getDataSource());
            sessionBuilder.scanPackages(PropertyUtil.getInstance().getPropertySplitTrimmed("hibernate", "packagesToScan"));
            sessionBuilder.addProperties(PropertyUtil.getInstance().getProperties("hibernate"));
            return sessionBuilder.buildSessionFactory();
    }

    @Bean(name="transactionManager")
    public HibernateTransactionManager transactionManager() throws IOException {
        return new HibernateTransactionManager(getSessionFactory());
    }
}

控制器:

@RestController
@Transactional
@RequestMapping("/persons")
public class IndexController {
   @Autowired
   PersonsDao personsDoa;

   private ExecutorService executorService = Executors.newFixedThreadPool(100);

  @RequestMapping(value="/index")
  public void populateIndex(@DefaultValue("") @RequestParam String name){
    ...
    ...
    List<Future<Persons>> holder = new ArrayList<>();

    for(Persons p : people){
       String name = p.name();
       Future<Person> f = this.executorService.submit(new Callable<Person>(){
          @Override
          public Person call() throws Exception {
            return personsDao.findByName(name);  // <-- Throws error here
          }
       });
       holder.add(f);  // process the array later once all threads are finished
    }
    ...
    ...
  }
}

更新:我根据一些建议更新了我的控制器,但是我仍然收到同样的错误

控制器:

@RestController
@Transactional
@RequestMapping("/persons")
public class IndexController {
   @Autowired
   PersonsDao personsDoa;

   private ExecutorService executorService = Executors.newFixedThreadPool(100);

  @RequestMapping(value="/index")
  public void populateIndex(@DefaultValue("") @RequestParam String name){
    ...
    ...
    List<Future<Persons>> holder = new ArrayList<>();
    TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(sessionFactory.getCurrentSession()));  //<-- THROWS ERROR HERE
    for(Persons p : people){
       String name = p.name();
       Future<Person> f = this.executorService.submit(new Callable<Person>(){
          SessionHolder holder = (SessionHolder)TransactionSynchronizationManager.getResources(sessionFactory);
          Session session = holder.getSession();
          @Override
          public Person call() throws Exception {
            Transaction t = session.getTransaction();
            t.begin();
            Persons p = personsDao.findByName(name);
            t.commit();
            session.flush();
            return p;
          }
       });
       holder.add(f);  // process the array later once all threads are finished
    }
    ...
    ...
  }
}

【问题讨论】:

  • 您是否尝试将@Transactional 注释放在PersonsDao 中的方法findByName(String name) 上方?
  • 是的。我尝试手动删除注释并管理事务,但仍然出现该错误。我想知道初始设置是否不正确。我尝试获取 currentSession (sessionFactory.getCurrentSession()) 并抛出错误

标签: java multithreading hibernate


【解决方案1】:

通常请求线程只使用一个共享会话,这个会话在请求开始时绑定,在请求结束时解除绑定,但是如果你想在另一个线程中使用它,我们必须:

1_ 防止会话从请求线程关闭。

2_ 将此会话绑定到新线程,以提供 TransactionManager 与同一会话一起工作。

首先当前的Session一定不能关闭,所以如果你使用OpenInViewFilter,你需要在调用新的Thread之前添加一个方法。

OpenEntityManagerInViewFilter.getCurrent().keepEmfOpen(request);            

然后在线程内你需要附加当前会话。

public void attachThread() {// this must bind the session to this thread.
    OpenEntityManagerInViewFilter.getCurrent().registerEmfs(request, session);
}

private boolean registerEmf(String key, ServletRequest request, EntityManagerFactory emf){
        if (emf == null)
            return true;
        if (TransactionSynchronizationManager.hasResource(emf))         
            return true;        
        else {
            boolean isFirstRequest = true;
            WebAsyncManager asyncManager = null;
            if (request!=null){
                asyncManager = WebAsyncUtils.getAsyncManager(request);
                isFirstRequest = !(request instanceof HttpServletRequest) || !isAsyncDispatch((HttpServletRequest) request);
            }
            if (emf.isOpen())
            if (isFirstRequest || !applyEntityManagerBindingInterceptor(asyncManager, key)) {
                logger.debug("Opening JPA EntityManager in OpenEntityManagerInViewFilter");
                try {
                    EntityManager em = createEntityManager( emf );
                    EntityManagerHolder emHolder = new EntityManagerHolder( em );
                    TransactionSynchronizationManager.bindResource( emf, emHolder );
                    if (asyncManager!=null)
                        asyncManager.registerCallableInterceptor( key, new EntityManagerBindingCallableInterceptor( emf, emHolder ) );
                    return false;
                }
                catch (PersistenceException ex) {
                    throw new DataAccessResourceFailureException("Could not create JPA EntityManager", ex);
                }
            }
        }
        return true;
    }

如果是 OpenSessionInViewFilter :

private void openHibernateSessionInView(){
  Session session=SessionFactoryUtils.getSession(sessionFactory,true);
  SessionHolder holder=new SessionHolder(session);
  if (!TransactionSynchronizationManager.hasResource(sessionFactory)) {
    TransactionSynchronizationManager.bindResource(sessionFactory,holder);
  }
}

private void closeHibernateSessionInView(){
  if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
    SessionHolder sessionHolder=(SessionHolder)TransactionSynchronizationManager.unbindResource(sessionFactory);
    if (sessionHolder.getTransaction() != null && !sessionHolder.getTransaction().wasRolledBack() && !sessionHolder.getTransaction().wasCommitted()) {
      sessionHolder.getTransaction().commit();
    }
    SessionFactoryUtils.closeSession(sessionHolder.getSession());
  }
}

【讨论】:

  • 我不相信我们使用的是OpenInViewFilter。另外,我没有使用JpaTransactionManager,而是选择使用HibernateTransactionManager
  • 是的,但是,您应该明白必须将当前会话绑定到线程,并防止在父线程结束请求时关闭会话
  • 尝试传递会话,但我仍然收到该错误
  • 用你的新尝试更新你的问题,我会尽力帮助你
  • 这不是更新,因为我只是出于测试目的尝试过。基本上我参加了当前会话并试图从中获取交易。然而,当要求currentSession 时抛出了错误,这让我相信初始配置不正确
猜你喜欢
  • 2015-02-04
  • 2017-01-01
  • 1970-01-01
  • 2015-09-22
  • 2015-04-11
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
  • 2014-11-28
相关资源
最近更新 更多