本周是时候将我们的代码库升级到最新的Hibernate 4.x了。 我们推迟了迁移(仍在Hibernate 3.3上),因为3.x分支的较新维护版本需要对API进行一些更改,这些更改显然仍在不断变化中。 一个示例是UserType API,该API仍然存在缺陷,将在Hibernate 4中完成。迁移非常顺利。 使UserType适应新界面非常简单。 到处都有一些刺激,但没有痛苦。
需要注意的是Spring集成。 如果您以前将Spring与Hibernate结合使用,则将使用LocalSessionFactoryBean (或AnnotationSessionFactoryBean )创建SessionFactory 。 对于休眠4
在自己的程序包中有一个单独的程序:org.springframework.orm。 hibernate4而不是org.springframework.orm。 休眠3 。 hibernate 4包中的LocalSessionFactoryBean将同时用于映射文件和带注释的实体,因此两种样式都只需要一个。
升级完成后,我们的所有测试都在运行,并且使用本地Hibernate事务管理器在Tomcat上的应用程序也运行良好。 但是,当使用JTA事务(和Spring的JtaTransactionManager )在Glassfish上运行时,在调用sessionFactory.getCurrentSession()时,我们得到了“未找到当前线程的会话” 。
因此,似乎我错过了与JTA配置有关的内容。 像通常使用Spring-Hibernate集成一样,让Spring驱动事务。 您指定一个事务管理器,Spring确保所有资源都已在事务管理器中注册,并最终调用提交或回滚。 Spring将与Hibernate集成,因此可确保在事务提交之前刷新会话。
使用hibernate 3和hibernate 3 Spring集成时,会话绑定到本地线程。 此技术使您可以使用sessionFactory.getCurrentSession()获取活动事务中任何位置的打开会话。 本地HibernateTransactionManager和JtaTransactionManager都是这种情况。 但是,从hibernate 4集成开始,hibernate会话将绑定到当前正在运行的JTA事务。
从用户的角度来看,什么都没有改变,因为sessionFactory.getCurrentSession()仍会完成其工作。 但是,当运行JTA时,这意味着Hibernate必须能够查找事务管理器以能够向当前正在运行的事务中注册会话。 如果您来自带有Spring的Hibernate 3,这是新的,实际上,您不必在Hibernate SessionFactory (或LocalSessionFactoryBean )配置中配置任何与事务有关的内容。 事实证明,通过Hibernate 4 Spring集成,事务管理器查找配置实际上是由hibernate完成的,而不是由Spring的LocalSessionFactoryBean完成的 。 解决方案非常简单。 将其添加到Hibernate( LocalSessionFactoryBean )配置中解决了我们的问题:
<prop key="hibernate.transaction.jta.platform">
org.hibernate.service.jta.platform.internal.SunOneJtaPlatform
</prop>
然后,应将“ SunOneJtaPlatform”替换为反映您的容器的子类。
有关可用的子类,请参阅API文档 。 此类实际上是在告诉Hibernate如何为您的环境查找事务管理器。 如果您不进行配置,则Hibernate不会将会话绑定到任何东西,因此不会引发异常。 还有一个属性:
hibernate.current_session_context_class
它应该指向org.springframework.orm.hibernate4.SpringSessionContext ,但这是由LocalSessionFactoryBean自动完成的,因此无需在配置中指定它。
解决了我的“当前会话未找到会话”问题后,还有另一个问题。 成功提交事务后,将看不到对事务内部的数据库所做的更改。 经过一番研究,我发现没有人在调用session.flush() 。 在进行hibernate 3集成时,注册了SpringSessionSynchronization ,它将在事务提交之前(在beforeCommmit方法中)调用session.flush( )。
在hibernate 4集成中,注册了SpringFlushSynchronization , 顾名思义 ,它还将执行刷新。 但是,这仅在TransactionSynchronization的实际“刷新”方法中实现,并且永远不会调用此方法。
我在Spring Bugtracker上对此提出了一个问题 ,其中包括两个示例应用程序,它们清楚地说明了该问题。 第一个使用的是Hibernate 3,另一个使用的是完全相同的应用程序,但是这次使用的是hibernte4。第二个将显示实际上没有任何信息持久化到数据库中(两个应用程序都在最新的Glassfish 3.1.2下进行了测试)。似乎正在创建一个环绕@Transactional注释的冲洗Aspect。 使用order属性,可以命令在刷新Aspect之前应用事务注释。 这样,您的Aspect仍在事务内部运行,并且能够刷新会话。 它可以通过注入SessionFactory (一种方式或另一种方式)然后调用sessionFactory.getCurrentSession()。flush()来以正常方式获取会话。
<tx:annotation-driven order="1"><bean id="flushinAspect" clas="..."><property name="order" value="2">
</property></bean>
</tx:annotation-driven>
或者,如果使用注释配置:
@EnableTransactionManagement(order=1)
更新:
关于此问题有一些反馈。 事实证明,这似乎不是Spring Hibernate集成中的错误,而是缺少的Hibernate配置元素。 显然,“ hibernate.transaction.factory_class”需要设置为JTA,默认值为JDBC,它依赖于Hibernate Transaction API进行显式事务管理。 通过将此设置为JTA,休眠将注册必要的同步,该同步将执行刷新。 见春
https://jira.springsource.org/browse/SPR-9404
更新2:
事实证明,在按照上一个问题建议的配置进行纠正后,仍然存在问题。 我将不重复任何事情,您可以在我在此处提交的第二个错误条目中找到详细信息: https : //jira.springsource.org/browse/SPR-9480它基本上可以归结为在JTA场景中在配置了JtaTransactionFactory的情况下,hibernate不会检测到它在事务中,因此将不执行中间刷新。 配置了JtaTransactionFactory之后,您应该通过Hibernate API而不是外部(在我们的例子中为Spring)机制来控制事务。 副作用之一是在某些情况下您可能正在读取陈旧的数据。
例:
//[START TX1]
Query query = session.createQuery('from Person p where p.firstName = :firstName and p.lastName = :lastName');
Person johnDoe = (Person)query.setString('firstName','john').setString('lastName','doe').uniqueResult();
johnDoe.setFirstName('Jim');
Person jimDoe = (Person)query.setString('firstName','jim').setString('lastName','doe').uniqueResult();
//[END TX1]
发生的情况是,在第5行执行第二个查询时,hibernate应该检测到它应该刷新对第4行的附加实体所做的先前更新(将名称从“ john”更新为“ jim”)。 但是,由于休眠不知道它在一个活动事务中运行,因此中间刷新不起作用。 在事务提交之前,它将仅刷新一次。 这会导致数据过时,因为第二个查询将找不到“ jim”,而是返回null。 解决方案(请参阅问题中Juergen Hoeller的答复)是将hibernate.transaction.factory_class配置为org.hibernate.transaction.CMTTransactionFactory 。 刚开始我有点怀疑,因为CMT使EJB容器成为现实。 但是,如果您阅读有关CMTTransaction的Java文档,则确实有道理:
/*** Implements a transaction strategy for Container Managed Transaction (CMT) scenarios. All work is done in* the context of the container managed transaction.** The term 'CMT' is potentially misleading; the pertinent point simply being that the transactions are being* managed by something other than the Hibernate transaction mechanism.** Additionally, this strategy does *not* attempt to access or use the {@link javax.transaction.UserTransaction} since* in the actual case CMT access to the {@link javax.transaction.UserTransaction} is explicitly disallowed. Instead* we use the JTA {@link javax.transaction.Transaction} object obtained from the {@link TransactionManager}
之后,一切似乎都正常。 综上所述,如果您希望休眠状态通过UserTransaction管理JTA事务,则应使用JtaTransactionFactory。 在这种情况下,您必须使用Hibernate API来控制事务。 如果还有其他人管理事务(Spring,EJB容器……),则应改用CMTTransactionFactory。 然后,通过使用javax.transaction.TransactionManager检查活动的javax.transaction.Transaction,Hibernate将恢复为注册同步。 如果弹出任何其他问题,我将相应地更新此条目。
参考:在Koen Serneels –技术博客博客上,我们的JCG合作伙伴 Koen Serneels 通过Spring集成从Hibernate 3迁移到4 。
翻译自: https://www.javacodegeeks.com/2013/03/migrating-from-hibernate-3-to-4-with-spring-integration.html