微信公衆號:一個優秀的廢人
若有問題或建議,請後臺留言,我會盡力解決你的問題。
如題,今天介紹 SpringBoot 的 聲明式事務。前端
全部的數據訪問技術都有事務處理機制,這些技術提供了 API 用於開啓事務、提交事務來完成數據操做,或者在發生錯誤時回滾數據。java
而 Spring 的事務機制是用統一的機制來處理不一樣數據訪問技術的事務處理,Spring 的事務機制提供了一個 PlatformTransactionManager 接口,不一樣的數據訪問技術的事務使用不一樣的接口實現,以下表:mysql
數據訪問技術 | 實現 |
---|---|
JDBC | DataSourceTransactionManager |
JPA | JPATransactionManager |
Hibernate | HibernateTransactionManager |
JDO | JdoTransactionManager |
分佈式事務 | JtaTransactionManager |
Spring 支持聲明式事務,即便用註解來選擇須要使用事務的方法,他使用 @Transactional 註解在方法上代表該方法須要事務支持。被註解的方法在被調用時,Spring 開啓一個新的事務,當方法無異常運行結束後,Spring 會提交這個事務。如:git
@Transactional public void saveStudent(Student student){ // 數據庫操做 }
注意,@Transactional 註解來自於 org.springframework.transcation.annotation 包,而不是 javax.transaction。github
Spring 提供一個 @EnableTranscationManagement 註解在配置類上來開啓聲明式事務的支持。使用了 @EnableTranscationManagement 後,Spring 容器會自動掃描註解 @Transactional 的方法與類。@EnableTranscationManagement 的使用方式以下:web
@Configuration @EnableTranscationManagement public class AppConfig{ }
@Transactional 有以下表所示的屬性來定製事務行爲。算法
屬性 | 含義 |
---|---|
propagation | 事務傳播行爲 |
isolation | 事務隔離級別 |
readOnly | 事務的讀寫性,boolean型 |
timeout | 超時時間,int型,以秒爲單位。 |
rollbackFor | 一組異常類,遇到時回滾。(rollbackFor={SQLException.class}) |
rollbackForCalssName | 一組異常類名,遇到回滾,類型爲 string[] |
noRollbackFor | 一組異常類,遇到不回滾 |
norollbackForCalssName | 一組異常類名,遇到時不回滾。 |
@Transactional 不只能夠註解在方法上,還能夠註解在類上。註解在類上時意味着此類的全部 public 方法都是開啓事務的。若是類級別和方法級別同時使用了 @Transactional 註解,則使用在類級別的註解會重載方法級別的註解。spring
在使用 JDBC 做爲數據訪問技術時,配置定義以下:sql
@Bean @ConditionalOnMissingBean @ConditionalOnBean(DataSource.class) public PlatformTransactionManager transactionManager(){ return new DataSourceTransactionManager(this.dataSource) }
在使用 JPA 做爲數據訪問技術時,配置定義以下:數據庫
@Bean @ConditionalOnMissingBean(PlatformTransactionManager.class) public PlatformTransactionManager transactionManager(){ return new JpaTransactionManager() }
SpringBoot 專門用於配置事務的類爲 org.springframework.boot.autoconfigure.transcation.TransactionAutoConfiguration,此配置類依賴於 JpaBaseConfiguration 和 DataSourceTransactionManagerAutoConfiguration。
而在 DataSourceTransactionManagerAutoConfiguration 配置裏還開啓了對聲明式事務的支持,代碼以下:
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class) @Configuration @EnableTransactionManagement protected static class TransactionManagementConfiguration{ }
因此在 SpringBoot 中,無須顯式開啓使用 @EnableTransactionManagement 註解。
演示如何使用 Transactional 使用異常致使數據回滾與使用異常致使數據不回滾。
SpringBoot 2.1.3
JDK 1.8
IDEA
<?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 http://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>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.nasus</groupId> <artifactId>transaction</artifactId> <version>0.0.1-SNAPSHOT</version> <name>transaction</name> <description>transaction Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- JPA 相關 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- web 啓動類 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- mysql 鏈接類 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- lombok 插件,簡化實體代碼 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> </dependency> <!-- 單元測試 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
代碼註釋很清楚,沒啥好說的。
spring: # \u6570\u636E\u5E93\u76F8\u5173 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=true username: root password: 123456 # jpa \u76F8\u5173 jpa: hibernate: ddl-auto: update # ddl-auto:\u8BBE\u4E3A create \u8868\u793A\u6BCF\u6B21\u90FD\u91CD\u65B0\u5EFA\u8868 show-sql: true
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @Entity @AllArgsConstructor @NoArgsConstructor public class Student { @Id @GeneratedValue private Integer id; private String name; private Integer age; }
import com.nasus.transaction.domain.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface StudentRepository extends JpaRepository<Student, Integer> { }
import com.nasus.transaction.domain.Student; public interface StudentService { Student saveStudentWithRollBack(Student student); Student saveStudentWithoutRollBack(Student student); }
實現類:
import com.nasus.transaction.domain.Student; import com.nasus.transaction.repository.StudentRepository; import com.nasus.transaction.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class StudentServiceImpl implements StudentService { @Autowired // 直接注入 StudentRepository 的 bean private StudentRepository studentRepository; // 使用 @Transactional 註解的 rollbackFor 屬性,指定特定異常時,觸發回滾 @Transactional(rollbackFor = {IllegalArgumentException.class}) @Override public Student saveStudentWithRollBack(Student student) { Student s = studentRepository.save(student); if ("高斯林".equals(s.getName())){ //硬編碼,手動觸發異常 throw new IllegalArgumentException("高斯林已存在,數據將回滾"); } return s; } // 使用 @Transactional 註解的 noRollbackFor 屬性,指定特定異常時,不觸發回滾 @Transactional(noRollbackFor = {IllegalArgumentException.class}) @Override public Student saveStudentWithoutRollBack(Student student) { Student s = studentRepository.save(student); if ("高斯林".equals(s.getName())){ throw new IllegalArgumentException("高斯林已存在,數據將不會回滾"); } return s; } }
代碼註釋一樣很清楚,沒啥好說的。
import com.nasus.transaction.domain.Student; import com.nasus.transaction.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/student") public class StudentController { // 注入 studentservice 的 bean @Autowired private StudentService studentService; // 測試回滾狀況 @PostMapping("/withRollBack") public Student saveStudentWithRollBack(@RequestBody Student student){ return studentService.saveStudentWithRollBack(student); } // 測試不回滾狀況 @PostMapping("/withOutRollBack") public Student saveStudentWithoutRollBack(@RequestBody Student student){ return studentService.saveStudentWithoutRollBack(student); } }
爲了更清楚地理解回滾,以 debug (調試模式) 啓動程序。並在 StudentServiceImpl 的 saveStudentWithRollBack 方法上打上斷點。
測試前數據庫結果:
debug 模式下可見數據已保存,且得到 id 爲 1。:
繼續執行拋出異常 IllegalArgumentException,將致使數據回滾:
測試後數據庫結果:並沒有新增數據,回滾成功。
測試前數據庫結果:
遇到 IllegalArgumentException 異常數據不會回滾:
測試後數據庫結果:新增數據,數據不回滾。
https://github.com/turoDog/Demo/tree/master/springboot_transaction_demo
以上爲 SpringBoot 聲明式事務的教程。最後,對 Python 、Java 感興趣請長按二維碼關注一波,我會努力帶給大家價值,若是以爲本文對你哪怕有一丁點幫助,請幫忙點好看,讓更多人知道。
另外,關注以後在發送 1024 可領取免費學習資料。資料內容詳情請看這篇舊文:Python、C++、Java、Linux、Go、前端、算法資料分享