JTA EntityManager 不能使用 getTransaction()

2022-09-01 21:06:44

如何在我的非 ejb 应用程序中使用以下代码。代码有效。

@Override
public void saveItems(Collection<T> items) {
    synchronized (em) {
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            for (T item : items) {
                saveItem_((Class<T>) null, item);
            }
            tx.commit();
        } finally {
            if (tx.isActive()) {
                tx.rollback();
            }
        }
    }
}

在一个新的应用程序中,我使用的是EJB3 + JSF,并希望重用包含上述代码的库。我用于新应用程序的持久性单位如下所示:

  <persistence-unit name="myApp" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>MySQLConnection</jta-data-source>
  </persistence-unit>

我的新应用程序在遇到此行时会引发异常:

    EntityTransaction tx = em.getTransaction();

例外情况是:

A JTA EntityManager cannot use getTransaction()

这已经足够清楚了。问题是,如何转换代码,让容器管理事务。想必我的豆类方法需要适当地注释...问题是如何实现?


答案 1

EntityTransaction与资源本地类型的实体管理器一起使用。如果你想使用JTA,那么必须使用接口。UserTransaction

来自 文档 : EntityTransaction - 用于控制资源本地实体管理器上的事务的接口。EntityManager.getTransaction() 方法返回 EntityTransaction 接口。


编辑:添加了伪代码。

@Resource
private SessionContext sessionContext;

void execute(){

UserTransaction userTxn = sessionContext.getUserTransaction();

try{

 userTxn.begin();
 /**
  *  do-something
  */
 userTxn.commit();

  } catch(Throwable e){
   userTxn.rollback(); //-- Include this in try-catch 
  }
}   

答案 2

在最简单的情况下 - 它只是工作。如果您将 EntityManager 注入到 EJB 中并且不使用任何特殊注释,则该事务将在输入的第一个 EJB 方法中打开(这意味着如果 EjbA 调用 EjbB 并反过来调用 EjbC,则所有 EJB 方法中将仅使用一个事务)。如果要修改事务的控制方式,请查找@Transaction。

执行回滚的最简单方法是引发标记为@ApplicationException的异常(rollback=true)

我可能是错的,但从你的代码来看,你应该阅读EXTENDED和NORMAL EntityManager之间的区别。看起来你正在以一种非常尴尬的方式使用扩展的em(将循环移出事务将帮助你最终摆脱)。

小编辑:如果您尝试使用UserTransaction,正如另一篇文章所建议的那样,您将收到错误,因为标准EntityManager(您可能正在使用)使用所谓的CMT(容器管理事务)。不要碰它,除非你理解三个基本位置(如果你愿意,我可以详细说明,但坦率地说,你不会需要它):

  • 容器托管的实体管理器与应用程序管理的实体管理器,
  • 容器管理事务与应用程序管理事务,
  • NORMAL EntityManager 和 Extended EntityManager。

推荐