Spring?Boot?中事務(wù)的用法示例詳解
引言
在 Spring Boot 中,事務(wù)管理是一個(gè)非常重要的功能,尤其是在涉及數(shù)據(jù)庫(kù)操作的業(yè)務(wù)場(chǎng)景中。Spring 提供了強(qiáng)大的事務(wù)管理支持,能夠幫助我們簡(jiǎn)化事務(wù)的管理和控制。本文將詳細(xì)介紹 Spring Boot 中事務(wù)的用法,包括事務(wù)的基本概念、事務(wù)的配置、事務(wù)的傳播行為、事務(wù)的隔離級(jí)別以及事務(wù)的回滾機(jī)制。
1. 事務(wù)的基本概念
事務(wù)(Transaction)是指一組數(shù)據(jù)庫(kù)操作,這些操作要么全部成功,要么全部失敗。事務(wù)的四大特性(ACID)包括:
- 原子性(Atomicity):事務(wù)中的所有操作要么全部成功,要么全部失敗。
- 一致性(Consistency):事務(wù)執(zhí)行前后,數(shù)據(jù)庫(kù)的狀態(tài)保持一致。
- 隔離性(Isolation):多個(gè)事務(wù)并發(fā)執(zhí)行時(shí),彼此之間互不干擾。
- 持久性(Durability):事務(wù)一旦提交,對(duì)數(shù)據(jù)庫(kù)的修改是永久性的。
在 Spring Boot 中,事務(wù)管理是通過(guò) @Transactional
注解來(lái)實(shí)現(xiàn)的。
2. Spring Boot 中事務(wù)的配置
2.1 啟用事務(wù)管理
Spring Boot 默認(rèn)已經(jīng)集成了事務(wù)管理功能,只需要在配置類或啟動(dòng)類上添加 @EnableTransactionManagement
注解即可啟用事務(wù)管理。
@SpringBootApplication @EnableTransactionManagement // 啟用事務(wù)管理 public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
2.2 配置數(shù)據(jù)源和事務(wù)管理器
Spring Boot 默認(rèn)使用 DataSourceTransactionManager
作為事務(wù)管理器。如果你使用的是 Spring Data JPA,事務(wù)管理器會(huì)自動(dòng)配置。
# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver
3. 使用 @Transactional 注解
@Transactional
是 Spring 提供的事務(wù)管理注解,可以標(biāo)注在類或方法上。標(biāo)注在類上時(shí),表示該類中的所有方法都啟用事務(wù)管理;標(biāo)注在方法上時(shí),表示該方法啟用事務(wù)管理。
@Service public class UserService { @Autowired private UserRepository userRepository; @Transactional // 開(kāi)啟事務(wù) 表示該方法開(kāi)啟了事務(wù) public void createUser(User user) { userRepository.save(user); } }
3.2 事務(wù)的傳播行為
事務(wù)的傳播行為(Propagation)定義了事務(wù)方法之間的調(diào)用關(guān)系。Spring 提供了以下幾種傳播行為:
- REQUIRED(默認(rèn)):如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。(適用于大多數(shù)業(yè)務(wù)場(chǎng)景,尤其是需要保證多個(gè)操作在同一個(gè)事務(wù)中執(zhí)行的場(chǎng)景。 例如,訂單創(chuàng)建時(shí)需要同時(shí)更新訂單表和庫(kù)存表。)
- REQUIRES_NEW:無(wú)論當(dāng)前是否存在事務(wù),都創(chuàng)建一個(gè)新的事務(wù)。(適用于需要獨(dú)立事務(wù)的場(chǎng)景,尤其是日志記錄、審計(jì)等操作。 例如,記錄操作日志時(shí),即使主事務(wù)失敗,日志記錄仍然需要成功。)
- SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)方式執(zhí)行。(適用于不需要強(qiáng)制事務(wù)的場(chǎng)景,例如查詢操作。 例如,查詢用戶信息時(shí),如果調(diào)用方有事務(wù),則加入事務(wù);如果沒(méi)有事務(wù),則以非事務(wù)方式執(zhí)行。)
- NOT_SUPPORTED:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),則掛起該事務(wù)。(適用于不需要事務(wù)支持的場(chǎng)景,例如發(fā)送消息、調(diào)用外部接口等。 例如,發(fā)送短信通知時(shí),不需要事務(wù)支持。)
- MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則拋出異常。(適用于強(qiáng)制要求調(diào)用方必須有事務(wù)的場(chǎng)景。 例如,某些核心業(yè)務(wù)邏輯必須在一個(gè)事務(wù)中執(zhí)行。)
- NEVER:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),則拋出異常。(適用于強(qiáng)制要求調(diào)用方不能有事務(wù)的場(chǎng)景。 例如,某些只讀操作或外部調(diào)用。)
- NESTED:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行;如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。(適用于需要部分回滾的場(chǎng)景。 例如,訂單創(chuàng)建時(shí)需要更新多個(gè)表,如果某個(gè)表更新失敗,只需要回滾該表的操作,而不影響其他表的操作。)
示例:
@Transactional(propagation = Propagation.REQUIRES_NEW) public void updateUser(User user) { userRepository.save(user); }
3.3 事務(wù)的隔離級(jí)別
事務(wù)的隔離級(jí)別(Isolation)定義了事務(wù)之間的可見(jiàn)性。Spring 支持以下幾種隔離級(jí)別:
- DEFAULT:使用數(shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別。(適用于大多數(shù)通用場(chǎng)景,尤其是當(dāng)你對(duì)數(shù)據(jù)庫(kù)的默認(rèn)行為沒(méi)有特殊要求時(shí)。 如果你不確定應(yīng)該選擇哪種隔離級(jí)別,可以使用 DEFAULT,讓數(shù)據(jù)庫(kù)根據(jù)其默認(rèn)行為處理事務(wù)。)
- READ_UNCOMMITTED:允許讀取未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀和不可重復(fù)讀。(適用于對(duì)數(shù)據(jù)一致性要求不高的場(chǎng)景,例如統(tǒng)計(jì)數(shù)據(jù)的讀取或日志記錄。 不適用于涉及資金、訂單等對(duì)數(shù)據(jù)一致性要求高的場(chǎng)景。)
- READ_COMMITTED:只能讀取已提交的數(shù)據(jù),可以避免臟讀,但可能會(huì)導(dǎo)致幻讀和不可重復(fù)讀。(適用于大多數(shù)業(yè)務(wù)場(chǎng)景,尤其是對(duì)數(shù)據(jù)一致性有一定要求但不需要嚴(yán)格隔離的場(chǎng)景。 例如,電商系統(tǒng)中的訂單查詢、用戶信息查詢等。)
- REPEATABLE_READ:確保在同一事務(wù)中多次讀取同一數(shù)據(jù)時(shí)結(jié)果一致,可以避免臟讀和不可重復(fù)讀,但可能會(huì)導(dǎo)致幻讀。(適用于對(duì)數(shù)據(jù)一致性要求較高的場(chǎng)景,例如銀行系統(tǒng)中的賬戶余額查詢。 例如,在一個(gè)事務(wù)中多次讀取同一賬戶的余額時(shí),確保結(jié)果一致。)
- SERIALIZABLE:最高隔離級(jí)別,確保事務(wù)串行執(zhí)行,可以避免臟讀、幻讀和不可重復(fù)讀(適用于對(duì)數(shù)據(jù)一致性要求極高的場(chǎng)景,例如金融系統(tǒng)中的資金清算、庫(kù)存管理等。 由于性能開(kāi)銷較大,通常只在必要時(shí)使用。)
示例:
@Transactional(isolation = Isolation.READ_COMMITTED) public User getUserById(Long id) { return userRepository.findById(id).orElse(null); }
3.4 事務(wù)的回滾機(jī)制
默認(rèn)情況下,Spring 會(huì)在方法拋出 RuntimeException
或 Error
時(shí)回滾事務(wù)。如果需要自定義回滾規(guī)則,可以通過(guò) rollbackFor(哪些異?;貪L)
和 noRollbackFor(哪些異常不會(huì)回滾)
屬性來(lái)指定。
示例:
@Transactional(rollbackFor = Exception.class) // 所有異常都回滾 public void updateUser(User user) throws Exception { userRepository.save(user); if (user.getName() == null) { throw new Exception("用戶名不能為空"); // 拋出受檢異常 } }
4. 事務(wù)的嵌套與傳播行為
在復(fù)雜的業(yè)務(wù)場(chǎng)景中,可能會(huì)存在事務(wù)方法調(diào)用事務(wù)方法的情況。此時(shí),事務(wù)的傳播行為決定了事務(wù)的嵌套方式。
4.1 嵌套事務(wù)示例
@Service public class OrderService { @Autowired private UserService userService; @Transactional //一級(jí)事務(wù) public void createOrder(Order order) { // 保存訂單 orderRepository.save(order); // 調(diào)用另一個(gè)事務(wù)方法 userService.updateUser(order.getUser()); } } @Service public class UserService { @Transactional(propagation = Propagation.REQUIRES_NEW) //二級(jí)事務(wù) public void updateUser(User user) { userRepository.save(user); } }
在上面的示例中,createOrder
方法調(diào)用 updateUser
方法時(shí),updateUser
方法會(huì)開(kāi)啟一個(gè)新的事務(wù)。
5. 事務(wù)的注意事項(xiàng)(事務(wù)不生效的幾種情況)
事務(wù)方法的可見(jiàn)性:
@Transactional
只能應(yīng)用于 public
方法。如果應(yīng)用于 private
或 protected
方法,事務(wù)將不會(huì)生效。Spring 的事務(wù)管理是基于代理模式實(shí)現(xiàn)的,代理對(duì)象只能攔截 public
方法。對(duì)于 private
或 protected
方法,Spring 無(wú)法生成代理,因此事務(wù)不會(huì)生效。
@Service public class UserService { @Autowired private UserRepository userRepository; // 正確:public 方法,事務(wù)生效 @Transactional public void createUser(User user) { userRepository.save(user); } // 錯(cuò)誤:private 方法,事務(wù)不會(huì)生效 @Transactional private void updateUser(User user) { userRepository.save(user); } // 錯(cuò)誤:protected 方法,事務(wù)不會(huì)生效 @Transactional protected void deleteUser(Long userId) { userRepository.deleteById(userId); } }
事務(wù)的自我調(diào)用問(wèn)題:
如果事務(wù)方法調(diào)用了同一個(gè)類中的另一個(gè)事務(wù)方法,事務(wù)的傳播行為可能不會(huì)生效。這是因?yàn)镾pring 的代理對(duì)象只能攔截從外部調(diào)用的方法。如果事務(wù)方法在同一個(gè)類中調(diào)用另一個(gè)事務(wù)方法,實(shí)際上是直接調(diào)用目標(biāo)方法,而不是通過(guò)代理對(duì)象調(diào)用,因此事務(wù)的傳播行為不會(huì)生效。
@Service public class OrderService { @Autowired private OrderRepository orderRepository; @Transactional public void createOrder(Order order) { // 保存訂單 orderRepository.save(order); // 調(diào)用另一個(gè)事務(wù)方法(自我調(diào)用) updateInventory(order.getProductId(), order.getQuantity()); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateInventory(Long productId, int quantity) { // 更新庫(kù)存邏輯 } }
在上面的示例中,createOrder
方法調(diào)用了 updateInventory
方法,但由于是自我調(diào)用,updateInventory
方法的事務(wù)傳播行為(REQUIRES_NEW
)不會(huì)生效。
解決方法:
將事務(wù)方法拆分到不同的類中:
將 updateInventory
方法移到另一個(gè)服務(wù)類中,通過(guò)依賴注入調(diào)用。
@Service public class OrderService { @Autowired private OrderRepository orderRepository; @Autowired private InventoryService inventoryService; @Transactional public void createOrder(Order order) { // 保存訂單 orderRepository.save(order); // 調(diào)用另一個(gè)服務(wù)類的事務(wù)方法 inventoryService.updateInventory(order.getProductId(), order.getQuantity()); } } @Service public class InventoryService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateInventory(Long productId, int quantity) { // 更新庫(kù)存邏輯 } }
事務(wù)的超時(shí)設(shè)置:
可以通過(guò) @Transactional(timeout = 10)
設(shè)置事務(wù)的超時(shí)時(shí)間(單位為秒)。如果事務(wù)執(zhí)行時(shí)間超過(guò)指定時(shí)間,事務(wù)將自動(dòng)回滾。
@Service public class ReportService { @Autowired private ReportRepository reportRepository; @Transactional(timeout = 10) // 設(shè)置事務(wù)超時(shí)時(shí)間為 10 秒 public void generateReport() { // 模擬耗時(shí)操作 for (int i = 0; i < 1000000; i++) { reportRepository.save(new Report("Report " + i)); } } }
在上面的示例中,如果 generateReport
方法的執(zhí)行時(shí)間超過(guò) 10 秒,事務(wù)將自動(dòng)回滾。
6. 總結(jié)
Spring Boot 提供了強(qiáng)大的事務(wù)管理功能,通過(guò) @Transactional
注解可以輕松實(shí)現(xiàn)事務(wù)的控制。在實(shí)際開(kāi)發(fā)中,需要根據(jù)業(yè)務(wù)需求選擇合適的傳播行為和隔離級(jí)別,同時(shí)注意事務(wù)方法的可見(jiàn)性和自我調(diào)用問(wèn)題。
通過(guò)本文的介紹,相信你已經(jīng)掌握了 Spring Boot 中事務(wù)的基本用法。如果你有更多問(wèn)題,歡迎在評(píng)論區(qū)留言討論!
到此這篇關(guān)于Spring Boot 中事務(wù)的用法詳解的文章就介紹到這了,更多相關(guān)Spring Boot 事務(wù)的用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 深入理解SpringBoot事務(wù)傳播機(jī)制
- SpringBoot 編程式事務(wù)使用及兩種實(shí)現(xiàn)方式
- SpringBoot同一個(gè)方法操作多個(gè)數(shù)據(jù)源保證事務(wù)一致性
- SpringBoot中的手動(dòng)提交事務(wù)
- springboot結(jié)合mybatis操作事務(wù)配置的處理
- springboot使用kafka事務(wù)的示例代碼
- SpringBoot 多數(shù)據(jù)源及事務(wù)解決方案小結(jié)
- SpringBoot各種事務(wù)操作實(shí)戰(zhàn)(自動(dòng)回滾、手動(dòng)回滾、部分回滾)
相關(guān)文章
Java多線程Runable售票系統(tǒng)實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Java多線程Runable售票系統(tǒng)實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06通過(guò)入門(mén)demo簡(jiǎn)單了解netty使用方法
這篇文章主要介紹了通過(guò)入門(mén)demo簡(jiǎn)單了解netty使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12MyBatis動(dòng)態(tài)創(chuàng)建表的實(shí)例代碼
在項(xiàng)目需求中,我們經(jīng)常會(huì)遇到動(dòng)態(tài)操作數(shù)據(jù)表的需求,常見(jiàn)的我們會(huì)把日志、設(shè)備實(shí)時(shí)位置信息等存入數(shù)據(jù)表,并且以一定時(shí)間段生成一個(gè)表來(lái)存儲(chǔ)。接下來(lái)通過(guò)本文給大家介紹MyBatis動(dòng)態(tài)創(chuàng)建表的方法,感興趣的朋友一起看看吧2018-07-07基于CyclicBarrier和CountDownLatch的使用區(qū)別說(shuō)明
這篇文章主要介紹了基于CyclicBarrier和CountDownLatch的使用區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09SpringBoot整合mybatis常見(jiàn)問(wèn)題(小結(jié))
這篇文章主要介紹了SpringBoot整合mybatis常見(jiàn)問(wèn)題(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Java Socket報(bào)錯(cuò)打開(kāi)文件過(guò)多的問(wèn)題
這篇文章主要介紹了Java Socket報(bào)錯(cuò)打開(kāi)文件過(guò)多的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Mybatis Generator Plugin悲觀鎖實(shí)現(xiàn)示例
本文將從悲觀鎖為例,讓你快速了解如何實(shí)現(xiàn)Mybatis Generator Plugin。文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09解決Java項(xiàng)目中request流只能獲取一次的問(wèn)題
Java項(xiàng)目開(kāi)發(fā)中可能存在以下幾種情況,你需要在攔截器中統(tǒng)一攔截請(qǐng)求和你項(xiàng)目里可能需要搞一個(gè)統(tǒng)一的異常處理器,這兩種情況是比較常見(jiàn)的,本文將給大家介紹如何解決Java項(xiàng)目中request流只能獲取一次的問(wèn)題,需要的朋友可以參考下2024-02-02SpringBoot整合logback日志的詳細(xì)步驟
這篇文章主要介紹了SpringBoot整合logback日志的詳細(xì)步驟,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05淺析我對(duì) String、StringBuilder、StringBuffer 的理解
StringBuilder、StringBuffer 和 String 一樣,都是用于存儲(chǔ)字符串的。這篇文章談?wù)勑【帉?duì)String、StringBuilder、StringBuffer 的理解,感興趣的朋友跟隨小編一起看看吧2020-05-05