Поведение распространения транзакций Spring:
springУникальное поведение распространения транзакций, Spring поддерживает 7 вариантов поведения распространения транзакций для определения границы транзакции между клиентом и вызываемым концом (с точки зрения непрофессионала, это сложный контроль границы транзакции, сформированный, когда несколько служб с управлением транзакциями вызываются друг к другу). на рисунке показан 7-минутный механизм распространения транзакций
o
распространять поведение |
значение |
PROPAGATION_REQUIRED (ОБЯЗАТЕЛЬНО в файле XML) |
Указывает, что текущий метод должен выполняться в контексте с транзакцией.Если у клиента выполняется транзакция, вызываемый конец будет выполняться в транзакции, в противном случае транзакция будет перезапущена. (Если на вызываемой стороне возникает исключение, транзакция как на вызывающей, так и на вызываемой стороне будет отменена) |
PROPAGATION_SUPPORTS (SUPPORTS в файле XML) |
Указывает, что текущий метод не обязательно должен иметь контекст транзакции, но он также может выполняться внутри транзакции, если он есть. |
PROPAGATION_MANDATORY (ОБЯЗАТЕЛЬНО в файле XML) |
Указывает, что текущий метод должен выполняться в транзакции, если транзакции нет, будет выдано исключение |
PROPAGATION_NESTED (ВКЛАДЫВАЕТСЯ в файл XML) |
Указывает, что если в текущем методе выполняется транзакция, этот метод должен выполняться во вложенной транзакции, а вложенная транзакция может быть зафиксирована или отменена независимо от инкапсулированной транзакции. Если инкапсулированная транзакция существует, а внешняя транзакция генерирует исключение и выполняет откат, то внутреннюю транзакцию необходимо откатить, иначе внутренняя транзакция не влияет на внешнюю транзакцию. Если инкапсулирующая транзакция не существует, то же, что и PROPAGATION_REQUIRED. |
PROPAGATION_NEVER (НИКОГДА в файле XML) |
Указывает, что текущая транзакция не должна выполняться в транзакции, если транзакция есть, выдать исключение |
PROPAGATION_REQUIRES_NEW (REQUIRES_NEW в файле XML) |
Указывает, что текущий метод должен выполняться в собственной транзакции. Будет запущена новая транзакция, и, если уже выполняется существующая транзакция, метод будет приостановлен во время выполнения до тех пор, пока новая транзакция не будет зафиксирована или не будет отменено. |
PROPAGATION_NOT_SUPPORTED (NOT_SUPPORTED в файле XML) |
Указывает, что метод не следует запускать в транзакции. Если транзакция выполняется, она будет приостановлена во время выполнения и не возобновится до тех пор, пока транзакция не будет зафиксирована или отменена. |
Spring настраивает декларативные транзакции:
* Настроить источник данных
* Настройка менеджера транзакций
* Характеристики распространения транзакций
* Эти классы и эти методы используют транзакции
Конфигурация транзакции в файле конфигурации Spring всегда состоит из трех частей, а именно DataSource, TransactionManager и прокси-механизма.Независимо от того, какой метод конфигурации используется, обычно изменяется только часть прокси-механизма.
DataSource и TransactionManager изменяются только в зависимости от метода доступа к данным.Например, при использовании Hibernate для доступа к данным DataSource на самом деле является SessionFactory, а TransactionManager реализуется как HibernateTransactionManager.
В зависимости от механизма прокси существует несколько различных способов настройки транзакций Spring:
Первый способ: у каждого бина есть прокси
Второй способ: все бины имеют общий базовый класс прокси
Третий способ: использовать перехватчик
Четвертый способ: перехватчик, настроенный с тегом tx
Способ пятый: полная аннотация
будь осторожен:
1. Границы транзакции Spring начинаются до вызова бизнес-метода. Фиксация или откат выполняются после выполнения бизнес-метода, в зависимости от того, было ли выдано исключение во время выполнения.
1.PROPAGATION_REQUIRED
Добавляем соответствующие методы User1Service и User2ServicePropagation.REQUIRED
Атрибуты.
Метод User1Service:
@Service
public class User1ServiceImpl implements User1Service {
//省略其他...
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequired(User1 user){
user1Mapper.insert(user);
}
}
Метод User2Service:
@Service
public class User2ServiceImpl implements User2Service {
//省略其他...
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequired(User2 user){
user2Mapper.insert(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequiredException(User2 user){
user2Mapper.insert(user);
throw new RuntimeException();
}
}
1.1 Сценарий 1
Периферийный метод этого сценария не запускает транзакцию.
Способ проверки 1:
@Override
public void notransaction_exception_required_required(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequired(user2);
throw new RuntimeException();
}
Метод проверки 2:
@Override
public void notransaction_required_required_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiredException(user2);
}
Выполняем метод проверки отдельно, результат:
серийный номер метода проверки | Результаты базы данных | Анализ результатов |
---|---|---|
1 | Вставлены «Чжан Сан» и «Ли Си». | Периферийный метод не открывает транзакцию, а вставленные методы «Чжан Сан» и «Ли Си» работают независимо друг от друга. Исключение периферийного метода не влияет на независимую транзакцию «Чжан Сан» и «Ли Си». Методы Si", вставленные внутрь. |
2 | «Чжан Сан» вставлено, «Ли Си» не вставлено. | Периферийный метод не имеет транзакции, а методы вставки «Чжан Сан» и «Ли Си» выполняются независимо в своих собственных транзакциях, поэтому метод вставки «Ли Си» вызывает исключение и откатывает только метод вставки «Ли Си». , вставка метода "Чжан Сан" не влияет. |
Вывод: с помощью этих двух методов мы доказываем, что в случае, когда периферийный метод не открывает транзакциюPropagation.REQUIRED
Декорированный внутренний метод заново откроет свою собственную транзакцию, а открытые транзакции не зависят друг от друга и не мешают друг другу.
1.2 Сценарий 2
Периферийный метод запускает транзакцию, что является сценарием с относительно высокой частотой использования.
Способ проверки 1:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_required(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequired(user2);
throw new RuntimeException();
}
Метод проверки 2:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_required_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiredException(user2);
}
Метод проверки 3:
@Transactional
@Override
public void transaction_required_required_exception_try(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
try {
user2Service.addRequiredException(user2);
} catch (Exception e) {
System.out.println("方法回滚");
}
}
Выполняем метод проверки отдельно, результат:
серийный номер метода проверки | Результаты базы данных | Анализ результатов |
---|---|---|
1 | «Чжан Сан» и «Ли Си» не вставляются. | Внешний метод запускает транзакцию, внутренний метод присоединяется к транзакции внешнего метода, внешний метод выполняет откат, и внутренний метод также выполняет откат. |
2 | «Чжан Сан» и «Ли Си» не вставляются. | Внешний метод запускает транзакцию, внутренний метод присоединяется к транзакции внешнего метода, внутренний метод создает исключение и выполняет откат, а внешний метод обнаруживает исключение и вызывает откат всей транзакции. |
3 | «Чжан Сан» и «Ли Си» не вставляются. | Внешний метод запускает транзакцию, внутренний метод присоединяется к транзакции внешнего метода, а внутренний метод создает исключение и выполняет откат.Даже если метод перехвачен и не обнаружен внешним методом, вся транзакция все равно откатывается. |
Вывод: Приведенные выше экспериментальные результаты показывают, что в случае периферийного метода открытия транзакцииPropagation.REQUIRED
Декорированный внутренний метод будет добавлен к транзакции внешнего метода, всеPropagation.REQUIRED
И декорированный внутренний метод, и внешний метод принадлежат одной и той же транзакции.Пока происходит откат одного метода, откатывается вся транзакция.
2.PROPAGATION_REQUIRES_NEW
Добавляем соответствующие методы User1Service и User2ServicePropagation.REQUIRES_NEW
Атрибуты.
Метод User1Service:
@Service
public class User1ServiceImpl implements User1Service {
//省略其他...
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addRequiresNew(User1 user){
user1Mapper.insert(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void addRequired(User1 user){
user1Mapper.insert(user);
}
}
Метод User2Service:
@Service
public class User2ServiceImpl implements User2Service {
//省略其他...
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addRequiresNew(User2 user){
user2Mapper.insert(user);
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addRequiresNewException(User2 user){
user2Mapper.insert(user);
throw new RuntimeException();
}
}
2.1 Сценарий 1
Периферийный метод не запускает транзакцию.
Способ проверки 1:
@Override
public void notransaction_exception_requiresNew_requiresNew(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequiresNew(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNew(user2);
throw new RuntimeException();
}
Метод проверки 2:
@Override
public void notransaction_requiresNew_requiresNew_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequiresNew(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNewException(user2);
}
Выполняем метод проверки отдельно, результат:
серийный номер метода проверки | Результаты базы данных | Анализ результатов |
---|---|---|
1 | Вставлено «Чжан Сан» и вставлено «Ли Си». | В периферийном методе нет транзакции. Вставка методов "Чжан Сан" и "Ли Си" выполняется независимо в своих собственных транзакциях, а внешний метод выдает исключение и выполняет откат, не затрагивая внутренний метод. |
2 | «Чжан Сан» вставлено, «Ли Си» не вставлено | Периферийный метод не открывает транзакцию. Вставка метода «Чжан Сан» и вставка метода «Ли Си» соответственно открывают свои транзакции. Вставка метода «Ли Си» вызывает исключение и откат, а другие транзакции не затрагиваются . |
Вывод: с помощью этих двух методов мы доказываем, что в случае, когда периферийный метод не открывает транзакциюPropagation.REQUIRES_NEW
Декорированный внутренний метод заново откроет свою собственную транзакцию, а открытые транзакции не зависят друг от друга и не мешают друг другу.
2.2 Сценарий 2
Периферийный метод запускает транзакцию.
Способ проверки 1:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_exception_required_requiresNew_requiresNew(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNew(user2);
User2 user3=new User2();
user3.setName("王五");
user2Service.addRequiresNew(user3);
throw new RuntimeException();
}
Метод проверки 2:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNew(user2);
User2 user3=new User2();
user3.setName("王五");
user2Service.addRequiresNewException(user3);
}
Метод проверки 3:
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void transaction_required_requiresNew_requiresNew_exception_try(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addRequired(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addRequiresNew(user2);
User2 user3=new User2();
user3.setName("王五");
try {
user2Service.addRequiresNewException(user3);
} catch (Exception e) {
System.out.println("回滚");
}
}
Выполняем метод проверки отдельно, результат:
серийный номер метода проверки | Результаты базы данных | Анализ результатов |
---|---|---|
1 | «Чжан Сан» не вставляется, вставляется «Ли Си» и вставляется «Ван Ву». | Периферийный метод запускает транзакцию, вставляет метод «Чжан Сан» и периферийный метод как транзакцию, вставляет метод «Ли Си» и вставляет метод «Ван Ву» в отдельные новые транзакции, а периферийный метод генерирует исключение и только откатывается так же, как периферийный метод.Метод транзакции, поэтому метод вставки «Чжан Сан» откатывается. |
2 | «Чжан Сан» не вставлено, «Ли Си» вставлено и «Ван Ву» не вставлено. | Периферийный метод открывает транзакцию, вставляет метод «Чжан Сан» и периферийный метод в качестве транзакции, а также вставляет метод «Ли Си» и метод «Ван Ву» в отдельные новые транзакции. Вставка метода «Ван Ву» вызывает исключение, транзакция, которая первой вставляет метод «Ван Ву», откатывается, исключение продолжает создаваться и воспринимается периферийным методом, транзакция периферийного метода также откатывается. назад, поэтому метод вставки «Чжан Сан» также откатывается. |
3 | «Чжан Сан» вставлено, «Ли Си» вставлено, а «Ван Ву» не вставлено. | Периферийный метод открывает транзакцию, вставляет метод «Чжан Сан» и периферийный метод в качестве транзакции, а также вставляет метод «Ли Си» и метод «Ван Ву» в отдельные новые транзакции. Вставка метода «Ван Ву» вызывает исключение, транзакция, вставленная в метод «Ван Ву», сначала откатывается, исключение перехватывается и не будет восприниматься периферийным методом, а транзакция периферийного метода не откатывается. назад, поэтому вставка метода «Чжан Сан» прошла успешно. |
Вывод: В случае, когда периферийный метод открывает транзакциюPropagation.REQUIRES_NEW
Декорированный внутренний метод по-прежнему будет открывать независимую транзакцию независимо, и он также не зависит от транзакции внешнего метода.Внутренние методы, внутренние методы и транзакции внешнего метода независимы друг от друга и не мешают друг другу.
3.PROPAGATION_NESTED
Добавляем соответствующие методы User1Service и User2ServicePropagation.NESTED
Атрибуты.
Метод User1Service:
@Service
public class User1ServiceImpl implements User1Service {
//省略其他...
@Override
@Transactional(propagation = Propagation.NESTED)
public void addNested(User1 user){
user1Mapper.insert(user);
}
}
Метод User2Service:
@Service
public class User2ServiceImpl implements User2Service {
//省略其他...
@Override
@Transactional(propagation = Propagation.NESTED)
public void addNested(User2 user){
user2Mapper.insert(user);
}
@Override
@Transactional(propagation = Propagation.NESTED)
public void addNestedException(User2 user){
user2Mapper.insert(user);
throw new RuntimeException();
}
}
3.1 Сценарий 1
Периферийный метод этого сценария не запускает транзакцию.
Способ проверки 1:
@Override
public void notransaction_exception_nested_nested(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNested(user2);
throw new RuntimeException();
}
Метод проверки 2:
@Override
public void notransaction_nested_nested_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNestedException(user2);
}
Выполняем метод проверки отдельно, результат:
серийный номер метода проверки | Результаты базы данных | Анализ результатов |
---|---|---|
1 | Вставлены «Чжан Сан» и «Ли Си». | Периферийный метод не открывает транзакцию, а вставленные методы «Чжан Сан» и «Ли Си» работают независимо друг от друга. Исключение периферийного метода не влияет на независимую транзакцию «Чжан Сан» и «Ли Си». Методы Si", вставленные внутрь. |
2 | «Чжан Сан» вставлено, «Ли Си» не вставлено. | Периферийный метод не имеет транзакции, а методы вставки «Чжан Сан» и «Ли Си» выполняются независимо в своих собственных транзакциях, поэтому метод вставки «Ли Си» вызывает исключение и откатывает только метод вставки «Ли Си». , вставка метода "Чжан Сан" не влияет. |
Вывод: с помощью этих двух методов мы доказываем, что в случае, когда периферийный метод не открывает транзакциюPropagation.NESTED
иPropagation.REQUIRED
Эффект тот же, измененные внутренние методы будут заново открывать свои собственные транзакции, а открытые транзакции независимы друг от друга и не мешают друг другу.
3.2 Сценарий 2
Периферийный метод запускает транзакцию.
Способ проверки 1:
@Transactional
@Override
public void transaction_exception_nested_nested(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNested(user2);
throw new RuntimeException();
}
Метод проверки 2:
@Transactional
@Override
public void transaction_nested_nested_exception(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
user2Service.addNestedException(user2);
}
Метод проверки 3:
@Transactional
@Override
public void transaction_nested_nested_exception_try(){
User1 user1=new User1();
user1.setName("张三");
user1Service.addNested(user1);
User2 user2=new User2();
user2.setName("李四");
try {
user2Service.addNestedException(user2);
} catch (Exception e) {
System.out.println("方法回滚");
}
}
Выполняем метод проверки отдельно, результат:
серийный номер метода проверки | Результаты базы данных | Анализ результатов |
---|---|---|
1 | «Чжан Сан» и «Ли Си» не вставляются. | Внешний метод запускает транзакцию, внутренняя транзакция является подтранзакцией внешней транзакции, внешний метод выполняет откат, и внутренний метод также выполняет откат. |
2 | «Чжан Сан» и «Ли Си» не вставляются. | Внешний метод запускает транзакцию, внутренняя транзакция является подтранзакцией внешней транзакции, внутренний метод создает исключение и выполняет откат, а внешний метод обнаруживает исключение и вызывает откат всей транзакции. |
3 | «Чжан Сан» вставлено, «Ли Си» не вставлено. | Периферийный метод запускает транзакцию, а внутренняя транзакция является подтранзакцией периферийной транзакции.Вставка внутреннего метода «Чжан Сан» вызывает исключение, и подтранзакция может быть отброшена отдельно. |
Вывод: Приведенные выше экспериментальные результаты показывают, что в случае периферийного метода открытия транзакцииPropagation.NESTED
Декорированный внутренний метод принадлежит подтранзакции внешней транзакции, периферийная основная транзакция откатывается, подтранзакция должна быть отброшена, а внутренняя подтранзакция может быть отброшена одна, не затрагивая периферийную основную транзакцию и другие субтранзакции
4. Сходства и различия между REQUIRED, REQUIRES_NEW, NESTED
Сравнивая «1.2 Сценарий 2» и «3.2 Сценарий 2», мы видим, что:
Внутренние методы, модифицированные NESTED и REQUIRED, относятся к транзакции внешнего метода.Если внешний метод выдает исключение, транзакция этих двух методов будет отброшена. Но ТРЕБУЕТСЯ присоединиться к транзакции периферийного метода, поэтому он принадлежит к той же транзакции, что и периферийная транзакция.Как только ТРЕБУЕМАЯ транзакция выдает исключение и откатывается, транзакция периферийного метода также будет откатываться. NESTED является подтранзакцией периферийного метода и имеет отдельную точку сохранения, поэтому метод NESTED выдает исключение и откатывается, не затрагивая транзакцию периферийного метода.
Из сравнения «2.2 Сценарий 2» и «3.2 Сценарий 2» мы можем узнать, что:
И NESTED, и REQUIRES_NEW могут откатывать транзакцию внутреннего метода, не затрагивая транзакцию внешнего метода. Но поскольку NESTED является вложенной транзакцией, после отката внешнего метода подтранзакции, которые являются транзакциями внешнего метода, также будут откатываться. В то время как REQUIRES_NEW реализуется путем открытия новой транзакции, внутренняя транзакция и периферийная транзакция являются двумя транзакциями, и откат периферийной транзакции не повлияет на внутреннюю транзакцию.
5. Другие способы распространения транзакций
Ввиду объема статьи тесты других вариантов поведения распространения транзакций здесь не описываются.Заинтересованные читатели могут перейти к исходному коду, чтобы найти соответствующий тестовый код и объяснение результата. Портал:GitHub.com/TMT-color/вдруг…
Пример использования моделирования
С таким количеством введенных способов распространения транзакций, как мы применим их на практике? Позвольте мне привести пример ниже:
Допустим, у нас есть метод регистрации, который вызывает метод добавления баллов.Если мы надеемся, что добавление баллов не повлияет на процесс регистрации (то есть, если добавление баллов не откатится, то метод регистрации не может быть откатан), мы напишем :
@Service
public class UserServiceImpl implements UserService {
@Transactional
public void register(User user){
try {
membershipPointService.addPoint(Point point);
} catch (Exception e) {
//省略...
}
//省略...
}
//省略...
}
Мы также оговариваем, что отказ в регистрации должен повлиять наaddPoint()
метода (метод регистрации откатывается и метод добавления интеграла тоже нужно откатить), тоaddPoint()
Метод нужно реализовать следующим образом:
@Service
public class MembershipPointServiceImpl implements MembershipPointService{
@Transactional(propagation = Propagation.NESTED)
public void addPoint(Point point){
try {
recordService.addRecord(Record record);
} catch (Exception e) {
//省略...
}
//省略...
}
//省略...
}
мы заметили вaddPoint()
также называемыйaddRecord()
этот метод используется для ведения журнала. Его реализация такова:
@Service
public class RecordServiceImpl implements RecordService{
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void addRecord(Record record){
//省略...
}
//省略...
}
мы заметилиaddRecord()
методpropagation = Propagation.NOT_SUPPORTED
, т.к. не имеет значения точность лога, может быть на один больше или на один меньше, так чтоaddRecord()
Сам метод и периферияaddPoint()
исключение генерации метода не будетaddRecord()
метод откатывается, иaddRecord()
Метод выдает исключение и не влияет на внешнийaddPoint()
выполнение метода.
Благодаря этому примеру я считаю, что у каждого есть более интуитивное понимание использования поведения распространения транзакций, и комбинация различных атрибутов действительно может сделать нашу бизнес-реализацию более гибкой и разнообразной.
в заключении
Благодаря приведенному выше введению я считаю, что вы лучше понимаете поведение распространения транзакций Spring, и надеюсь, что ваша ежедневная работа по разработке будет полезна.