数据库事务
一、Spring Boot 的事务机制
- 在之前的博文已经写过 Spring 事务方面的,今天的针对 Spring Boot 的事务机制进行学习。
1、Spring Boot 的事务机制的支持
- 无论是使用 JDBC 数据访问技术,还是用 JPA 数据访问技术,Spring Boot 都为我们定义了一个 PlatformTransactionManager 的实现专门的 Bean,如 JDBC 的 DataSourceTransactionManager,如 JPA 的 JpaTransactionManager,并且我们进行了自动配置。
二、示例项目
- 同样用一个简单的项目测试特定错误引起事务回滚和特定错误下事务不回滚。
1、新建项目
- 用 IDEA 的项目创建向导创建一个带初始依赖的 Spring Boot 项目,初始依赖选择 Spring Web 和 Spring Data JPA。
2、添加依赖
- 因为目标数据库仍旧是 Oracle 数据库,因此还是需要 ojdbc6.jar 这个包,在 POM 文件添加如下的坐标:
<dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.2.0</version> </dependency>
- 项目结构信息如下:
<groupId>com.pyc</groupId> <artifactId>transaction</artifactId> <version>0.0.1-SNAPSHOT</version> <name>transaction</name> <packaging>jar</packaging> <description>Demo project for Spring Boot</description>
- Spring Boot 的版本为 1.3.0.M2
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.0.M2</version> <relativePath/> <!-- lookup parent from repository --> </parent>
- 整个 POM 文件如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.0.M2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.pyc</groupId> <artifactId>transaction</artifactId> <version>0.0.1-SNAPSHOT</version> <name>transaction</name> <packaging>jar</packaging> <description>Demo project for Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3、application.properties 配置
- 在 application.properties 中对数据源和数据驱动等进行配置
# 数据驱动 database driver spring.datasource.driverClassName=oracle.jdbc.OracleDriver # 数据源 data source spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe # 数据库登录用户 database account and also name of the goal database spring.datasource.username=boot # 相应用户的登录密码 password of the corresponding database account spring.datasource.password=boot # auto maintain data sheet of goal database spring.jpa.hibernate.ddl-auto=update # show sql in console window spring.jpa.show-sql=true spring.jackson.serialization.indent-output=true debug=true logging.file=log.log logging.level.org.springframework.web=DEBUG
4、Entity class
- 准备一个实体类,当然这里因为目标数据源一样,所以实体类仍旧是 Person,具体代码见上一篇。
5、Entity Repository
- 准备实体类对应的 Repository,这在 JPA 数据访问技术中是必不可少的,在这个项目中无需自定义任何 Repository 方法,只要继承 JpaRepository 的方法就够了,代码如下:
//IntelliJ IDEA //transaction //PersonRepository //2020/2/11 // Author:御承扬 //E-mail:2923616405@qq.com package com.pyc.transaction.dao; import com.pyc.transaction.domain.Person; import org.springframework.data.jpa.repository.JpaRepository; public interface PersonRepository extends JpaRepository<Person, Long> { }
6、Business Service
- 要以事务的形式操作数据库,就需要相应的业务服务程序。
6.1、Service Interface
- 首先是编写一个服务接口,这里主要就两个方法:一个发生特定异常时事务回滚,另一个是发生特定异常时事务不回滚。
package com.pyc.transaction.service; import com.pyc.transaction.domain.Person; public interface DemoService { public Person savePersonWithRollBack(Person person); public Person savePersonWithoutRollBack(Person person); }
6.2、Service Achieve
- 编写一个继承服务接口的类,并事项接口中的所有方法,代码如下:
package com.pyc.transaction.service.impl; import com.pyc.transaction.dao.PersonRepository; import com.pyc.transaction.domain.Person; import com.pyc.transaction.service.DemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class DemoServiceImpl implements DemoService { @Autowired PersonRepository personRepository; // 使用 @Transaction 注解的 rollback 属性,指定特定异常时,数据回滚 // comment @Transaction and use property rollback to assign transaction rollback // when happen illegal argument exception @Transactional(rollbackFor = {IllegalArgumentException.class}) public Person savePersonWithRollBack(Person person){ Person p = personRepository.save(person); if(person.getName().equals("林秋延")){ throw new IllegalArgumentException("林秋延已经存在,数据回滚"); } return p; } // comment @Transaction and use property rollback to assign transaction no rollback // when happen illegal argument exception @Transactional(noRollbackFor = {IllegalArgumentException.class}) public Person savePersonWithoutRollBack(Person person){ Person p = personRepository.save(person); if(person.getName().equals("pyc")){ throw new IllegalArgumentException("pyc 虽然已经存在,数据不会回滚"); } return p; } }
- 在类体上用 @Service 注解,向 Spring 表面这是一个服务类,项目启动时就会自动扫描当前类体。
- 类体中的两个方法都用代码 throw 一个异常,这种方式叫做硬编码手动触发异常。
7、Controller
- 为了前台能够调用刚刚 Service 中的相关方法,需要用一个 Controller 编辑相应的 URL 暴露到前台中去,代码如下:
package com.pyc.transaction.web; import com.pyc.transaction.domain.Person; import com.pyc.transaction.service.DemoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class WebController { @Autowired DemoService demoService; // 测试回滚 // try rollback transaction @RequestMapping("/rollback") public Person rollback(Person person){ return demoService.savePersonWithRollBack(person); } // trying no rollback transaction @RequestMapping("/norollback") public Person noRollback(Person person){ return demoService.savePersonWithoutRollBack(person); } }
- 调用特定异常发生时事务回滚的方法的 URL 为 ”/rollback“。
- 入口类无需修改。项目结构树如下:
8、运行测试
- 运行项目,首先测试事务回滚的,打开浏览器,在地址栏输入:localhost:8080/rollback?name=林秋延&age=21
- 服务器将返回有异常发生并显示异常信息:
- 而数据库中也确实没有任何记录增加:
- 测试事务不回滚,访问 URL localhost:8080/norollback?name=pyc&address=陆丰&age=22
- 服务器仍然返回有异常
- 但是查看数据库,增加了一条数据记录