RuntimeException в дочерней функции не должно влиять на родительскую вызывающую функцию - играет ли REQUIRES_NEW роль? - PullRequest
0 голосов
/ 02 апреля 2020

Я работаю с базой данных DB2 и протестировал следующий код: независимо от того, имеет ли methodB значение Propagation.REQUIRES_NEW или нет, если для methodB есть исключение, результат methodA будет корректно зафиксирован независимо.

Это противоречит моему предположению что Propagation.REQUIRES_NEW должен использоваться для достижения этой цели.

        ClassA
            @Autowire
            private ClassB classB;

            @Transactional
            methodA(){
                ...
                try{
                     classB.methodB();
                }catch(RuntimeException ex){
                     handleException(ex);
                }
                ...
            }

        ClassB
            @Transactional(propagation = Propagation.REQUIRES_NEW)
            methodB(){...}

Спасибо за @Kayaman Я думаю, я понял это сейчас.

Поведение, которое я видел, связано с @Transactional метода methodB аннотация не работала, поэтому methodB обрабатывается как обычная функция без любой аннотации транзакции.

В чем проблема, так это то, что в methodA я вызывал methodB из подкласса ClassB с помощью super.methodB() и подумал, что он даст транзакционный метод B, который не работает:

    @Service
    @Primary
    ClassC extends ClassB{
        @override
        methodB(){
            super.methodB();
        }
    }

Я знаю, что аннотация транзакции не будет работать, если вы вызываете метод транзакции из другого нетранзакционного метода того же учебный класс.

Не знал, что super.methodB() также потерпит неудачу по той же причине (кто-нибудь может дать немного больше объяснений, пожалуйста?)


В заключение, на примере первого блок кода, когда methodB имеет RuntimeException,

Если methodB имеет NO transaction аннотацию: A & B совместно используют одну и ту же транзакцию; methodA НЕ откатит

, если methodB имеет аннотацию REQUIRED: A & B совместно используют одну и ту же транзакцию; methodA выполнит откат

, если methodB имеет аннотацию REQUIRES_NEW: A & B имеют отдельные транзакции; methodA НЕ ОТКАДЕТ

1 Ответ

2 голосов
/ 02 апреля 2020

Без REQUIRES_NEW (то есть по умолчанию REQUIRED или одного из других, который ведет себя аналогичным образом), ClassB.methodB() участвует в той же транзакции , что и ClassA.methodA(). Исключение в methodB() помечает той же транзакции , которую необходимо откатить. Даже если вы поймаете исключение, транзакция будет откатана.

При REQUIRES_NEW откат транзакции будет определен как methodB(), поэтому, когда вы перехватываете исключение, все еще остается исправный исходный проверенная существующая транзакция.


ClassA
@Transactional
methodA(){
    try{
         classB.methodB();
    }catch(RuntimeException ex){
         handleException(ex);
    }
}

ClassB
@Transactional
methodB(){
    throw new RuntimeException();
}

Приведенный выше код выполнит откат всей транзакции. С propagation=TransactionPropagation.REQUIRES_NEW для methodB() не будет.

Без любых комментариев для methodB(), будет только одна граница tx на уровне methodA(), и Spring не будет знать что исключение выдается, так как оно перехватывается в середине метода. Это похоже на вставку содержимого от methodB() до methodA(). Если это исключение составляет исключение из базы данных (например, NullPointerException), транзакция будет выполняться нормально. Если это исключение является исключением из базы данных, базовая транзакция базы данных 1032 * будет откатана, но Spring об этом не знает. Затем Spring пытается выполнить фиксацию и выдает UnexpectedRollbackException, потому что база данных не позволит зафиксировать передачу.

Оставлять аннотацию явно или случайно неправильно. Если вы намереваетесь выполнять операции с БД, вы должны работать с четко определенным контекстом транзакции и знать свое распространение.

Вызов super.methodB() обходит нормальный механизм проксирования Spring, так что даже если есть аннотация, она игнорируется. Наконец, звонить по номеру super.methodB() мне кажется запахом дизайна. Использование наследования для сокращения количества строк часто является плохой практикой, и в этом случае возникает серьезная ошибка.

...