Практическое руководство по транзакциям SpringBoot

Spring Boot

Источник: https://www.cnblogs.com/xuwujing/p/11184162.html.

Управляемое чтение

Впервые опубликовано на паблике: JAVA вор, замечательные статьи, жду вашего внимания! Публичный аккаунт для обмена учебными ресурсами по Java, практическим опытом и техническими статьями!

предисловие

Эта статья в основном знакомит с учебным пособием по использованию транзакционных транзакций SpringBoot.

SpringBoot Transaction

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

Transaction

управление бизнесом

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

  • Программное управление транзакциями: Программное управление транзакциями используетTransactionTemplateили напрямую использовать базовыйPlatformTransactionManager. Для программного управления транзакциями Spring рекомендует использоватьTransactionTemplate.
  • Декларативное управление транзакциями: построено на основе АОП. Его суть заключается в том, чтобы перехватить метод до и после, а затем создать или присоединиться к транзакции до запуска целевого метода и зафиксировать или откатить транзакцию в соответствии со статусом выполнения после выполнения целевого метода. Декларативное управление транзакциями не требует кода взлома, через@TransactionalВы можете выполнять операции с транзакциями, которые быстрее и проще и рекомендуются.

Метод фиксации транзакции

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

Для нормального управления транзакциями в транзакции находится группа связанных операций, поэтому режим автофиксации базы данных должен быть выключен. Однако нам не нужно об этом беспокоиться, Spring установит для функции автоматической фиксации базового соединения значение false. То есть при использовании Spring для управления транзакциями Spring установит, следует ли автоматически отправлять значение false, что эквивалентно JDBC.connection.setAutoCommit(false);, представить после выполнения,connection.commit();.

Уровень изоляции транзакции

Уровень изоляции относится к степени изоляции между несколькими одновременными транзакциями. В интерфейсе TransactionDefinition определены пять констант, представляющих уровни изоляции:

  • TransactionDefinition.ISOLATION_DEFAULT: это значение по умолчанию, означающее использование уровня изоляции базовой базы данных по умолчанию. Для большинства баз данных это значение обычно TransactionDefinition.ISOLATION_READ_COMMITTED.
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED: этот уровень изоляции указывает, что транзакция может считывать данные, измененные другой транзакцией, но еще не зафиксированные. Этот уровень не предотвращает грязные чтения, неповторяемые чтения и фантомные чтения, поэтому этот уровень изоляции используется редко. Например, PostgreSQL на самом деле не имеет этого уровня.
  • TransactionDefinition.ISOLATION_READ_COMMITTED: этот уровень изоляции указывает, что транзакция может считывать только те данные, которые были зафиксированы другой транзакцией. Этот уровень предотвращает грязное чтение и является рекомендуемым значением в большинстве случаев.
  • TransactionDefinition.ISOLATION_REPEATABLE_READ: этот уровень изоляции указывает, что транзакция может неоднократно выполнять запрос несколько раз в течение всего процесса, и каждый раз возвращаются одни и те же записи. Этот уровень предотвращает грязные чтения и неповторяющиеся чтения.
  • TransactionDefinition.ISOLATION_SERIALIZABLE: Все транзакции выполняются одна за другой, так что абсолютно исключена возможность интерференции между транзакциями, то есть этот уровень может предотвращать грязные чтения, неповторяемые чтения и фантомные чтения. Но это серьезно повлияет на производительность программы. Обычно этот уровень также не используется.

Поведение распространения транзакции

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

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

Правила отката транзакции

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

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

Общая конфигурация транзакций

  • readOnly: Это свойство используется, чтобы установить, является ли текущая транзакция транзакцией только для чтения. Если установлено значение true, оно означает, что оно доступно только для чтения, а false означает, что транзакция может быть прочитана и записана. Значение по умолчанию — false. Например:

@Транзакционный(только для чтения=истина);

  • rollbackFor: это свойство используется для установки массива классов исключений, который необходимо откатить.Когда в методе выдается исключение в указанном массиве исключений, транзакция будет откатлена. Например:

Укажите один класс исключений: @Transactional(rollbackFor=RuntimeException.class) Укажите несколько классов исключений: @Transactional(rollbackFor={RuntimeException.class, Exception.class});

  • rollbackForClassName: это свойство используется для установки массива имен классов исключений, которые необходимо откатить.Когда в методе выдается исключение в указанном массиве имен исключений, транзакция будет откатана. Например:

