当必须执行步骤 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
}
请注意 validateAndPushA
和 validateAndPushB
上方的 @Transactional
。
persist
方法也应该用 @Transactional
注释。
如果您以这种方式构建代码,如果发生任何验证或持久性异常,所有数据库更改都将回滚。发生这种情况是因为 @Transactional
有一个名为 propagationLevel
的 属性,如果保留默认值,它将在单个中执行任何内部事务(例如 persist
操作的事务)外部事务(即 validateAndPushA
、validateAndPushB
、validate
和 persist
都将在同一个事务中执行 - 因此这些方法抛出的任何异常都将导致整个事务被回滚).
@Transactional
允许进行大量微调,例如对于哪些异常不应回滚事务。请参阅文档了解所有详细信息。
希望对您有所帮助!
在我们的应用程序情况下,用户已请求更新帐户的 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
}
请注意 validateAndPushA
和 validateAndPushB
上方的 @Transactional
。
persist
方法也应该用 @Transactional
注释。
如果您以这种方式构建代码,如果发生任何验证或持久性异常,所有数据库更改都将回滚。发生这种情况是因为 @Transactional
有一个名为 propagationLevel
的 属性,如果保留默认值,它将在单个中执行任何内部事务(例如 persist
操作的事务)外部事务(即 validateAndPushA
、validateAndPushB
、validate
和 persist
都将在同一个事务中执行 - 因此这些方法抛出的任何异常都将导致整个事务被回滚).
@Transactional
允许进行大量微调,例如对于哪些异常不应回滚事务。请参阅文档了解所有详细信息。
希望对您有所帮助!