Скажите 6 видов сценариев отказа на одном дыхании, аннотация @Transactional

Java
Скажите 6 видов сценариев отказа на одном дыхании, аннотация @Transactional

Я разобрал кое-какую архитектуру Java и материалы интервью (микросервисы, кластеры, распределенное, промежуточное ПО и т.д.), а друзья, кому нужно, могут обратить внимание на официальный аккаунт [Внутренние дела программиста], и получить самостоятельно без всяких рутин

введение

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

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

1. Дела

Управление транзакциями является неотъемлемой частью разработки системы.SpringОбеспечивает хороший механизм управления транзакциями, в основном разделенный на编程式事务и声明式事务два вида.

Программные транзакции: относится к ручному управлению отправкой транзакций, откатом и другими операциями в коде, код относительно навязчив, следующий пример:

try {
    //TODO something
     transactionManager.commit(status);
} catch (Exception e) {
    transactionManager.rollback(status);
    throw new InvoiceApplyException("异常失败");
}

декларативная сделка: на основеAOPАспектно-ориентированный, он отделяет конкретный бизнес от части обработки транзакций, а код менее навязчив, поэтому в реальной разработке больше используются декларативные транзакции. Есть также два способа реализации декларативных транзакций.TXиAOPМетод файла конфигурации xml основан на аннотации @Transactional.

    @Transactional
    @GetMapping("/test")
    public String test() {
    
        int insert = cityInfoDictMapper.insert(cityInfoDict);
    }

2. Введение в @Transactional

1. Где можно использовать аннотацию @Transactional?

@Transactional можно использовать в接口,,类方法.

  • Действовать в классе: Когда аннотация @Transactional выпущена для класса, это означает, что всеpublicметоды настроены с использованием одной и той же информации об атрибутах транзакций.

  • Действуйте по методу: когда класс настроен с помощью @Transactional, а метод также настроен с помощью @Transactional, транзакция метода переопределит информацию о конфигурации транзакции класса.

  • действует на интерфейс: этот метод использования не рекомендуется, поскольку после того, как он отмечен в интерфейсе, а Spring AOP настроен на использование динамического прокси-сервера CGLib, это приведет к сбою аннотации @Transactional.

@Transactional
@RestController
@RequestMapping
public class MybatisPlusController {
    @Autowired
    private CityInfoDictMapper cityInfoDictMapper;
    
    @Transactional(rollbackFor = Exception.class)
    @GetMapping("/test")
    public String test() throws Exception {
        CityInfoDict cityInfoDict = new CityInfoDict();
        cityInfoDict.setParentCityId(2);
        cityInfoDict.setCityName("2");
        cityInfoDict.setCityLevel("2");
        cityInfoDict.setCityCode("2");
        int insert = cityInfoDictMapper.insert(cityInfoDict);
        return insert + "";
    }
}

2. Каковы атрибуты аннотации @Transactional?

свойство распространения

propagationПредставляет поведение распространения транзакции, значение по умолчанию —Propagation.REQUIRED, другая информация атрибута выглядит следующим образом:

  • Propagation.REQUIRED: если транзакция уже существует, присоединиться к транзакции, если текущей транзакции нет, создать новую транзакцию.(То есть, если и метод A, и метод B аннотированы, в режиме распространения по умолчанию метод A вызывает метод B внутри, и транзакции двух методов будут объединены в одну транзакцию.)

  • Propagation.SUPPORTS: если есть текущая транзакция, присоединиться к транзакции; если текущей транзакции нет, продолжить выполнение в нетранзакционном режиме.

  • Propagation.MANDATORY: если есть текущая транзакция, присоединиться к транзакции, если текущей транзакции нет, создать исключение.

  • Propagation.REQUIRES_NEW: воссоздать новую транзакцию, если есть текущая транзакция, приостановить текущую транзакцию.(Когда метод класса A использует значение по умолчаниюPropagation.REQUIREDобразец, метод b в классе B плюс принятиеPropagation.REQUIRES_NEWрежиме, а затем вызвать метод b в методе a для работы с базой данных, но после того, как метод a выдает исключение, метод b не выполняет откат, потому чтоPropagation.REQUIRES_NEWприостановит транзакцию метода)

  • Propagation.NOT_SUPPORTED: запустить нетранзакционным способом, если есть текущая транзакция, приостановить текущую транзакцию.

  • Propagation.NEVER: работает без транзакций, вызывая исключение, если в данный момент есть транзакция.

  • Propagation.NESTED: То же, что и распространение. ТРЕБУЕТСЯ.

