11.2.2. 使用JTA

如果你的持久层运行在一个应用服务器中(例如,在EJB session beans的后面),Hibernate获取 的每个数据源连接将自动成为全局JTA事务的一部分。 你可以安装一个独立的JTA实现,使用它而不使用EJB。Hibernate提供了两种策略进行JTA集成。

如果你使用bean管理事务(BMT),可以通过使用Hibernate的 Transaction API来告诉 应用服务器启动和结束BMT事务。因此,事务管理代码和在非托管环境下是一样的。

// BMT idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();

    // do some work
    ...

    tx.commit();
}
catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e; // or display error message
}
finally {
    sess.close();
}

如果你希望使用与事务绑定的Session,也就是使用getCurrentSession()来简化上下文管理,你将不得不直接使用JTA UserTransactionAPI。

// BMT idiom with getCurrentSession()
try {
    UserTransaction tx = (UserTransaction)new InitialContext()
                            .lookup("java:comp/UserTransaction");

    tx.begin();

    // Do some work on Session bound to transaction
    factory.getCurrentSession().load(...);
    factory.getCurrentSession().persist(...);

    tx.commit();
}
catch (RuntimeException e) {
    tx.rollback();
    throw e; // or display error message
}

在CMT方式下,事务声明是在session bean的部署描述符中,而不需要编程。 因此,代码被简化为:

// CMT idiom
Session sess = factory.getCurrentSession();

// do some work
...

在CMT/EJB中甚至会自动rollback,因为假若有未捕获的RuntimeException从session bean方法中抛出,这就会通知容器把全局事务回滚。这就意味着,在BMT或者CMT中,你根本就不需要使用Hibernate Transaction API ,你自动得到了绑定到事务的“当前”Session。

注意,当你配置Hibernate的transaction factory的时候,在直接使用JTA的时候(BMT),你应该选择org.hibernate.transaction.JTATransactionFactory,在CMT session bean中选择org.hibernate.transaction.CMTTransactionFactory。记得也要设置hibernate.transaction.manager_lookup_class。还有,确认你的hibernate.current_session_context_class未设置(为了向下兼容),或者设置为"jta"

getCurrentSession()在JTA环境中有一个弊端。对after_statement连接释放方式有一个警告,这是被默认使用的。因为JTA规范的一个很愚蠢的限制,Hibernate不可能自动清理任何未关闭的ScrollableResults 或者Iterator,它们是由scroll()iterate()产生的。你must通过在finally块中,显式调用ScrollableResults.close()或者Hibernate.close(Iterator)方法来释放底层数据库游标。(当然,大部分程序完全可以很容易的避免在JTA或CMT代码中出现scroll()iterate()。)