hibernate + EJB中的容器管理事务(CMT)

时间:2015-09-01 10:44:09

标签: java hibernate jta

hibernate documentation说:

  

使用CMT,事务划分在会话bean部署描述符中声明,而不是以编程方式执行。

但我无法找到有关如何执行此操作的完整示例。

这就是我的想法,我的代码应该是这样的:

@Stateless
public class Dao{

  @Inject // or some other annotation
  private SessionFactory factory;

  public void doDaoStuff(){
    Object obj = factory.getCurrentSession().get(Entity.class, "Id");
    // do something with obj
    return;
  }
}

hibernate具有的所有样板都是免费的,因为事务应该由容器启动,提交和滚动。

那么,有可能这样做吗?虽然文档说明应该在 bean部署描述符中指定所需的声明,但使用注释执行此操作会很棒。

3 个答案:

答案 0 :(得分:1)

在JavaEE环境中,Hibernate可以使用CMT(容器管理事务)策略,该策略将hibernate事务与底层JTA事务绑定,从而消除了手动存在,提交和回滚事务的需要。示例here

但是,有一些问题:

  1. 这不适用于所有Java EE容器。不支持较新版本的Websphere,并引用source code of hibernate - WebSphere,但是,它不是一个理智的JEE / JTA容器......

  2. 这限制了一个会话一个事务习惯用法。因此,在调用EJB业务方法期间,只能有一个JTA或Hibernate事务。

  3. 幸运的是,使用CDI和一些自定义拦截器可以解决这个问题,并且可以删除大量的Hibernate样板。我写了一个github的样本。

    此方法为Hibernate SessionFactory创建了一个包装器,并提供了最常用的API。使用CDI,@RequestScoped会话被自动启用并关闭。使用Interceptor管理事务。 @RequestScoped每个请求确保一个Session,因此不会在多个请求之间共享会话。

    import javax.annotation.PreDestroy;
    import javax.enterprise.context.RequestScoped;
    import javax.inject.Inject;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.StatelessSession;
    
    @RequestScoped
    public class MySessionFactory implements SessionFactoryTemplate{
    
        @Inject
        private SessionFactory sessionFactory;// Inject after creating the singleton instance
    
        private Session currentSession;
    
        public Session openSession(){
            return sessionFactory.openSession();
        }
    
        public Session getCurrentSession(){
            if(currentSession == null){
                currentSession = sessionFactory.openSession();
            }
            return currentSession;
        }
    
        public StatelessSession openStatelessSession() {
            return sessionFactory.openStatelessSession();
        }
    
        @PreDestroy
        private void closeSession(){
            if(currentSession!=null && currentSession.isOpen()) {
                currentSession.close();
            }
        }
    }
    

    然后将这个实现注入数据库层并用于获取会话。

    import org.ares.cdi.hibernate.sf.MySessionFactory;
    import org.ares.cdi.hibernate.interceptors.Transactional;
    
    public class Dao {
    
      @Inject
      private MySessionFactory sf;
    
      public void get(int id){
        sf.getCurrentSession().get(clazz,id);
      }
    
      @Transactional
      public void add(Object entity){
        sf.getCurrentSesion().add(entity);
      }
    }
    

    事务由TranscationManager拦截器管理,并由@Transactional注释声明。

    import javax.inject.Inject;
    import javax.interceptor.AroundInvoke;
    import javax.interceptor.Interceptor;
    import javax.interceptor.InvocationContext;
    
    import org.ares.cdi.hibernate.sf.MySessionFactory;
    import org.hibernate.Session;
    import org.hibernate.Transaction;
    import org.hibernate.resource.transaction.spi.TransactionStatus;
    
    @Interceptor
    @Transactional
    public class TransactionManager {
    
        @Inject
        private MySessionFactory sessionFactory;
    
        @AroundInvoke
        public Object handleTransaction(InvocationContext context) throws Exception{
            Session session = sessionFactory.getCurrentSession();
            Transaction tx = null;
            try{
                tx = session.beginTransaction();
                return context.proceed();
            }
            catch(Exception e){
                tx.rollback();
                throw e;
            }
            finally{
                if(tx.getStatus().equals(TransactionStatus.ACTIVE)){
                    try{
                        tx.commit();
                    }
                    catch(Exception e){
                        tx.rollback();
                        throw e;
                    }
                }
            }
        }
    }
    
    
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import javax.interceptor.InterceptorBinding;
    
    @InterceptorBinding
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    public @interface Transactional {
    
    
    
    }
    

答案 1 :(得分:0)

默认情况下,EJB方法是事务性的。

您可以使用TransactionAttribute注释调整其行为。

您可以在此处阅读有关CMT的更多信息:

https://docs.oracle.com/javaee/7/tutorial/transactions003.htm#BNCIJ

https://docs.oracle.com/javaee/7/tutorial/transactions.htm#BNCIH

答案 2 :(得分:0)

你的典型例子就像 -

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

    @TransactionManagement(TransactionManagementType.CONTAINER) 
    @Stateless(..)
    public class YourBean{

            @TransactionAttribute(TransactionAttributeType.REQUIRED)  // if in case you wanted to use 'existing' transaction
             public void DoStuff(){
               }

    }

在您的服务器配置中,您需要<enterprise-beans>

下的标记
<transaction-type>Container</transaction-type>