Укажите одно имя класса исключений @Transactional(rollbackForClassName="RuntimeException") Укажите несколько имен классов исключений: @Transactional(rollbackForClassName={"RuntimeException","Exception"}).

  • noRollbackFor: это свойство используется для установки массива классов исключений, который не нужно откатывать.Когда в методе возникает исключение в указанном массиве исключений, транзакция не откатывается. Например:

Укажите один класс исключений: @Transactional(noRollbackFor=RuntimeException.class) Укажите несколько классов исключений: @Transactional(noRollbackFor={RuntimeException.class, Exception.class}).

  • noRollbackForClassName: это свойство используется для установки массива имен классов исключений, которые не нужно откатывать.Когда в методе создается исключение в указанном массиве имен исключений, транзакция не будет откатываться. Например:

Укажите одно имя класса исключений: @Transactional(noRollbackForClassName="RuntimeException") Укажите несколько имен классов исключений: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}).

  • propagation: это свойство используется для установки поведения распространения транзакции. Например:

@Транзакционный(распространение=Распространение.НЕ ПОДДЕРЖИВАЕТСЯ,только для чтения=истина).

  • isolation: Это свойство используется для установки уровня изоляции транзакций базовой базы данных. Уровень изоляции транзакций используется для работы с параллельными ситуациями с несколькими транзакциями. Обычно можно использовать уровень изоляции базы данных по умолчанию, и в принципе нет необходимости установить его.
  • timeout: это свойство используется для установки тайм-аута в секундах для транзакции.Значение по умолчанию равно -1, что означает отсутствие тайм-аута.

Вопросы, требующие внимания

  • Необходимо решить, использовать ли вещи по фактическим потребностям, лучше всего обдумать это перед кодированием, иначе в дальнейшем будет сложно поддерживать;
  • Если вы что-то используете, обязательно проверяйте их, потому что во многих случаях считается, что что-то работает, но на самом деле это может не работать!
  • предмет@TransactionalИспользование должно быть размещено в публичном (public) методе класса, следует отметить, что вprotected,privateМетод аннотирован @Transactional, он не сообщит об ошибке (IDEA подскажет), но транзакция недействительна.
  • предмет@TransactionalЭто не повлияет на подметоды в этом методе! Это то, что вы объявляете в публичном методе A@Transactional, но в методе A есть подметоды B и C, в которых метод B выполняет операции с данными, но исключение обрабатывается самим B, поэтому ничего не произойдет! И наоборот, вещи, объявленные методом B, являются @Transactional, но если публичный метод A ничего не объявляет, он не вступит в силу! Если вы хотите, чтобы что-то вступило в силу, вам нужно передать управление транзакциями подметода вызывающему методу и использовать его в подметоде.rollbackForВ аннотации указывается исключение, которое необходимо откатить или передать исключение вызывающему методу для обработки. Одним словом, исключения в использовании вещей обрабатываются вызывающей стороной!
  • предмет@TransactionalПод управлением Spring он откатывается при возникновении исключения. Если вы используете catch для захвата обработки, он не вступит в силу.Если вы хотите, чтобы эффект вступил в силу, вы можете вручную откатиться или создать исключение в catch, например, throw new RuntimeException();.

подготовка к разработке

Требования к окружающей среде

JDK: 1.8 SpringBoot: 1.5.17.РЕЛИЗ

Прежде всего, связанные зависимости Maven:

Файл pom.xml выглядит следующим образом:

<properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <java.version>1.8</java.version>
  <maven.compiler.source>1.8</maven.compiler.source>
  <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.17.RELEASE</version>
  <relativePath /> 
 </parent>
  <dependencies>
    <!-- Spring Boot Web 依赖 核心 -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
   <!-- Spring Boot Test 依赖 -->
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
   <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.2.0</version>
   </dependency>
   <!-- MySQL 连接驱动依赖 -->
   <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
   </dependency>
   <!-- Druid 数据连接池依赖 -->
   <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.8</version>
   </dependency>
  </dependencies>

Конфигурация файла application.properties:

banner.charset=UTF-8
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.messages.encoding=UTF-8
spring.application.name=springboot-transactional
server.port=8182

