Трудно ли транзакциям Spring распространять свойства? Хватит это читать

Java

Трудно ли транзакциям Spring распространять свойства? Хватит это читать

Писательские навыки у автора пока неглубокие, если что не так, укажите великодушно, буду признателен

Изучение чего-либо требует единства знания и действия. Если вы знаете только теорию, но не практиковали ее, у вас не будет твердого понимания. Предполагается, что вы забудете это через несколько дней. Затем давайте попрактикуемся вместе, чтобы изучить свойства распространения транзакций Spring.

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

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

коммуникабельный стоимость описывать
PROPAGATION_REQUIRED 0 Поддержите текущую транзакцию, если нет, создайте новую транзакцию
PROPAGATION_SUPPORTS 1 Поддержите текущую транзакцию, если нет, она не будет работать в транзакционном режиме
PROPAGATION_MANDATORY 2 Поддерживать текущую транзакцию, создавать исключение, если текущей транзакции нет
PROPAGATION_REQUIRES_NEW 3 Независимо от того, существует ли текущая транзакция, будет запущена новая транзакция.
PROPAGATION_NOT_SUPPORTED 4 Транзакции не поддерживаются, если есть текущая транзакция, эта транзакция будет приостановлена ​​и не будет выполняться в транзакционном режиме.
PROPAGATION_NEVER 5 Транзакции не поддерживаются, создайте исключение, если есть транзакция
PROPAGATION_NESTED 6 Если есть текущая транзакция, начать новую транзакцию в текущей транзакции

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

В этой демонстрации мы используем базу данных H2, которая используется в памяти, поэтому нам как раз подходит демонстрация эффекта транзакции.Нам не нужно выполнять другие настройки.Мы создаем новую таблицу. Поместите следующее утверждение вschema.sqlВнутри файла программа SpringBoot автоматически создаст для нас такую ​​таблицу в памяти при запуске.

1CREATE TABLE FOO (ID INT IDENTITY, BAR VARCHAR(64));

Перед демонстрацией мы определим два классаFooServiceа такжеBarService. Мы используемBarServiceметод внутри вызоваFooServiceметод в .

Подготовка окружающей среды

Перед демонстрацией транзакции ее можно разделить на следующие ситуации: в соответствии с расположением и комбинацией мы можем нарисовать следующие восемь ситуаций.

  • Звонящий: есть ли транзакция
  • Звонивший: Есть ли исключение?
  • Callee: есть ли транзакция(Это контролируется свойством распространения)Так что не в комбинации перестановок
  • Callee: Есть ли исключение?
Есть ли у вызывающего абонента транзакция Есть ли у вызывающего объекта исключение Есть ли у вызываемого объекта исключение
имеют имеют имеют
имеют имеют без
имеют без имеют
имеют без без
без имеют имеют
без имеют без
без без имеют
без без без

класс исключений

один из нихRollbackExceptionэто класс исключений, определенный нами

 1@Service
2public class BarServiceImpl implements BarService{
3    @Autowired
4    private FooService fooService;
5
6     // PROPAGATION_REQUIRED演示 无事务
7    @Override
8    public void testRequiredNoTransactional() throws RollbackException {
9        fooService.testRequiredTransactional();
10    }
11}

абонент

существуетBarServiceДва метода определены в , один с транзакцией и один без транзакции.

 1// 有事务
2@Override
3@Transactional(rollbackFor = Exception.class)
4public void hasTransactional() throws RollbackException {
5
6}
7
8// 无事务
9@Override
10public void noTransactional() throws RollbackException {
11
12}

Далее мы изучим атрибуты распространения транзакций в соответствии с восемью определенными выше ситуациями в России.

`PROPAGATION_REQUIRED`

В соответствии с этим свойством распространения то, создает ли вызываемый объект новую транзакцию, зависит от того, есть ли у вызывающего объекта транзакция.

Фактически, чтобы понять характеристики этого свойства распространения, нам достаточно продемонстрировать два примера из восьми вышеперечисленных случаев.

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

Давайте сначала рассмотрим пример метода вызываемого класса.

 1@Service
2public class FooServiceImpl implements FooService {
3    @Autowired
4    private JdbcTemplate jdbcTemplate;
5
6    // REQUIRED传播属性-被调用者有异常抛出
7    @Override
8    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
9    public void testRequiredHasException() throws RollbackException {
10        jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ("+Global.REQUIRED_HAS_EXCEPTION+")");
11        throw new RollbackException();
12    }
13
14    // REQUIRED传播属性-被调用者无异常抛出
15    @Override
16    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
17    public void testRequiredNoException() throws RollbackException {
18        jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ("+Global.REQUIRED_NO_EXCEPTION+")");
19    }
20}

Далее мы рассмотрим пример вызывающего метода

 1@Service
2public class BarServiceImpl implements BarService{
3    @Autowired
4    private FooService fooService;
5
6    // 有事务
7    @Override
8    @Transactional(rollbackFor = Exception.class)
9    public void hasTransactional() throws RollbackException {
10        // 调用者有事务,抛异常  被调用者无异常
11        fooService.testRequiredNoException();
12        throw new RollbackException();
13    }
14
15    // 无事务
16    @Override
17    public void noTransactional() throws RollbackException {
18        // 调用者无事务,不抛异常  被调用者有异常
19        fooService.testRequiredHasException();
20    }
21}

