【问题标题】:How to avoid Rollbacking after Catching EJB Rollbacked ApplicationException?捕获 EJB Rollbacked ApplicationException 后如何避免回滚?
【发布时间】:2015-02-26 18:47:31
【问题描述】:

我们面临类似于以下的情况:

@ApplicationException(rollback = true)
class UniqueConstraintViolated extends RuntimeException { ... }

interface GenericStorageService {
  void insert(Object ety); // throws UniqueConstraintViolation
}

class ServiceA {
  @Inject GenericStorageService store;

  void insert(A ety) {
    someSideEffect();
    store.insert(ety);
    someOtherSideEffect();
  }
}

class ServiceB {
  @Inject GenericStorageService store;

  void insertIfNotYetPresent(B ety) {
    try {        
      someSideEffect();
      store.insert(ety);
      someOtherSideEffect();
    } catch (UniqueConstraintViolation e) {
      // that's totally ok
    }
  }
}

在这种情况下,

  • 请求插入一些先前插入的A 是一个实际的用户错误。无法以有意义的方式提交事务。
  • 请求插入一些以前存在的B 是不是错误。只需确认上述B 的存在,就可以安全地提交事务。特别是,无论给定的 B 之前是否插入,都需要提交副作用。

根据(我对 EJB 规范的理解),上述代码将触发在任何一种情况下的回滚,不会导致所需的语义。

据我了解,EJB 为我们提供了以下选择:

  1. 用rollback = false 装饰UniqueConstraintViolated,在ServiceA 中手动捕获它并通过程序化事务控制回滚事务。
  2. 将UniqueConstraintViolated 拆分为两个兄弟UniqueConstraintViolatedThatNeedsRollback 和UniqueConstraintViolatedThatNeedsNoRollback。此外,将GenericStorageService 的insert 方法替换为insertWithRollbackingUniqueConstraint 和insertWithNonRollbackingUniqueConstraint 两个变体。
  3. 吸吧。

选项 1 是不可取的,因为大多数服务与ServiceA 的类型相同,所以rollback = true 是更准确的选择。此外,它还破坏了声明式事务控制的优雅。

选项 2 是不可取的,因为对于GenericStorageService,这两种情况实际上是相同的。在这个层面上的区别是没有意义的。此外,UniqueConstraintViolated 并不是唯一需要区分的例外......我们会遭受组合爆炸的影响。

选项 3 无需进一步解释。

这给我留下了最后一个问题:

什么是选项 4?

【问题讨论】:

    标签: java jakarta-ee transactions ejb


    【解决方案1】:

    对于选项 2,这通常是我的工作。

    //So generic transaction service, that commits every transaction in a different transaction context.
    @Stateless
    @TransactionAttribute(REQUIRES_NEW)
    public class TransactionalService {
    
       public void executeTransactional(final Runnable task) {
         task.run();
       }
    }
    
    @Statless
    public class ServiceB {
    
      @Inject GenericStorageService store;
      @Inject TransactionalService transactionalService;
    
      public void insertIfNotYetPresent(B ety) {
        try {       
          transactionalService.executeTransactional(new Runnable() {
             public void run() {
                store.insert(ety);
             }
          };
    
          transactionalService.executeTransactional(new Runnable() {
             public void run() {
                someSideEffect();
             }
          };
    
        } catch (UniqueConstraintViolation e) {
          // that's totally ok
        }
      }
    }
    

    //如果你在java 8,非常简单,所有的冗长都没有了

    @Statless
    public class ServiceB {
    
      @Inject GenericStorageService store;
      @Inject TransactionalService transactionalService;
    
      public void insertIfNotYetPresent(B ety) {
        try {   
          transactionalService.executeTransactional(() -> store.insert(ety) ); 
          transactionalService.executeTransactional(() -> someSideEffect() );
    
        } catch (UniqueConstraintViolation e) {
          // that's totally ok
        }
      }
    }
    

    【讨论】:

    • 我通过添加对someOtherSideEffect() 的调用稍微改变了我的问题。我们如何将您的解决方法应用于此更改的案例?我们需要确保someSideEffect() 和someOtherSideEffect() 在同一个事务中运行。
    • 在同一笔交易中,忘记它。除非您实现对异常的手动处理。 EJB 规范明确指出,如果抛出异常,事务将中止
    • 我担心你会这么说。感谢您确认我的理解!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 2016-10-17
    相关资源
    最近更新 更多