spring.datasource.url=jdbc:mysql://localhost:3306/springBoot?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,wall,log4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

logging.level.com.pancm.dao=debug

написание кода

SpringBoot использует вещиTransactionalКогда вы хотите добавить его в основной метод@EnableTransactionManagementАннотируйте объявление объекта разработки, добавьте его в общедоступный метод используемого сервисного уровня.@Transactional(весна) аннотация.

Используйте пример один

Итак, сначала давайте посмотрим на@TransactionalЧтобы использовать эту аннотацию, вам нужно только добавить аннотацию к общедоступному методу, который вам нужно добавить. Но если вы используете его таким образом, вам нужно сгенерировать исключение и контролировать его с помощью spring.

Пример кода:


 @Transactional
 public boolean test1(User user) throws Exception {
  long id = user.getId();
  System.out.println("查询的数据1:" + udao.findById(id));
  // 新增两次,会出现主键ID冲突,看是否可以回滚该条数据
  udao.insert(user);
  System.out.println("查询的数据2:" + udao.findById(id));
  udao.insert(user);
  return false;
 }

Используйте второй пример

если мы используем вещи@TransactionalКогда мы хотим сами обработать исключение, мы можем откатить его вручную. добавить в уловTransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); метод ручного отката. Однако следует отметить, что исключение необходимо вручную откатить в первый раз, то есть до того, как исключение будет сгенерировано!

Пример кода:


 @Transactional
 public boolean test2(User user) {

  long id = user.getId();
  try {
   System.out.println("查询的数据1:" + udao.findById(id));
   // 新增两次,会出现主键ID冲突,看是否可以回滚该条数据
   udao.insert(user);
   System.out.println("查询的数据2:" + udao.findById(id));
   udao.insert(user);
  } catch (Exception e) {
   System.out.println("发生异常,进行手动回滚!");
   // 手动回滚事物
   TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
   e.printStackTrace();
  }
  return false;
 }

Используйте третий пример

если мы используем вещи@TransactionalКогда для выполнения операций с базой данных вызываются другие подметоды, но если мы хотим, чтобы что-то работало, мы можем использоватьrollbackForАннотируйте или сгенерируйте исключение подметода, который будет обрабатываться вызывающим методом, но здесь следует отметить, что подметод также должен быть общедоступным методом!

Пример кода:


@Transactional
 public boolean test3(User user) {

  /*
   * 子方法出现异常进行回滚
   */
  try {
   System.out.println("查询的数据1:" + udao.findById(user.getId()));
   deal1(user);
   deal2(user);
   deal3(user);
  } catch (Exception e) {
   System.out.println("发生异常,进行手动回滚!");
   // 手动回滚事物
   TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
   e.printStackTrace();
  } 
  return false;

 }

 public void deal1(User user) throws SQLException {
  udao.insert(user);
  System.out.println("查询的数据2:" + udao.findById(user.getId()));
 }

 public void deal2(User user)  throws SQLException{
  if(user.getAge()<20){
   //SQL异常
   udao.insert(user);
  }else{
   user.setAge(21);
   udao.update(user);
   System.out.println("查询的数据3:" + udao.findById(user.getId()));
  }
 }


 @Transactional(rollbackFor = SQLException.class)
 public void deal3(User user)  {
  if(user.getAge()>20){
   //SQL异常
   udao.insert(user);
  }

 }

Используйте пример четыре

если мы не хотим использовать вещи@TransactionalАннотация, если вы хотите контролировать вещи самостоятельно (программирование управления вещами) и контролировать определенный раздел кода, чтобы он вступил в силу, но вы не хотите писать так много кода самостоятельно, вы можете использовать SpringbootDataSourceTransactionManagerа такжеTransactionDefinitionЭти два класса используются в комбинации для ручного управления откатом фиксации вещей. Однако при его использовании нужно обращать внимание при откате, следить за тем, чтобы вещи были включены, но не отправлены.Если откатиться, когда не включено или не отправлено, в улове произойдет исключение!

