Я разобрал кое-какую архитектуру Java и материалы интервью (микросервисы, кластеры, распределенное, промежуточное ПО и т.д.), а друзья, кому нужно, могут обратить внимание на официальный аккаунт [Внутренние дела программиста], и получить самостоятельно без всяких рутин
-
Интервьюер немного смутился, сказав 9 распределенных методов генерации ID на одном дыхании.
-
9 видов распределенного ID поколения Meituan (Leaf) в реальном бою
введение
Вчера фанаты паблика аккаунта задали вопрос, сообщив, что им задавали в интервью ранее@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] Самовывоз