UniqueConstraintViolated

UniqueConstraintViolated

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

@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为我们提供了以下选择:


UniqueConstraintViolated装饰rollback = false,手动将其捕获在ServiceA中,并通过程序化事务控制回滚事务。
UniqueConstraintViolated分为两个同级UniqueConstraintViolatedThatNeedsRollbackUniqueConstraintViolatedThatNeedsNoRollback。此外,用两个变体GenericStorageServiceinsert替换insertWithRollbackingUniqueConstraintinsertWithNonRollbackingUniqueConstraint方法。
只是吸它。


选项1是不可取的,因为大多数服务与ServiceA是同一类型,因此rollback = true是更准确的选择。而且,它使声明式事务控制的优雅性失效。

选项2是不可取的,因为对于GenericStorageService,这两种情况实际上是相同的。在此级别上进行区分没有任何意义。此外,UniqueConstraintViolated并不是唯一需要区别的例外...我们将遭受组合爆炸的困扰。

选项3无需进一步解释。

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

什么是选项4?

最佳答案

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

10-05 21:13