Пример кода:


 @Autowired
 private DataSourceTransactionManager dataSourceTransactionManager;
 @Autowired
 private TransactionDefinition transactionDefinition;

    public boolean test4(User user) {
  /*
   * 手动进行事物控制
   */
  TransactionStatus transactionStatus=null;
  boolean isCommit = false;
  try {
   transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
   System.out.println("查询的数据1:" + udao.findById(user.getId()));
   // 进行新增/修改
   udao.insert(user);
   System.out.println("查询的数据2:" + udao.findById(user.getId()));
   if(user.getAge()<20) {
    user.setAge(user.getAge()+2);
    udao.update(user);
    System.out.println("查询的数据3:" + udao.findById(user.getId()));
   }else {
    throw new Exception("模拟一个异常!");
   }
   //手动提交
   dataSourceTransactionManager.commit(transactionStatus);
   isCommit= true;
   System.out.println("手动提交事物成功!");
   throw new Exception("模拟第二个异常!");

  } catch (Exception e) {
   //如果未提交就进行回滚
   if(!isCommit){
    System.out.println("发生异常,进行手动回滚!");
    //手动回滚事物
    dataSourceTransactionManager.rollback(transactionStatus);
   }
   e.printStackTrace();
  }
  return false;
 }

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

 Object savePoint =null;
 try{
 //设置回滚点
 savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
 }catch(Exception e){
  //出现异常回滚到savePoint。
  TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
 }

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

Первый — это класс сущности:

класс сущности

Еще одна универсальная пользовательская таблица


 public class User {
  
   private Long id;
 
   private String name;
   
   private Integer age;
   
  //getter 和 setter 略
  
 }

Уровень управления контроллером

Затем есть контрольный слой.В контрольном слое я сделал последний запрос, чтобы проверить, успешно ли все подействовало!

Код уровня управления выглядит следующим образом:


 @RestController
 @RequestMapping(value = "/api/user")
 public class UserRestController {
 
  @Autowired
     private UserService userService;
  
  @Autowired
  private UserDao userDao;
  
 
  @PostMapping("/test1")
     public boolean test1(@RequestBody User user) {
      System.out.println("请求参数:" + user);
   try {
    userService.test1(user);
   } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   System.out.println("最后查询的数据:" + userDao.findById(user.getId()));
         return true;
     }
     
  @PostMapping("/test2")
     public boolean test2(@RequestBody User user) { 
      System.out.println("请求参数:" + user);
   userService.test2(user);
   System.out.println("最后查询的数据:" + userDao.findById(user.getId()));
         return true;
     }
    
  @PostMapping("/test3")
     public boolean test3(@RequestBody User user) { 
      System.out.println("请求参数:" + user);
   userService.test3(user);
   System.out.println("最后查询的数据:" + userDao.findById(user.getId()));
         return true;
     }
  
  @PostMapping("/test4")
     public boolean test4(@RequestBody User user) { 
       System.out.println("请求参数:" + user);
   userService.test4(user);
   System.out.println("最后查询的数据:" + userDao.findById(user.getId()));
         return true;
     }
 }

Запись приложения

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

код показывает, как показано ниже:


 @EnableTransactionManagement
 @SpringBootApplication
 public class TransactionalApp
 {
   
     public static void main( String[] args )
     {
   SpringApplication.run(TransactionalApp.class, args);
   System.out.println("Transactional 程序正在运行...");
  
     }
 }

функциональный тест

После запуска программы мы выполним вышеупомянутые примеры тестов.Примеры тестов здесь соответствуют вышеупомянутым примерам использования, а в некоторых примерах необходимо проверить более двух сторон, чтобы проверить, могут ли они работать! Здесь мы используем Postman для тестирования!

Тестовый пример один

Дважды проверено, первый раз не использовалось@TransactionalОбратите внимание, второе использование!

Первый тест:

закомментируйте@Transactionalаннотация! Сделайте запрос POST, используя

http://localhost:8182/api/user/test1

Параметры кузова:

{"id":1,"name":"xuwujing","age":18}

Данные печати консоли:

Параметры запроса: Пользователь [id=1, name=xuwujing, age=18] Данные запроса 1: ноль Данные запроса 2: Пользователь [id=1, name=xuwujing, age=18] Дублирующаяся запись «1» для ключа «PRIMARY» Последние запрашиваемые данные: пользователь [id=1, name=xuwujing, age=18]

Второй тест:

поднимать@TransactionalПримечания Примечания!

Сделайте запрос POST, используя

http://localhost:8182/api/user/test1

Параметры кузова:

