SpringBoot 注解事务声明式事务的方式

下面是详细讲解SpringBoot注解事务声明式事务的方式的完整攻略。

什么是事务

在数据库的操作中,当多条SQL语句同时执行时,为了保证数据的一致性和完整性,我们需要让这些SQL语句在一个整体中完成,有且只有所有语句都执行成功时才提交到数据库里,而任一条语句执行失败时则所有语句都不会被提交。这个整体操作就是“事务”。

Java中,事务可以通过编程式、声明式两种方式来实现。其中,声明式事务更加方便、简洁,不需要像编程式事务那样手动管理事务,而是通过注解自动实现。

SpringBoot注解事务的声明方式

在SpringBoot中,声明式事务的方式有两种,一种是基于AOP的方式,即通过在方法上添加@Transactional注解来开启事务;另一种是基于AspectJ的方式,即通过XML配置或@AspectJ注解来开启事务。这里我们主要讲解基于AOP的方式。

基于AOP的声明式事务

1.配置数据源

首先通过application.properties将数据源配置好:

spring.datasource.url=jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

2.添加依赖

在pom.xml中添加SpringBoot的jdbc、MySQL连接驱动、事务等依赖。

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>

3.添加注解

在需要使用事务的方法上添加@Transactional注解。这里我们用一个简单的例子来说明:

@Service
public class UserServiceImpl implements UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Override
  @Transactional
  public void add(User user) {
    jdbcTemplate.update("insert into user(name, age) values(?, ?)", user.getName(), user.getAge());
  }

  @Override
  public User find(int id) {
    return jdbcTemplate.queryForObject("select * from user where id=?", new BeanPropertyRowMapper<>(User.class), id);
  }
}

在add方法上添加了@Transactional注解,表示此方法需要开启事务。如果在add方法执行过程中抛出异常,事务将会自动回滚。

4.测试

可以通过JUnit测试来验证事务是否正常工作。

@SpringBootTest
class UserServiceTest {

  @Autowired
  private UserService userService;

  @Test
  public void test_add() {
    User user = new User("test", 18);
    userService.add(user);
    User result = userService.find(user.getId());
    assertNotNull(result);
    assertEquals(user.getId(), result.getId());
  }

  @Test
  public void test_exception() {
    User user = new User("test", 18);
    userService.add(user);
    assertThrows(DataIntegrityViolationException.class, () -> {
      userService.add(user);
    });
  }
}

test_add方法测试了正常情况下的插入操作,test_exception方法测试了在插入数据时出现异常的情况,如果事务正常工作则会回滚整个事务。

通过以上步骤,我们就可以轻松地使用基于AOP的声明式事务了。

基于AspectJ的声明式事务

基于AOP的方式声明事务需要使用@Transactional注解,而基于AspectJ的方式需要通过XML配置或@AspectJ注解来实现。

这里我们以@AspectJ注解的方式为例,步骤如下:

1.添加依赖

在pom.xml中添加SpringBoot和AspectJ的依赖。

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
</dependency>

2.添加注解

在SpringBoot的启动类上添加@EnableAspectJAutoProxy注解,表示启用AspectJ自动代理。

@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

然后在需要使用事务的方法上添加@Transational注解,并在配置类上添加@Aspect注解。

@Service
public class UserServiceImpl implements UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Override
  public User find(int id) {
    return jdbcTemplate.queryForObject("select * from user where id=?", new BeanPropertyRowMapper<>(User.class), id);
  }

  @Override
  @Transactional
  public void add(User user) {
    jdbcTemplate.update("insert into user(name, age) values(?, ?)", user.getName(), user.getAge());
  }
}
@Aspect
@Configuration
public class TransactionAspect {

  @Autowired
  private DataSource dataSource;

  @Bean
  public PlatformTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dataSource);
  }

  @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
  public void transactionPointcut() {}

  @Around("transactionPointcut()")
  public void transactional(ProceedingJoinPoint joinPoint) {
    TransactionStatus transactionStatus = null;
    try {
      DefaultTransactionDefinition def = new DefaultTransactionDefinition();
      def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
      transactionStatus = transactionManager().getTransaction(def);
      joinPoint.proceed();
      transactionManager().commit(transactionStatus);
    } catch (Throwable e) {
      if (transactionStatus != null) {
        transactionManager().rollback(transactionStatus);
      }
      throw new RuntimeException(e);
    }
  }
}

其中,@Pointcut注解表示切入点,即需要被拦截的方法,这里使用了@Transactional注解来表示切入点;@Around注解表示环绕通知,即在方法执行前后执行的逻辑,这里使用该注解来实现事务的开启、提交和回滚操作。

示例

为了更好地理解使用注解声明式事务的方式,这里我们用一个简单的示例来演示。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

  private static final long serialVersionUID = 1L;

  private Integer id;

  private String name;

  private Integer age;
}
public interface UserService {

  void add(User user);

  User find(int id);
}
@Service
public class UserServiceImpl implements UserService {

  @Autowired
  private UserDao userDao;

  @Override
  @Transactional
  public void add(User user) {
    userDao.add(user);
  }

  @Override
  public User find(int id) {
    return userDao.find(id);
  }
}
@Repository
public class UserDao {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public User find(int id) {
    return jdbcTemplate.queryForObject("select * from user where id=?", new BeanPropertyRowMapper<>(User.class), id);
  }

