今天下午开始尝试将项目的transaction交给Glassfish的JTA管理,因为之后会使用到JMS,需要与JDBC组成跨data source的事务。但是不知道是没人这么干过还是大家不屑于将完整的配置过程就下来,JBoss的官方文档、Spring的官方文档、SOF都没有可用的配置建议。经过差不多半天时间的Google和尝试,终于配置成功,在此分享

环境:

  • Spring 3.1.1
  • Hibernate 4.1.1
  • Glassfish 3.1.2

应该都是最新的版本,Spring配置文件如下:

...
<!-- 打开Spring的@Transaction声明式事务支持 -->
<!-- 配置Spring使用JTA事务 -->

...

    hibernate.current_session_context_class = jta<!-- 1 -->
    hibernate.transaction.manager_lookup_class = org.hibernate.transaction.SunONETransactionManagerLookup<!-- 2 -->
	
...

关键配置在1和2处:

根据Hibernate官方文档此处此处的描述:

When configuring Hibernate's transaction factory, choose

org.hibernate.transaction.JTATransactionFactory

if you use JTA directly (BMT), and 

org.hibernate.transaction.CMTTransactionFactory

in a CMT session bean. Remember to also set 

hibernate.transaction.manager_lookup_class

Ensure that your 

hibernate.current_session_context_class

is either unset (backwards compatibility), or is set to 

"jta"

See the Javadocs for the 

org.hibernate.context.spi.CurrentSessionContext

interface for a detailed discussion of its contract. It defines a single method, 

currentSession()

by which the implementation is responsible for tracking the current contextual session. Out-of-the-box, Hibernate comes with three implementations of this interface:

  • org.hibernate.context.internal.JTASessionContext: current sessions are tracked and scoped by a JTAtransaction. The processing here is exactly the same as in the older JTA-only approach. See the Javadocs for details.
  • org.hibernate.context.internal.ThreadLocalSessionContext:current sessions are tracked by thread of execution. See the Javadocs for details.
  • org.hibernate.context.internal.ManagedSessionContext: current sessions are tracked by thread of execution. However, you are responsible to bind and unbind a Session instance with static methods on this class: it does not open, flush, or close a Session

即需要设置

hibernate.current_session_context_class

为jta,同时设置

hibernate.transaction.manager_lookup_class

但是这里耽误了我半天的就是

hibernate.transaction.manager_lookup_class

的设置,这里只说“需要设置”,但是没有给出具体的值,虽然文档中给出了一个

TransactionManagerLookup

接口的实现类列表,但是其中没有Glassfish⋯⋯

又经过了N久的Google,在这里这里找到了一个神奇的实现类:

org.hibernate.transaction.SunONETransactionManagerLookup

根据Javadoc,此类实现了“for Sun ONE Application Server 7 and above”的查找策略,想想GF的前身即是Sun AS,应该可以使用。试了一下,终于work~

这里遗留的一个问题是,我并没有在Hibernate 4.1.1的源代码里找到org.hibernate.transaction.SunONETransactionManagerLookup(上面的Javadoc也是3.6的Javadoc),甚至整个org.hibernate.transaction包下面只有TransactionManagerLookup接口的定义。因此究竟是怎么work的我没有找到最终答案,如果有人知道,希望可以在这里留下答案

2012-4-16补:今天启动服务器时偶然间发现抛出个warning,

hibernate.transaction.manager_lookup_class

在Hibernate 4中已被deprecated掉,需要使用

hibernate.transaction.jta.platform=org.hibernate.service.jta.platform.internal.SunOneJtaPlatform

替换

2012-4-17补:今天发现Hibernate的自动flush失效,查看GF启动日志后发现一个新的warning:

JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()

查文档后发现上面的1、2处需要指定TransactionFactory,否则Hibernate将默认使用JDBCTransactionFactory:

hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory

问题解决。这次的经验同样是,虽然app server启动时一般会输出大量log,但是要让自己养成从中发现warning级别及以上信息的洞察力,这样解决问题将事半功倍

2012-4-22补:今天又遇到了问题,在使用Query.iterate()方法时,Hibernate抛出异常:

org.hibernate.HibernateException: proxy handle is no longer valid

根据这里的讨论,把

JTATransactionFactory

换成了

CMTTransactionFactory

问题解决:

hibernate.transaction.factory_class=org.hibernate.transaction.CMTTransactionFactory