На этом этапе мы делаем запрос, когда программа вызывается

 1String noException = Global.REQUIRED_NO_EXCEPTION;
2String hasException = Global.REQUIRED_HAS_EXCEPTION;
3
4try {
5    barService.noTransactional();
6}catch (Exception e){
7    log.info("第一种情况 {}",
8            jdbcTemplate
9                    .queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='"+hasException+"'", Long.class));
10}
11
12try {
13    barService.hasTransactional();
14}catch (Exception e){
15    log.info("第二种情况 {}",
16            jdbcTemplate
17                    .queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='"+noException+"'", Long.class));
18}

Просмотр распечатанного журнала

12019-10-16 13:02:04.142  INFO 11869 --- [           main] c.e.t.t.TransactionApplication           : 第一种情况 0
22019-10-16 13:02:04.143  INFO 11869 --- [           main] c.e.t.t.TransactionApplication           : 第二种情况 0

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

`PROPAGATION_SUPPORTS`

Есть ли у вызывающего абонента транзакция, полностью зависит от вызывающего.У вызывающего есть транзакция, если есть транзакция, и если у вызывающего нет транзакции, нет транзакции.

Далее мы будем использовать приведенные выше два примера, чтобы продемонстрировать

Есть ли у вызывающего абонента транзакция Есть ли у вызывающего объекта исключение Есть ли у вызываемого объекта исключение
без без имеют
имеют имеют без
  • Первый случай: если вызываемый объект выдает исключение, если данные все еще можно запросить, это означает, что транзакция не была откатана, что указывает на то, что у вызываемого объекта нет транзакции.
  • Второй случай: когда вызывающая сторона выбрасывает исключение, если данные не могут быть найдены, значит, два метода находятся в одной транзакции

Ниже приведен пример демонстрации

вызываемый, просто@Transactionalв примечанияхpropagationимущество заменено наPropagation.SUPPORTS

 1// SUPPORTS传播属性-被调用者有异常抛出
2@Override
3@Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS)
4public void testSupportsHasException() throws RollbackException {
5    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.SUPPORTS_HAS_EXCEPTION+"')");
6    throw new RollbackException();
7}
8
9// SUPPORTS传播属性-被调用者无异常抛出
10@Override
11@Transactional(rollbackFor = Exception.class,propagation = Propagation.SUPPORTS)
12public void testSupportsNoException() throws RollbackException {
13    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.SUPPORTS_NO_EXCEPTION+"')");
14}

Вызывающий код такой же, как в приведенном выше примере вызова, мы непосредственно видим эффект выполнения

12019-10-16 13:50:27.738  INFO 12174 --- [           main] c.e.t.t.TransactionApplication           : 第一种情况 1
22019-10-16 13:50:27.741  INFO 12174 --- [           main] c.e.t.t.TransactionApplication           : 第二种情况 0

Мы видели, что данные были найдены в первом случае, что указывает на то, что у вызываемого объекта нет транзакции в первом случае. Мы должны понять это сейчас
Поддержите текущую транзакцию, если нет, она не будет работать в транзакционном режиме.

`PROPAGATION_MANDATORY`

Это еще два примера, чтобы продемонстрировать

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

Далее приведен пример кода вызываемого объекта

 1// MANDATORY传播属性-被调用者有异常抛出
2@Override
3@Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY)
4public void testMandatoryHasException() throws RollbackException {
5    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.SUPPORTS_HAS_EXCEPTION+"')");
6    throw new RollbackException();
7}
8
9// MANDATORY传播属性-被调用者无异常抛出
10@Override
11@Transactional(rollbackFor = Exception.class,propagation = Propagation.MANDATORY)
12public void testMandatoryNoException() throws RollbackException {
13    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.SUPPORTS_NO_EXCEPTION+"')");
14}

Вызывающий код такой же, как в приведенном выше примере вызова, мы непосредственно видим эффект выполнения

12019-10-16 13:58:39.178 ERROR 12317 --- [           main] c.e.t.t.TransactionApplication           : org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
22019-10-16 13:58:39.276  INFO 12317 --- [           main] c.e.t.t.TransactionApplication           : 第一种情况 0
32019-10-16 13:58:39.281  INFO 12317 --- [           main] c.e.t.t.TransactionApplication           : 第二种情况 0

Мы обнаружили, что, как мы и предполагали, это означает, что вызываемый объект не будет создавать новую транзакцию самостоятельно.На данный момент мы должны понять это предложениеПоддерживать текущую транзакцию, создавать исключение, если текущей транзакции нет.

`PROPAGATION_REQUIRES_NEW`

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

Есть ли у вызывающего абонента транзакция Есть ли у вызывающего объекта исключение Есть ли у вызываемого объекта исключение
без без имеют
имеют имеют без
  • Первый случай: у вызывающего абонента нет транзакции, а вызываемый создаст новую транзакцию, поэтому данные не могут быть найдены.
  • Второй случай: у вызывающего есть транзакция, а вызываемый создаст новую транзакцию, поэтому вызывающий выдает исключение и не влияет на вызываемый, поэтому данные можно найти

