Hibernate - 使用程序化事务习惯用法的CMT EJB

时间:2014-01-15 01:35:54

标签: hibernate java-ee jpa transactions ejb

当在CMT(EJB3)中使用以下编程事务和会话习惯用法并且Hibernate Core设置为使用CMT时会发生什么? 假设需要当前的CMT交易并使用默认@TransactionAttribute(REQUIRED)

开始
  1. hibernate事务是否会加入beginTransaction()上的当前CMT?
  2. commit()尝试立即提交hibernate事务还是等到当前CMT提交?
  3. 在CMT中关闭会话会发生什么?
  4. B中。行为是否取决于当前会话是否使用getCurrentSession()绑定到CMT?

    // A: openSession()
    // B: getCurrentSession();
    Session session = sessionFactory.openSession(); 
    Transaction tx = null;
    try
    {
        tx = session.beginTransaction();
    
        // do some work
    
        tx.commit();
    }
    catch (final RuntimeException e)
    {
        try 
        {
            tx.rollback();
        }
        catch (final RuntimeException e)
        {
            // log error
        }
        throw e;
    }
    finally
    {
        session.close();
    }
    

    在我的应用程序中,我目前正在使用单个数据库,并且使用Hibernate的编程JDBC事务可以正常工作。现在,该应用程序还使用JMS-Queue for Mail消息传递,并希望将其合并到全局CMT事务中。

    修改

    目前我根本不在应用程序中使用EntityManager,并且希望将代码可移植到非托管环境中。

    启用CMT的Hibernate配置hibernate.cfg.xml

    Hibernate 4.2.6和Glassfish 3.1.2

    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.autocommit">false</property>
    <property name="hibernate.connection.datasource">jdbc/datasource</property>
    <property name="hibernate.current_session_context_class">jta</property>
    <property name="hibernate.transaction.factory_class">org.hibernate.transaction.CMTTransactionFactory</property>
    <property name="hibernate.transaction.jta.platform">org.hibernate.service.jta.platform.internal.SunOneJtaPlatform</property>
    

    SessionFactory检索

    SessionFactory是在单例EJB中构建的。 删除了不必要的东西。

    @Startup
    @Singleton
    public class SessionManager
    {
        private SessionFactory sessionFactory;
    
        public SessionManager()
        {
            final Configuration configuration = new Configuration().configure();
            this.sessionFactory = configuration.buildSessionFactory();
        }
    }
    

4 个答案:

答案 0 :(得分:1)

使用CMT(容器管理事务),您不会声明类似tx = session.beginTransaction();你让容器为你工作。您只需指定容器何时以及是否支持事务。查看oracle doc Java EE 5 Tutorial

假设您有一个EJB,其默认事务范围是必需的。因此,hibernate实际上将绑定到该事务范围。

以下示例第一个ejb,没有任何事务调用另一个带cmt:

的事务
@TransactionAttribute(NOT_SUPPORTED)
@Stateful
public class TransactionBean implements TransactionInterface{

   @EJB BusinessBean businessBean;

   public method1(){
       businessBean.doSomething();
   }
}

@TransactionAttribute(REQUIRED)
@Stateful
public class BusinessBean implements BusinessInterface{

    @PersistenceContext(unitName = "some-persistence-unit")
    private EntityManager entityManager;

    public void doSomething(){
        Someclass entity = entityManager.finde(Someclass.class, 1) // Find entity with id 1
        entity.setData("somedata");
    }
}

当完成方法doSomething()时,容器将刷新并将更新提交给数据库,因为外部ejb没有正在运行的事务。这仅适用于容器

也提供数据源的情况

答案 1 :(得分:1)

正如Luk指出的那样,这不是在CMT环境中编码的方法。无论如何,根据session.beginTransaction()部分是安全的 http://docs.jboss.org/hibernate/annotations/3.5/api/org/hibernate/Session.html#beginTransaction%28%29

  

如果需要新的基础交易,请开始交易。否则,在现有基础事务的上下文中继续新工作

tx.rollback()也很安全。它没有在文档中说明,但CMTTransaction实际上执行getTransaction().setRollbackOnly(),即它只是标记TX以进行回滚。提交实际上不提交TX,但可以刷新会话。如果涉及多个资源,真正的提交将违反事务语义。

答案 2 :(得分:0)

会话(在JPA persistence context中,与EntityManager实例绑定)是数据库模式子集状态的“内存中”快照。 根据您的配置,会话的范围会有所不同。在标准Web应用程序中,每个请求将有一个会话。

您可以同时拥有多个具有不同状态的会话实例,会话彼此隔离(会话上执行的操作在另一个会话中不可见)

交易是一个工作单元(理论上也是一个会话)。它绑定到底层的RDBMS事务系统,并与它打开的会话绑定。

在“容器管理实体管理器”上下文(您称之为CMT)中,容器将负责将会话绑定到定义的范围,并根据遇到的@Transactional注释沿方法传播事务调用

实际上发生了什么: 您的容器在某处维护会话实例,并且能够使用@PersistenceContext注释将其提供给您的ejb实例。 您正在使用sessionFactory.openSession()手动构建新的会话实例,在其上打开一个事务并执行操作。在您提交事务,手动刷新或关闭自定义会话并手动触发容器刷新之前,托管会话实例无法看到任何修改。

getCurrentSession()方法是一种特定于hibernate的机制,它充当Java SE上下文(无容器)中的容器会话范围管理机制。我想(但我不知道hibernate JPA实现)它不会返回容器管理会话,但我可能在这一点上错了。 (编辑我是)

这里一个合适的解决方案是使用@PersistenceContext检索当前容器管理的会话实例,并使用@Transactional注释管理事务传播。

请参阅https://community.jboss.org/wiki/SessionsAndTransactions

见下面的Luk anwser

仅供参考Container-Managed Transactions

编辑(根据问题版本)

请参阅Difference between a "jta-datasource" and a " resource-local " datasource?

事实上,我似乎确实错了,并且您不需要从容器中使用持久性上下文注入,但您必须使用JTA事务。

从EJB 3.0规范,第13.3.4节“企业Bean使用容器管理的事务划分”:

The enterprise bean’s business methods [...] must not attempt to obtain or use the javax.transaction.UserTransaction interface.

这意味着您可以使用

sessionFactory.getCurrentSession() 

但你不能使用tx = session.beginTransaction()而是

@TransactionAttribute(TransactionAttributeType.REQUIRED) 

请参阅上面的jboss doc中的EJB / CMT部分的事务划分

答案 3 :(得分:0)

我从你的问题中学到了一些新东西,因为我不知道Hibernate可以用这种方式配置(尽管很明显它支持JTA)。无论如何根据文档似乎你没有被迫配置它以便使用JTA,因为它被描述为here

  

如果您的持久层在应用程序服务器中运行(例如,   在EJB会话bean之后),获得的每个数据源连接   Hibernate将自动成为全局JTA事务的一部分。   您还可以安装独立的JTA实现并使用它   没有EJB。 Hibernate为JTA集成提供了两种策略。

另请参阅文档中的示例,因为您不需要在CMT上下文中打开任何事务。但是,如果要控制事务划分,请检查那些BMT示例。