我们正面临类似以下情况:
@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?
答案 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
}
}
}