Каков наилучший способ справиться с исключением на шаге 1, когда шаг 2 должен быть выполнен - PullRequest
0 голосов
/ 21 сентября 2018

В нашем случае приложения пользователь запросил обновление 2 полей, скажем (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 голосов
/ 21 сентября 2018

Поскольку у вас есть 2 операции, которые должны выполняться, даже если один из них завершился неудачно, и вы также хотите распространить исключение / ошибку на вышеприведенный уровень, можно использовать следующий подход: *

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.

0 голосов
/ 21 сентября 2018

Два обновления должны быть включены в транзакцию.

@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, * 1025).*, validate и persist будут выполняться в одной и той же транзакции, поэтому любое исключение, вызванное этими методами, приведет к откату всей транзакции.

@Transactional допускает большую тонкую настройкуНапример, для каких исключений транзакция не должна откатываться.Пожалуйста, обратитесь к документации для всех деталей.

Надеюсь, это поможет!

0 голосов
/ 21 сентября 2018

Захватите исключение вместо использования finally:

boolean failed = false;
try {

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

if (failed) {

}

Если вы хотите, чтобы исключение распространялось, вы можете сохранить его как переменную и затем перебросить его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...