当必须执行步骤2时,在步骤1中处理异常的最佳方法是什么?

时间:2018-09-21 06:35:37

标签: java exception-handling

在我们的应用情况下,用户已请求更新一个帐户的两个字段,例如(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);
        }
    }
}

正如我在多个论坛上阅读的那样,这种处理最终是不正确/高效的。因此,我正在寻找替代或更好的方法来处理这种情况。

3 个答案:

答案 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
}

请注意@TransactionalvalidateAndPushA上方的validateAndPushBpersist方法也应使用@Transactional进行注释。

如果以这种方式构造代码,如果发生任何验证或持久性异常,则所有数据库更改将回滚。发生这种情况是因为@Transactional具有一个名为propagationLevel的属性,如果保留默认值,它将在单个外部事务(即persist操作中执行任何内部事务)(例如validateAndPushAvalidateAndPushBvalidatepersist都将在同一事务中执行-因此,这些方法引发的任何异常都将导致整个事务被回滚。 >

@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的答案中的代码。