свойство изоляции

isolation: уровень изоляции транзакции, значение по умолчаниюIsolation.DEFAULT.

  • Isolation.DEFAULT: использовать уровень изоляции базовой базы данных по умолчанию.
  • Isolation.READ_UNCOMMITTED
  • Isolation.READ_COMMITTED
  • Isolation.REPEATABLE_READ
  • Isolation.SERIALIZABLE
свойство тайм-аута

timeout: время ожидания транзакции, значение по умолчанию равно -1. Если лимит времени превышен, но транзакция не завершена, транзакция автоматически откатывается.

свойство только для чтения

readOnly: Указывает, является ли транзакция транзакцией только для чтения, значение по умолчанию — false; чтобы игнорировать те методы, которые не требуют транзакций, например чтение данных, можно установить только для чтения значение true.

откатДля свойства

rollbackFor: Используется для указания типа исключения, которое может инициировать откат транзакции.Можно указать несколько типов исключений.

noRollbackForАтрибуты**

noRollbackFor: Выдает указанный тип исключения и не откатывает транзакцию.Вы также можете указать несколько типов исключений.

2. Сценарий @транзакционного сбоя

Далее мы проанализируем сценарии, в которых аннотация @Transactional не сработает, исходя из конкретного кода.

1. @Transactional применяется к закрытым модифицированным методам.

еслиTransactionalАннотации используются в не-publicВ измененном методе Transactional будет недействительным.

在这里插入图片描述

Причина, по которой это не удается, заключается в том, что при проксировании Spring AOP, как показано на рисунке вышеTransactionInterceptor(перехватчик транзакций) перехватывает до и после выполнения целевого метода,DynamicAdvisedInterceptor(внутренний класс CglibAopProxy) метод перехвата илиJdkDynamicAopProxyМетод вызова будет вызываться косвенноAbstractFallbackTransactionAttributeSourceизcomputeTransactionAttributeметод для получения информации о конфигурации транзакции аннотации Transactional.

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
}

Этот метод проверяет, является ли модификатор целевого метода общедоступным.Если он не является общедоступным, он не получит информацию о конфигурации свойства @Transactional.

Уведомление:protected,privateиспользуется в модифицированном методе@TransactionalОбратите внимание, что хотя транзакция недействительна, ошибки не будет, и мы склонны совершать ошибки.

2. Распространение атрибута аннотации @Transactional установлено неправильно

Этот сбой связан с ошибками конфигурации. Если следующие три распространения настроены неправильно, транзакция не будет отменена.

TransactionDefinition.PROPAGATION_SUPPORTS: если есть текущая транзакция, присоединиться к транзакции; если текущей транзакции нет, продолжить выполнение в нетранзакционном режиме.TransactionDefinition.PROPAGATION_NOT_SUPPORTED: запустить в нетранзакционном режиме, если есть текущая транзакция, приостановить текущую транзакцию.TransactionDefinition.PROPAGATION_NEVER: Работает в нетранзакционном режиме, выдает исключение, если транзакция уже существует.

3. Неправильно задан атрибут rollbackFor аннотации @Transactional

rollbackForВы можете указать типы исключений, которые могут инициировать откат транзакции. Spring бросает непроверенные по умолчаниюuncheckedисключение (унаследовано отRuntimeExceptionисключение) илиErrorТранзакция откатывается; другие исключения не вызывают откат транзакции. Если вы выбрасываете другие типы исключений в транзакции, но ожидаете, что Spring сможет откатить транзакцию, вам нужно указатьrollbackForАтрибуты.

在这里插入图片描述

// 希望自定义的异常可以进行回滚
@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class

Если исключение, сгенерированное в целевом методе,rollbackForПодкласс указанного исключения, транзакция также будет отброшена. Исходный код Spring выглядит следующим образом:

private int getDepth(Class<?> exceptionClass, int depth) {
        if (exceptionClass.getName().contains(this.exceptionName)) {
            // Found it!
            return depth;
}
        // If we've gone as far as we can go and haven't found it...
        if (exceptionClass == Throwable.class) {
            return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}

4. Вызовы методов в том же классе приводят к сбою @Transactional

При разработке неизбежно вызывать методы одного и того же класса, например, есть класс Test, у которого есть метод A, и A вызывает метод B этого класса (независимо от того, модифицируется ли метод B с помощью public или private). , но метод A не объявляет транзакцию аннотации, а метод B имеет. Тогда после внешнего вызова метода A транзакция метода B не будет работать. Это также место, где часто совершаются ошибки.

Так почему же это происходит? На самом деле это связано с использованиемSpring AOPвызвано прокси, потому что методы транзакций вызываются только кодом вне текущего классаSpringСгенерированные прокси-объекты для управления.

//@Transactional
    @GetMapping("/test")
    private Integer A() throws Exception {
        CityInfoDict cityInfoDict = new CityInfoDict();
        cityInfoDict.setCityName("2");
        /**
         * B 插入字段为 3的数据
         */
        this.insertB();
        /**
         * A 插入字段为 2的数据
         */
        int insert = cityInfoDictMapper.insert(cityInfoDict);

        return insert;
    }

    @Transactional()
    public Integer insertB() throws Exception {
        CityInfoDict cityInfoDict = new CityInfoDict();
        cityInfoDict.setCityName("3");
        cityInfoDict.setParentCityId(3);

        return cityInfoDictMapper.insert(cityInfoDict);
    }

5. Исключение «съедается» вашим уловом, что приводит к сбою @Transactional

Эта ситуация является наиболее распространенным сценарием сбоя аннотации @Transactional,

    @Transactional
    private Integer A() throws Exception {
        int insert = 0;
        try {
            CityInfoDict cityInfoDict = new CityInfoDict();
            cityInfoDict.setCityName("2");
            cityInfoDict.setParentCityId(2);
            /**
             * A 插入字段为 2的数据
             */
            insert = cityInfoDictMapper.insert(cityInfoDict);
            /**
             * B 插入字段为 3的数据
             */
            b.insertB();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Если внутри метода B возникает исключение, а метод A в это время пытается перехватить исключение метода B, можно ли нормально откатить транзакцию?

Ответ: Нет!

вызовет исключение:

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

потому что когдаServiceBПосле создания исключения вServiceBВыявление текущих транзакционных потребностейrollback. ноServiceAПоскольку вы вручную ловите это исключение и обрабатываете его,ServiceAдумаю, что текущая транзакция должна быть нормальнойcommit. В этот момент возникает несоответствие, т. е. из-за этого предыдущийUnexpectedRollbackExceptionаномальный.

springТранзакция запускается до вызова бизнес-метода и выполняется после его выполнения.commit or rollback, выполнение транзакции зависит от того, выдает ли онаruntime异常.如果抛出runtime exceptionИ если в вашем бизнес-методе нет подвоха, транзакция будет откатана.

Исключения перехвата обычно не нужны в бизнес-методах.throw new RuntimeException(), либо указать тип исключения в аннотации@Transactional(rollbackFor=Exception.class), иначе транзакция завершится неудачно, а коммит данных вызовет несогласованность данных, поэтому иногда try catch будет лишним.

6. Механизм базы данных не поддерживает транзакции

Вероятность этой ситуации невелика.Может ли транзакция вступить в силу или нет, является ключом к тому, поддерживает ли механизм базы данных транзакцию. Обычно используемые базы данных MySQL по умолчанию используют базы данных с поддержкой транзакций.innodbдвигатель. Как только ядро ​​базы данных переключится на нетранзакционныйmyisam, сделка в корне недействительна.

Суммировать

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


Сегодня так много нужно сказать, если эта статья была вам полезна, я надеюсь получить от вас лайк 👍

Ваше одобрение является движущей силой для моего письма!

Небольшие преимущества:

Сотни различных технических электронных книг пересылаются друг другу, тсс~,бесплатноОтправить его друзьям. Обратите внимание на ответ на общедоступный номер【666] Самовывоз