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

What is the best way to deal with exception in step 1 when step 2 is must to execute

在我们的应用程序情况下,用户已请求更新帐户的 2 个字段,例如 (A, B)。 一个帐户有多个商店,更新字段要推送到这些商店。其中一家商店被标记为默认商店。 字段 A 需要对默认商店进行一些验证(比如数量限制)。

If validation fails I am throwing exception. On success field value is added to 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);
        }
    }
}

正如我在多个论坛上看到的那样,finally 中的这种处理不是 correct/efficient。所以我正在寻找替代或更好的方法来处理这种情况。

捕获异常而不是使用finally:

boolean failed = false;
try {

} catch (YourException ex) {
    failed = true;
}

if (failed) {

}

如果您希望传播异常,您可以将其存储在一个变量中,然后重新抛出它。

由于您有 2 个操作,即使其中一个操作失败也应该执行,并且您还想将 exception/error 传播到上层,因此可以使用以下方法-

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

这两个更新应该包含在一个事务中。

@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
}

请注意 validateAndPushAvalidateAndPushB 上方的 @Transactionalpersist 方法也应该用 @Transactional 注释。

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

@Transactional 允许进行大量微调,例如对于哪些异常不应回滚事务。请参阅文档了解所有详细信息。

希望对您有所帮助!