Я разобрал кое-какую архитектуру 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] Самовывоз