捕获EJB Rollbacked ApplicationException后如何避免回滚?

时间:2015-02-26 18:47:31

标签: java java-ee transactions ejb

我们正面临类似以下情况:

@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. UniqueConstraintViolated分成两个兄弟UniqueConstraintViolatedThatNeedsRollbackUniqueConstraintViolatedThatNeedsNoRollback。此外,将GenericStorageService insert方法替换为两个变体insertWithRollbackingUniqueConstraintinsertWithNonRollbackingUniqueConstraint
  3. 只是吮吸它。
  4. 选项1是不受欢迎的,因为大多数服务与ServiceA属于同一类型,因此rollback = true是更准确的选择。此外,它还使声明式事务控制的优雅无效。

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

    选项3无需进一步解释。

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

    什么是选项4?

1 个答案:

答案 0 :(得分:1)

这通常是我的工作,对于option2。

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