在Spring Boot中,當我們使用了`spring-boot-starter-jdbc`或`spring-boot-starter-data-jpa`依賴的時候,框 架會自動默認分別注入`DataSourceTransactionManager`或`JpaTransactionManager`。所以我們不需要任何額外 配置就可以用`@Transactional`注解進行事務的使用。
在該樣例工程中,我們引入了spring-data-jpa,并創建了`User`實體以及對User的數據訪 問對象`UserRepository`,在`ApplicationTest`類中實現了使用`UserRepository`進行數據讀寫的單元測試用例.
通過定義User的name屬性長度為5,這樣通過創建時User實體的name屬性超長就可以觸發異常產生。
~~~
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, length = 5)
private String name;
@Column(nullable = false)
private Integer age;
// 省略構造函數、getter和setter
}
~~~
測試用例
~~~
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class ApplicationTests {
@Autowired
private UserRepository userRepository;
@Test
public void test() throws Exception {
// 創建10條記錄
userRepository.save(new User("AAA", 10));
userRepository.save(new User("BBB", 20));
userRepository.save(new User("CCC", 30));
userRepository.save(new User("DDD", 40));
userRepository.save(new User("EEE", 50));
userRepository.save(new User("FFF", 60));
userRepository.save(new User("GGG", 70));
userRepository.save(new User("HHHHHHHHHH", 80));
userRepository.save(new User("III", 90));
userRepository.save(new User("JJJ", 100));
// 省略后續的一些驗證操作
}
}
~~~
執行測試用例
~~~
2016-05-27 10:30:35.948 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1406, SQLState: 22001
2016-05-27 10:30:35.948 ERROR 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data truncation: Data too long for column 'name' at row 1
2016-05-27 10:30:35.951 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 1406, SQLState: HY000
2016-05-27 10:30:35.951 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data too long for column 'name' at row 1
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement
~~~
此時查數據庫中,創建了name從AAA到GGG的記錄,沒有HHHHHHHHHH、III、JJJ的記錄。而若這是一個希望保證完整性操作的情況 下,AAA到GGG的記錄希望能在發生異常的時候被回退,這時候就可以使用事務讓它實現回退,做法非常簡單,我們只需要在test函數上添加 `@Transactional` 注解即可。
~~~
@Test
@Transactional
public void test() throws Exception {
// 省略測試內容
}
~~~
這里主要通過單元測試演示了如何使用 @Transactional 注解來聲明一個函數需要被事務管理,通常我們單元測試為了保證每個測試之間的數據獨立,會使用 `@Rollback` 注解讓每個單元測試都能在結束時回滾。而真正在開發業務邏輯時,我們通常在service層接口中使用 `@Transactional` 來對各個業務邏輯進行事務管理的配置,例如:
~~~
public interface UserService {
@Transactional
User login(String name, String password);
}
~~~
spring Boot 使用事務非常簡單,首先使用注解 `@EnableTransactionManagement` 開啟事務支持后,然后在訪問數據庫的Service方法上添加注解 `@Transactional `便可。
關于事務管理器,不管是JPA還是JDBC等都實現自接口 `PlatformTransactionManager` 如果你添加的是 spring-boot-starter-jdbc 依賴,框架會默認注入 `DataSourceTransactionManager` 實例。如果你添加的是 spring-boot-starter-data-jpa 依賴,框架會默認注入 `JpaTransactionManager` 實例。
你可以在啟動類中添加如下方法,Debug測試,就能知道自動注入的是 `PlatformTransactionManager` 接口的哪個實現類。
~~~
@EnableTransactionManagement // 啟注解事務管理,等同于xml配置方式的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication {
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager){
System.out.println(">>>>>>>>>>" + platformTransactionManager.getClass().getName());
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
~~~
這些SpringBoot為我們自動做了,這些對我們并不透明,如果你項目做的比較大,添加的持久化依賴比較多,我們還是會選擇人為的指定使用哪個事務管理器。
代碼如下:
~~~
@EnableTransactionManagement
@SpringBootApplication
public class ProfiledemoApplication {
// 其中 dataSource 框架會自動為我們注入
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager) {
System.out.println(">>>>>>>>>>" + platformTransactionManager.getClass().getName());
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
~~~
在Spring容器中,我們手工注解@Bean 將被優先加載,框架不會重新實例化其他的 PlatformTransactionManager 實現類。
然后在Service中,被 @Transactional 注解的方法,將支持事務。如果注解在類上,則整個類的所有方法都默認支持事務。
對于同一個工程中存在多個事務管理器要怎么處理,請看下面的實例,具體說明請看代碼中的注釋。
@EnableTransactionManagement // 開啟注解事務管理,等同于xml配置文件中的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication implements TransactionManagementConfigurer {
~~~
@Resource(name="txManager2")
private PlatformTransactionManager txManager2;
// 創建事務管理器1
@Bean(name = "txManager1")
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 創建事務管理器2
@Bean(name = "txManager2")
public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
// 實現接口 TransactionManagementConfigurer 方法,其返回值代表在擁有多個事務管理器的情況下默認使用的事務管理器
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager2;
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
~~~
~~~
@Component
public class DevSendMessage implements SendMessage {
// 使用value具體指定使用哪個事務管理器
@Transactional(value="txManager1")
@Override
public void send() {
System.out.println(">>>>>>>>Dev Send()<<<<<<<<");
send2();
}
// 在存在多個事務管理器的情況下,如果不使用value具體指定
// 則默認使用方法 annotationDrivenTransactionManager() 返回的事務管理器
@Transactional
public void send2() {
System.out.println(">>>>>>>>Dev Send2()<<<<<<<<");
}
}
~~~
>注:
如果Spring容器中存在多個 PlatformTransactionManager 實例,并且沒有實現接口 TransactionManagementConfigurer 指定默認值,在我們在方法上使用注解 @Transactional 的時候,就必須要用value指定,如果不指定,則會拋出異常。
>對于系統需要提供默認事務管理的情況下,實現接口 TransactionManagementConfigurer 指定。
>對有的系統,為了避免不必要的問題,在業務中必須要明確指定 @Transactional 的 value 值的情況下。不建議實現接口 TransactionManagementConfigurer,這樣控制臺會明確拋出異常,開發人員就不會忘記主動指定。
>
除了指定不同的事務管理器之后,還能對事務進行隔離級別和傳播行為的控制,下面分別詳細解釋:
## 隔離級別
隔離級別是指若干個并發的事務之間的隔離程度,與我們開發時候主要相關的場景包括:臟讀取、重復讀、幻讀。
我們可以看 org.springframework.transaction.annotation.Isolation 枚舉類中定義了五個表示隔離級別的值:
~~~
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
}
~~~
* DEFAULT :這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是: READ_COMMITTED 。
* READ_UNCOMMITTED :該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀和不可重復讀,因此很少使用該隔離級別。
* READ_COMMITTED :該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止臟讀,這也是大多數情況下的推薦值。
* REPEATABLE_READ :該隔離級別表示一個事務在整個過程中可以多次重復執行某個查詢,并且每次返回的記錄都相同。即使在多次查詢之間有新增的數據滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止臟讀和不可重復讀。
* SERIALIZABLE :所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
指定方法:通過使用 isolation 屬性設置,例如:
`@Transactional(isolation = Isolation.DEFAULT)`
## 傳播行為
所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。
我們可以看 org.springframework.transaction.annotation.Propagation 枚舉類中定義了6個表示傳播行為的枚舉值:
~~~
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
}
~~~
* REQUIRED :如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
* SUPPORTS :如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
* MANDATORY :如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
* REQUIRES_NEW :創建一個新的事務,如果當前存在事務,則把當前事務掛起。
* NOT_SUPPORTED :以非事務方式運行,如果當前存在事務,則把當前事務掛起。
* NEVER :以非事務方式運行,如果當前存在事務,則拋出異常。
* NESTED :如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于 REQUIRED
指定方法:通過使用 propagation 屬性設置,例如:
`@Transactional(propagation = Propagation.REQUIRED)`
- I. Spring Boot Documentation
- 1.關于文檔
- 2. 獲取幫助
- 3.第一步
- 4.使用spring boot
- 5.學習spring boot的特點
- 6.轉移到生產
- 7.高級話題
- II. Getting Started
- 8.引入spring boot
- 9.系統需要
- 9.1 Servlet容器
- 10.安裝spring boot
- 10.1 java開發者安裝說明
- 10.1.1 Maven Installation
- 10.2 Installing the Spring Boot CLI TODO
- 10.3 從早期的版本升級
- 11.開發你的第一個spring boot應用
- 11.1 創建POM
- 11.2 添加Classpath依賴
- 11.3 寫代碼
- 11.3.1 @RestController 和@RequestMapping 注解
- 11.3.2 @EnableAutoConfiguration注解
- 11.3.3 main方法
- 11.4運行 Example
- 11.5 創建可執行的Jar
- 12.下一步要閱讀的
- III. Using Spring Boot
- 13.構建系統
- 13.1 依賴管理
- 13.2 Maven
- 13.2.1 繼承Starter Parent
- 13.2.2 不使用Spring Boot的Parent POM
- 13.2.3 使用Spring Boot Maven 插件
- 13.3. Gradle TODO
- 13.4. Ant TODO
- 13.5. Starters TODO
- 14.結構化代碼
- 14.1使用“default”包
- 14.2 主應用程序類的位置
- 15.配置類
- 15.1 引入新增的配置類
- 15.2 引入xml配置
- 16.自動化配置
- 16.1 逐漸替換自動配置
- 16.2 禁用特定的配置類
- 17.spring的bean和依賴配置
- 18.使用@SpringBootApplication注解
- 19.運行的你的程序
- 19.1 在IDE中運行
- 19.2 運行打包的jar
- 19.3 使用maven插件運行
- 19.4 使用Gradle 插件 TODO
- 19.5 熱交換
- 20.開發者工具
- 20.1 默認屬性
- 20.2 自動重啟
- 20.2.1 記錄環境評估的更改
- 20.2.2 排除Resources
- 20.2.3 監視其他路徑
- 20.2.4 禁止重啟
- 20.2.5 使用觸發文件
- 20.2.6 自定義重啟類加載器
- 20.2.7 了解限制
- 20.3 實時加載
- 20.4 全局設置
- 20.5 遠程應用
- 20.5.1 運行遠程客戶端
- 20.5.2 遠程更新
- 21.把你的應用打包到生產
- 22.下一步要閱讀的
- IV. Spring Boot features
- 23.spring應用
- 23.1 啟動失敗
- 23.10 管理功能
- 23.8 使用ApplicationRunner 或 CommandLineRunner
- 24.可擴展的配置
- 24.1 配置隨機值
- 24.2 訪問命令行屬性
- 24.3 配置文件
- 24.4 特定配置文件
- 24.5 屬性的占位符
- 24.6 使用yaml替代properties
- 24.7 類型安全的配置屬性
- 24.7.1 第三方配置
- 24.7.2 靈活的綁定
- 25.外部的配置profiles
- 25.1 追加可用的外部配置
- 25.2 編程式的設置外部配置
- 25.3 外部配置文件
- 26.日志
- 26.1 日志格式
- 26.2 控制臺輸出
- 26.2.1顏色代碼數據
- 26.3 文件輸出
- 26.4 日志級別
- 26.5 自定義日志配置
- 26.6 Logback擴展
- 26.6.1特定于配置文件的配置
- 26.6.2 環境屬性
- 26.6.3 logback.xml配置說明
- 27.開發web應用
- 27.1 spring mvc
- 27.1.1 Spring MVC自動配置
- 27.1.2 HttpMessageConverters
- 27.1.3 定制JSON序列號和反序列化
- 27.1.4 MessageCodesResolver
- 27.1.5 靜態內容
- 27.1.6 歡迎頁面
- 27.1.7 定制Favicon
- 27.1.8 路徑匹配和內容判斷
- 27.1.9 ConfigurableWebBindingInitializer
- 27.1.10 模板引擎
- 27.1.11 處理錯誤
- 27.1.12 Spring HATEOAS
- 27.1.13 跨域支持
- 27.2 spring webflux
- 28.安全
- 28.1 mvc安全
- httpSecurity,webSecurity,authenticationManager
- 29.使用SQL數據庫
- 29.1 配置數據源
- 29.1.1 嵌入式數據庫支持
- 29.1.2 可用于生產的數據庫
- 29.1.3 從JNDI DataSource獲取連接
- 30.使用NoSql技術
- 30.1 Redis
- 30.1.1 連接Redis
- 31.緩存
- 32.消息
- 32.1 jms
- 32.2 amqp
- 32.2.1 支持RabbitMQ
- 32.2.2 發送消息
- 32.2.3 接收消息
- 33.使用RestTemplate調用REST服務
- 34.使用WebClient調用REST 服務
- 35.驗證
- 36.發郵件
- 37.使用JTA的分布式事務
- 37.01 結合spring框架的本地事務
- 38. Hazelcast
- 39.定時任務
- 40.spring集成
- 41.spring會話
- 42.JMX的監控和管理
- 43.測試
- 43.1測試范圍的依賴
- 43.2 測試Spring應用
- 43.2 測試spring boot應用
- 44. WebSockets
- 45. Web Services
- 46.創建你自己的自動化配置
- 47. Kotlin的支持
- 48.下一步要閱讀的
- V. Spring Boot Actuator: Production-ready features
- 49.開啟生產用的特性
- 50.端點
- 50.1 啟動端點
- 50.2 暴露端點
- 50.3 安全的http端點
- 51.HTTP的監控和管理
- 52.JMX的監控和管理
- 53.日志
- 54.度量
- 55.審計
- 56.HTTP追蹤
- 57.過程監控
- 58.上云的支持
- 59.下一步要閱讀的
- VI. Deploying Spring Boot Applications
- 60.云端的部署
- 61.安裝spring boot應用
- 62.下一步要閱讀的
- VII. Spring Boot CLI
- 63.安裝CTL
- 64.使用CTL
- 65.使用Groovy Beans DSL開發應用
- 66.使用settings.xml配置CTL
- 67.下一步要閱讀的
- VIII. Build tool plugins
- 68. Spring Boot Maven插件
- 69. Spring Boot Gradle插件
- 70. Spring Boot AntLib模塊
- 71.其他構建系統的支持
- 72.下一步要閱讀的
- IX. ‘How-to’ guides
- 73.spring boot應用
- 74.屬性和配置
- 75.嵌入式的web服務器
- 76. Spring MVC
- 76.1 寫JSON風格的服務
- 76.2 寫XML風格的服務
- 76.3 定制Jackson ObjectMapper
- 76.4 定制@ResponseBody呈現
- 76.5 處理文件上傳
- 77. Jersey
- 78. HTTP Clients
- 79. 日志
- 80. 數據訪問
- 80.1 配置自己的數據源
- 81. 數據庫初始化
- 82. 消息
- 83. 批處理應用
- 84. Actuator
- 85. 安全
- 86. 熱交換
- 87.構建
- 88.傳統開發
- X. Appendices
- A.通用的配置
- B. 配置元信息
- C.自動配置類
- D. 測試自動配置注解
- E. 可執行的jar格式
- F. 依賴的版本