【问题标题】:What's a clean, standard way to get multiple transactions in an EJB?在 EJB 中获取多个事务的干净、标准的方法是什么?
【发布时间】:2017-10-06 21:23:20
【问题描述】:

我有一个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 stackoverflow discussion@TransactionAttribute 在我的案例 1 中被忽略,因为它不跨越任何 EJB 边界,所以 batchEdit() 调用 edit() 就好像它没有被注释一样。在案例 2 中使用 context.getBusinessObject() 函数来获取 bean 的引用会导致 TransactionManagement 注释起作用,但通过所有这些似乎真的很奇怪。

选项 2

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

@TransactionManagement( value = TransactionManagementType.BEAN )

但是那样我就会失去“JPA 魔法”,不得不到处管理交易。我认为我的团队中的其他人不会想要这样做,所以如果有更好或标准的方法来做到这一点,任何见解都会受到赞赏。

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

【问题讨论】:

    标签: jakarta-ee ejb bulk transactional


    【解决方案1】:

    你可以只在 self 中注入 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,每次调用都需要新事务更有意义。

    【讨论】:

    • 当我在短期内调用异步方法两次(甚至更频繁)时会发生什么。调用是否堆叠直到发生溢出?
    • 最后一个问题。您使用 REQUIRED_NEW。由于调用函数具有 NOT_SUPPORTED,REQUIRED 属性不会相同吗?
    • @kaiser 不。它不是在同一个实例上调用的。我想您需要退后一步,了解 EJB 的实际工作原理:stackoverflow.com/q/25514361
    【解决方案2】:

    我猜“hacky”在旁观者的眼中。 context.getBusinessObject 的存在正是为了让你可以做这种事情。

    替代方法是使用第二类:

    @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 支持。

    【讨论】:

    • 有趣。我们最终选择了 getBusinessObject 解决方案;唯一烦人的部分是它需要 bean 的接口,而不是 bean 的类本身。传递 bean 的类本身会输出“组件没有这样的接口:”,而传入它实现的接口是有效的。
    • 如果您完全消除接口,那么您可以使用类名。如果您正在计划一堆不同的实现,请仅在 EJB 上使用接口,这并不常见。
    • 我将由我的团队负责。我们有一些尚未完成的潜在测试接口,但传入接口将是一个很大的重构。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-03
    相关资源
    最近更新 更多