代码之家  ›  专栏  ›  技术社区  ›  blubb

如何在捕获EJB回滚应用程序异常后避免回滚?

  •  0
  • blubb  · 技术社区  · 10 年前

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

    @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. 装饰 UniqueConstraintViolated 具有 rollback = false ,手动将其捕获 ServiceA 并通过编程事务控制回滚事务。
    2. 分裂 UniqueConstraint冲突 分成两个兄弟姐妹 UniqueConstraintViolatedThatNeedsRollback UniqueConstraintViolatedThatNeedsNoRollback 。此外,更换 GenericStorageService insert 两种变体的方法 insertWithRollbackingUniqueConstraint insertWithNonRollbackingUniqueConstraint .
    3. 吸一吸。

    选项1是不可取的,因为大多数服务与 服务A 所以 rollback = true 是更准确的选择。此外,它还取消了声明性事务控制的优雅。

    选项2是不可取的,因为 通用存储服务 ,这两种情况完全相同。在这个层面上的区分是没有意义的。此外 UniqueConstraint冲突 并不是唯一需要区别的例外。。。我们会遭受组合爆炸。

    选项3无需进一步解释。

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

    选项4是什么?

    1 回复  |  直到 10 年前
        1
  •  1
  •   maress    10 年前

    对于选项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
        }
      }
    }
    

    //如果您使用的是java8,非常简单,那么所有冗长的内容都将消失

    @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
        }
      }
    }