java - Hibernate + EJB 中的容器管理事务 (CMT)

标签 java hibernate jta

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 部署描述符 中指定所需的声明,但使用注释来实现会很棒。

最佳答案

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

但是,也存在问题:

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

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

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

此方法为 Hibernate SessionFactory 创建一个包装器并提供最常用的API。使用 CDI,@RequestScoped session 自动打开和关闭。事务使用拦截器进行管理。 @RequestScoped 确保每个请求有一个Session,因此 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();
        }
    }
}

然后将该实现注入(inject)到数据库层并用于获取 session 。

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 {



}

关于java - Hibernate + EJB 中的容器管理事务 (CMT),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32329611/

相关文章:

java - sql如何更快的获取数据?先取计数再取数据或者直接取数据

java - Apache 服务器上的 TLS

hibernate - 传递给持久化 Kotlin 的分离实体

java - 使用 Hibernate 注释和 Spring 进行 ORM

rest - 在两阶段提交协议(protocol)中回滚失败时会发生什么

java - 在WebLogic中获取JTA事务超时值

java - 如何打印 JDBC 的连接 url?

java - 如何进行更快的广度优先搜索?

java - 在这种情况下如何使用 DISTINCT 创建 hibernate 条件

java - 一首接一首播放歌曲导致 MP3Extractor 错误