Далее мы демонстрируем код.

вызываемый абонент

 1// REQUIRES_NEW传播属性-被调用者有异常抛出
2@Override
3@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
4public void testRequiresNewHasException() throws RollbackException {
5    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.REQUIRES_NEW_HAS_EXCEPTION+"')");
6    throw new RollbackException();
7}
8
9// REQUIRES_NEW传播属性-被调用者无异常抛出
10@Override
11@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
12public void testRequiresNewNoException() throws RollbackException {
13    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.REQUIRES_NEW_NO_EXCEPTION+"')");
14}

Пример звонящего такой же, как и выше, давайте посмотрим непосредственно на выполнение

12019-10-16 16:29:20.296  INFO 15553 --- [           main] c.e.t.t.TransactionApplication           : 第一种情况 0
22019-10-16 16:29:20.298  INFO 15553 --- [           main] c.e.t.t.TransactionApplication           : 第二种情况 1

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

`PROPAGATION_NOT_SUPPORTED `

Вызываемый объект не работает транзакционным образом, независимо от того, есть ли у вызывающего объекта транзакция.

Те же два примера

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

Далее проверяем нашу догадку

 1// NOT_SUPPORTED传播属性-被调用者有异常抛出
2@Override
3@Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED)
4public void testNotSupportHasException() throws RollbackException {
5    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NOT_SUPPORTS_HAS_EXCEPTION+"')");
6    throw new RollbackException();
7}
8
9// NOT_SUPPORTED传播属性-被调用者无异常抛出
10@Override
11@Transactional(rollbackFor = Exception.class,propagation = Propagation.NOT_SUPPORTED)
12public void testNotSupportNoException() throws RollbackException {
13    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NOT_SUPPORTS_NO_EXCEPTION+"')");
14}

Затем проверьте результат выполнения

12019-10-16 16:38:35.065  INFO 15739 --- [           main] c.e.t.t.TransactionApplication           : 第一种情况 1
22019-10-16 16:38:35.067  INFO 15739 --- [           main] c.e.t.t.TransactionApplication           : 第二种情况 1

Мы видим, что данные были найдены в последних двух случаях.По демонстрационному эффекту следует понимать это предложение.Транзакции не поддерживаются, если есть текущая транзакция, эта транзакция будет приостановлена ​​и не будет выполняться в транзакционном режиме..

`PROPAGATION_NEVER`

У вызывающей стороны есть транзакция, вызывающая сторона вызовет исключение

Есть ли у вызывающего абонента транзакция Есть ли у вызывающего объекта исключение Есть ли у вызываемого объекта исключение
без без имеют
имеют имеют без

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

`PROPAGATION_NESTED`

Согласно этому свойству распространения транзакция вызываемого объекта является подмножеством транзакции вызывающего объекта.

Давайте сосредоточимся наNESTEDСвойства свойств распространения

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

Что касается отношения вложенных транзакций, мы можем продемонстрировать это на следующих трех примерах.

Есть ли у вызывающего абонента транзакция Есть ли у вызывающего объекта исключение Есть ли у вызываемого объекта исключение
без без имеют
имеют имеют без
имеют без имеют
  • Первый случай: если данные не могут быть найдены, это означает, что вызываемый объект начнет новую транзакцию, когда у вызывающего объекта нет транзакции.
  • Второй случай: если данные не найдены, это означает, что внешняя транзакция может повлиять на внутреннюю транзакцию.
  • Третий случай: если данные найдены, значит, внутренняя транзакция не влияет на внешнюю транзакцию

Далее пишем конкретный код

 1// NESTED传播属性-回滚事务
2@Override
3@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
4public void testNestedHasException() throws RollbackException {
5    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_HAS_EXCEPTION+"')");
6   // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
7    throw new RollbackException();
8}
9
10// NESTED传播属性-不回滚事务
11@Override
12@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
13public void testNestedNoException() throws RollbackException {
14    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_NO_EXCEPTION+"')");
15}

Тогда следующий звонящий тоже будет немного другим

1@Override
2@Transactional()
3public void hasTransactionalNoException() throws RollbackException {
4    // NESTED传播属性 - 调用者有事务,不抛异常  被调用者有异常
5    jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('"+Global.NESTED_HAS_EXCEPTION_TWO+"')");
6    fooService.testNestedHasException();
7}

затем выполните эффект

12019-10-16 18:01:06.387  INFO 17172 --- [           main] c.e.t.t.TransactionApplication           : 第一种情况 0
22019-10-16 18:01:06.389  INFO 17172 --- [           main] c.e.t.t.TransactionApplication           : 第二种情况 0
32019-10-16 18:01:06.390  INFO 17172 --- [           main] c.e.t.t.TransactionApplication           : 第三种情况 1

Видно, что суть вложенных транзакций заключается вВнешний слой влияет на внутренний слой, внутренний слой не влияет на внешний слой. а такжеREQUIRES_NEWявляетсяне влияют друг на друга.

Суммировать

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

Кодовый адрес этой статьи