在 EJB 中获取多个事务的干净、标准的方法是什么?

What's a clean, standard way to get multiple transactions in an EJB?

我有一个 batchEdit(List<E> entity) 循环调用 edit(E entity) 函数,而每个 edit() 都有自己的事务,因此失败的编辑不会回滚好的编辑。我目前是这样实现的:

选项 1

@Stateless
@TransactionManagement( value = TransactionManagementType.CONTAINER )
public class Service<E> {

    @Resource
    private SessionContext context;

    @Override
    @TransactionAttribute( value = TransactionAttributeType.REQUIRES_NEW )
    public E edit( E entity ) {
       //edit code
    }

    @Override
    public List<E> bulkEdit( List<E> entities ) {
       for(E entity : entities){
          //case 1: Regular edit, Does not create a new transaction!
          //edit(entity);

          //case 2: Hacky edit, creates a new transaction
          context.getBusinessObject( Service.class ).editPersistNulls( entity );
       }
    }
}

根据 this Whosebug discussion@TransactionAttribute 在我的案例 1 中被忽略,因为它不跨越任何 EJB 边界,所以 batchEdit() 调用 edit() 就好像它没有注释。在案例 2 中使用 context.getBusinessObject() 函数来获取 bean 的引用会导致 TransactionManagement 注释起作用,但完成所有这些操作似乎真的很奇怪。

选项 2

我的另一个选择是更改为 bean 管理的事务:

@TransactionManagement( value = TransactionManagementType.BEAN )

但是那样的话我会丢失 "JPA Magic" 并且不得不管理所有地方的交易。我认为我团队中的其他人不会愿意经历这些,所以如果有更好或标准的方法来做到这一点,我们将不胜感激。

我们正在使用 OpenJPA 和 EJB,但我们正在努力接近 JPA 标准。

估计"hacky"是仁者见仁智者见智吧。 context.getBusinessObject正是为了你能做这种事而存在的

替代方法是使用第二个 class:

@Stateless
public class BulkService<E> {

    @EJB
    private Service<E> service;

    public List<E> bulkEdit( List<E> entities ) {
       for(E entity : entities) {
           service.editPersistNulls( entity );
       }
    }

}

但要注意大型实体列表,因为包含事务可能会超时。

如果您使用的是 Java EE 7 兼容服务器实现,请考虑使用 JSR-352 Batch Applications 支持。

你可以自己注入EJB。

@Stateless
public class Service<E> {

    @EJB
    private Service<E> self;

    @TransactionAttribute(REQUIRES_NEW)
    public void edit(E entity) {
        // ...
    }

    @TransactionAttribute(NOT_SUPPORTED)
    public void bulkEdit(List<E> entities) {
        for (E entity : entities) {
           self.edit(entity);
        }
    }
}

更好的做法是@Asynchronous。速度更快。

@Stateless
public class Service<E> {

    @EJB
    private Service<E> self;

    public void edit(E entity) {
        // ...
    }

    @Asynchronous
    @TransactionAttribute(REQUIRES_NEW)
    public void asyncEdit(E entity) {
        // ...
    }

    @TransactionAttribute(NOT_SUPPORTED)
    public void bulkEdit(List<E> entities) {
        for (E entity : entities) {
           self.asyncEdit(entity);
        }
    }
}

它还使原始 edit() 方法免受潜在不需要的 REQUIRES_NEW 事务属性的影响,因为它可能会被其他服务调用,当然应该保留在同一事务中。对于 @Asynchronous,每次调用都需要一个新事务更有意义。