我们面临的情况类似于以下情况:
@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
分为两个同级UniqueConstraintViolatedThatNeedsRollback
和UniqueConstraintViolatedThatNeedsNoRollback
。此外,用两个变体GenericStorageService
和insert
替换insertWithRollbackingUniqueConstraint
的insertWithNonRollbackingUniqueConstraint
方法。只是吸它。
选项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
}
}
}