  public void add(User user) {
    jdbcTemplate.update("insert into user(name, age) values(?, ?)", user.getName(), user.getAge());
  }
}

在UserService的实现类UserServiceImpl中,我们在add方法上添加了@Transactional注解,表示需要开启事务;在UserDao中直接使用了JdbcTemplate来执行SQL语句。

@SpringBootTest
class UserServiceTest {

  @Autowired
  private UserService userService;

  @Test
  public void test_add() {
    User user = new User(null, "test", 18);
    userService.add(user);
    User result = userService.find(user.getId());
    assertNotNull(result);
    assertEquals(user.getName(), result.getName());
  }

  @Test
  public void test_exception() {
    User user = new User(null, "test", 18);
    userService.add(user);
    assertThrows(DataIntegrityViolationException.class, () -> {
      userService.add(user);
    });
  }
}

在测试用例中,我们测试了正常插入数据和插入数据出现异常时事务回滚的情况。

总结

通过上述内容,我们可以总结出使用SpringBoot注解事务声明式事务的方式的步骤:

  1. 配置数据源。
  2. 添加依赖。
  3. 在需要使用事务的方法上添加@Transactional注解,或在切点方法上添加该注解。
  4. 测试所有情况以确定事务是否正确工作。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot 注解事务声明式事务的方式 - Python技术站

(0)
上一篇 2023年5月15日
下一篇 2023年5月15日

相关文章

  • 详解Java 缺失的特性扩展方法

    详解Java 缺失的特性扩展方法 Java 是一门非常成熟的编程语言,但它也存在一些不足之处。其中一个重要的问题就是缺乏特性扩展方法,这个问题一直以来都困扰着 Java 开发者。特性扩展方法是指在不改变类定义的情况下,在其上增加新的方法。这种机制在其他语言中已经被广泛应用了,例如 C#、Swift、Kotlin 等,它们都有内置的特性扩展方法。 在本文中,我…

    Java 2023年5月26日
    00
  • SpringMVC上传图片与访问

    SpringMVC上传图片与访问攻略 SpringMVC是一个非常流行的Java Web框架,它提供了很多方便的功能,包括文件上传和图片访问。在本文中,我们将详细讲解如何在SpringMVC中上传图片并访问它们。 上传图片 在Web应用程序中,文件上传是一个非常常见的需求。SpringMVC提供了很多方便的方式来处理文件上传,包括使用MultipartFil…

    Java 2023年5月18日
    00
  • 使用SpringBoot自定义starter详解

    使用SpringBoot自定义starter详解 在SpringBoot中,我们可以使用自定义starter来封装和共享常用的依赖和配置,以简化项目的开发和维护。以下是一个完整的使用SpringBoot自定义starter的攻略: 1. 确定需求和功能 在进行自定义starter之前,我们需要明确项目的需求和功能。在这个阶段,我们可以使用用户故事、用例图、流…

    Java 2023年5月15日
    00
  • java线程池参数位置导致的夺命故障宿主机打不开

    线程池是一种常见的并发处理机制,它可以有效地管理线程的生命周期,避免频繁创建和销毁线程而导致系统开销过大的问题。不过,在进行线程池的使用时,需要设置相应的参数,否则可能会导致不可预料的问题。 下面是针对“java线程池参数位置导致的夺命故障宿主机打不开”的攻略,具体内容如下: 1. 背景介绍 在使用线程池时,常见的参数包括线程池大小、任务队列大小、线程空闲时…

    Java 2023年5月27日
    00
  • java并发编程中ReentrantLock可重入读写锁

    ReentrantLock是Java并发编程中一种可重入的读写锁,它比Synchronized更加灵活,能够满足不同的场景需求。下面我们来详细讲解如何使用ReentrantLock读写锁。 1. ReentrantLock的基本使用 1.1 创建ReentrantLock对象 import java.util.concurrent.locks.Reentra…

    Java 2023年5月26日
    00
  • Java8 Lambda表达式详解及实例

    Java8 Lambda表达式详解及实例 什么是Lambda表达式 Lambda表达式是Java8中引入的一个新特性,是一种轻量级的匿名函数,用来替代过往繁琐的匿名内部类编写方式。Lambda表达式可以被赋值到一个变量中,或者传递到一个方法中作为参数,像对象一样使用。Lambda表达式的语法简洁、优雅,让Java8代码的可读性和可维护性更加强大。 Lambd…

    Java 2023年5月26日
    00
  • 如何在JDK 9中更简洁使用 try-with-resources 语句

    在 JDK 9 中,你可以更加简洁地使用 try-with-resources 语句。下面,我们来一步步讲解具体的步骤。 1. JDK 9 try-with-resources 简化语法 在 JDK 9 中,简化了 try-with-resources 语法。以前,你需要在 try 语句中申明一个资源,像这样: try (SomeResource resou…

    Java 2023年5月27日
    00
  • java.lang.Void 与 void的比较及使用方法介绍

    Java中的Void和void Java中的Void和void是两个容易混淆的概念,但实际上它们是有着明显的区别的。 Void 先来看看Void。Void是Java中的一个类,不同于基本类型(如int和double),它不能进行实例化。Void类只有一个实例,即常量Void.TYPE,表示的是空类型。 我们可以用Void类来定义一个返回值类型为void的方法…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部