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

Container managed transcation (CMT) in hibernate + EJB

hibernate documentation 说:

With CMT, transaction demarcation is declared in session bean deployment descriptors, rather than performed in a programmatic manner.

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

这就是我的想法,我的代码应该如下所示:

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

默认情况下,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>

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

但是,有问题:

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

  2. 这限制为一会话一交易成语。这样在调用一个EJB业务方法时,只能有一个JTA或Hibernate事务。

幸运的是,使用 CDI, and some custom interceptors this can be solved and a lot of Hibernate boilerplate can be removed. I wrote a sample on github

此方法为 Hibernate SessionFactory and provides most commonly used APIs. Using 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 {



}