{"id":1,"name":"xuwujing","age":18}

Данные печати консоли:

Параметры запроса: Пользователь [id=1, name=xuwujing, age=18] Данные запроса 1: ноль Данные запроса 2: Пользователь [id=1, name=xuwujing, age=18] Дублирующаяся запись «1» для ключа «PRIMARY» Данные последнего запроса: null

Примечание: Перед вторым тестом данные с id 1, записанные в базу данных для первого теста, были удалены!

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

Второй тестовый пример

Поскольку код в примере 2 почти такой же, как и в примере 1, разница в том, что исключение контролируется нами!

Сделайте запрос POST, используя

http://localhost:8182/api/user/test2

Параметры кузова:

{"id":1,"name":"xuwujing","age":18}

Данные печати консоли:

Параметры запроса: Пользователь [id=1, name=xuwujing, age=18] Данные запроса 1: ноль Данные запроса 2: Пользователь [id=1, name=xuwujing, age=18] Произошло исключение, откатывайтесь вручную! Дублирующаяся запись «1» для ключа «PRIMARY» Данные последнего запроса: null Вы можете видеть, как все работает!

Тестовый пример третий

Поскольку вызов подметода выполняется в примере 3, здесь мы проводим два теста для проверки в соответствии с разными условиями запроса!

Первый тест:

Сделайте запрос POST, используя

http://localhost:8182/api/user/test3

Параметры кузова:

{"id":1,"name":"xuwujing","age":18}

Данные печати консоли:

Параметры запроса: Пользователь [id=1, name=xuwujing, age=18] Данные запроса 1: ноль Данные запроса 2: Пользователь [id=1, name=xuwujing, age=18] Возникает исключение, откатывайтесь вручную! Дублирующаяся запись «1» для ключа «PRIMARY» Данные последнего запроса: null

Второй тест:

Сделайте запрос POST, используя

http://localhost:8182/api/user/test3

Параметры кузова:

{"id":1,"name":"xuwujing","age":21}

Данные печати консоли:

Параметры запроса: Пользователь [id=1, name=xuwujing, age=21] Данные запроса 1: ноль Данные запроса 2: Пользователь [id=1, name=xuwujing, age=21] Данные запроса 3: Пользователь [id=1, name=xuwujing2, age=21] Возникает исключение, откатывайтесь вручную! Дублирующаяся запись «1» для ключа «PRIMARY» Данные последнего запроса: null В соответствии с двумя приведенными выше тестами можно сделать вывод, что использование аннотации rollbackFor или выдача исключения подметода, который будет обрабатываться вызываемым методом, может привести к эффекту!

Тестовый пример четыре

Из-за ручного управления вещами в примере 4 здесь мы проводим два теста для проверки в соответствии с разными условиями запроса!

Первый тест:

Сделайте запрос POST, используя

http://localhost:8182/api/user/test4

Параметры кузова:

{"id":1,"name":"xuwujing","age":18}

Данные печати консоли:

Параметры запроса: Пользователь [id=1, name=xuwujing, age=18] Данные запроса 1: ноль Данные запроса 2: Пользователь [id=1, name=xuwujing, age=18] Данные запроса 3: Пользователь [id=1, name=xuwujing2, age=20] Отправка вещей вручную прошла успешно! Смоделируйте второе исключение! Последние запрашиваемые данные: пользователь [id=1, name=xuwujing, age=20]

Второй тест:

Предварительно удалите данные, чей идентификатор базы данных равен 1!

Сделайте запрос POST, используя

http://localhost:8182/api/user/test4

Параметры кузова:

{"id":1,"name":"xuwujing","age":21}

Данные печати консоли:

Параметры запроса: Пользователь [id=1, name=xuwujing, age=21] Данные запроса 1: ноль Данные запроса 2: Пользователь [id=1, name=xuwujing, age=21] Возникает исключение, откатывайтесь вручную! Смоделируйте исключение! Данные последнего запроса: null

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

Образ тестового примера:

разное

Ссылаться на: https://www.cnblogs.com/yepei/p/4716112.html

адрес проекта

Адрес проекта транзакции транзакции SpringBoot: https://github.com/xuwujing/springBoot-study/tree/master/springboot-transactional

Адрес всей коллекции SpringBoot:

https://github.com/xuwujing/springBoot-study