在我们的应用情况下,用户已请求更新一个帐户的两个字段,例如(A,B)。 一个帐户有多个商店,已更新的字段将被推送到这些商店。存储之一被标记为默认存储。 字段A对于默认存储(例如数量限制)需要进行一些验证。
如果验证失败,我将抛出异常。 成功时,将字段值添加到store_space_table
字段B必须被推送到所有存储。当存储关闭或不可达时,推送到存储可能会引发异常。 目前,我已经在finally块中编写了此代码。
我不想在第二步中回退关于异常的第一个操作。相反,我想合并步骤1和步骤2的异常,并将其传播。
void validateFieldAndPushToStore(List<Field> inputFieldList, Account account) throws ServiceException {
List<Store> allStoresOfAccount = getAllStoresOfAccount(account);
Set<Store> storeListToPushData = new HashSet<>();
try{
if(ifFieldAUpdated(inputFieldList)) {
// get default store from list of stores of an account,
Store defaultStore = getDefaultStore(allStoresOfAccount)
// Validate space availability of A on default store, if validation is successful, then update data in store_space_table
validateSpaceOnDefaultStoreForFieldA(defaultStore);
storeListToPushData.add(defaultStore);
}
} finally {
if( ifFieldBUpdated(inputFieldList) ) {
storeListToPushData.addAll(allStoresOfAccount);
}
if( ! storeListToPushData.isEmpty()) {
// This operation reads fields A from DB (store_space_table), reads field B from field_tbl and push to stores.
pushUpdatesToStores(account, storeListToPushData);
}
}
}
正如我在多个论坛上阅读的那样,这种处理最终是不正确/高效的。因此,我正在寻找替代或更好的方法来处理这种情况。
答案 0 :(得分:1)
这两个更新应该包装在一个事务中。
@Transaction
简而言之。
您的服务的结构应如下所示。
@Transactional
public void validateFieldAndPushToStore(A a, B b) {
serviceA.validateAndPushA(a);
serviceB.validateAndPushB(b);
}
serviceA和serviceB的实现将在哪里。
@Transactional
public void validateAndPushA(A a){
validate(a); // can throw validation exception from here
persist(a); // can throw persistence exception from here
}
@Transactional
public void validateAndPushB(B b){
validate(b); // can throw validation exception from here
persist(b); // can throw persistence exception from here
}
请注意@Transactional
和validateAndPushA
上方的validateAndPushB
。
persist
方法也应使用@Transactional
进行注释。
如果以这种方式构造代码,如果发生任何验证或持久性异常,则所有数据库更改将回滚。发生这种情况是因为@Transactional
具有一个名为propagationLevel
的属性,如果保留默认值,它将在单个外部事务(即persist
操作中执行任何内部事务)(例如validateAndPushA
,validateAndPushB
,validate
和persist
都将在同一事务中执行-因此,这些方法引发的任何异常都将导致整个事务被回滚。 >
@Transactional
允许进行许多微调,例如,不应该回退异常的事务。请查阅文档以获取所有详细信息。
希望这会有所帮助!
答案 1 :(得分:0)
捕获异常,而不是使用finally:
boolean failed = false;
try {
} catch (YourException ex) {
failed = true;
}
if (failed) {
}
如果要传播异常,可以将其存储为变量,然后将其重新引发。
答案 2 :(得分:0)
由于即使有一个失败,您也应该执行两个操作,并且您还希望将异常/错误传播到上一层,所以可以使用以下方法-
void methodPerformingTwoOperations() throws Exception {
Exception firstOperationErrror = null;
Exception secondOperationError = null;
try {
performFirstOperation();
} catch(Exception e) {
firstOperationError = e;
}
try {
performSecondOperation();
} catch(Exception e) {
secondOperationError = e;
}
throwExceptionAsPerErrors(firstOperationError, secondOperationError);
}
void throwExceptionAsPerErrors(Exception firstOperationError, Exception secondOperationError) {
// depending upon exceptions compose and throw new exception from here
}
更新
请注意,在使用@Transactional
时,请始终验证要传递给此注释的参数。默认情况下,传播属性设置为REQUIRED
,这意味着所有事务将在同一个事务中运行,并且如果指定了例外,则将全部还原。
如果您要保留一项操作的数据,即使另一项操作失败,则可以将Transactional
应用于内部方法( 但不能应用于主要方法 )。请参阅@alexrolea的答案中的代码。