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



hibernate文档说:

使用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部署描述符中指定,但是用注释来做会很好。

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

然而,也存在一些问题:

  1. 这并不适用于所有Java EE容器。不支持更新版本的Websphere,并且引用hibernate的源代码- Websphere,然而,不是一个相同的JEE/JTA容器…

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

幸运的是,使用CDI和一些自定义拦截器可以解决这个问题,并且可以删除许多Hibernate样板文件。我在github上写了一个示例。

这种方法为Hibernate SessionFactory创建了一个包装器,并提供了最常用的api。通过CDI,可以自动打开和关闭@RequestScoped会话。事务使用拦截器进行管理。@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 {

}

EJB方法默认是事务性的。

您可以使用TransactionAttribute注释调整它们的行为。

你可以在这里阅读更多关于CMT的信息:

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

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

典型的例子是——

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>

最新更新