問題:Spring 與 Myabatis 整合后,為什么 DAO 不提交事務,但是數據能夠插入數據庫中?
- Mybatis 提供的連接池對象 —> 創建 Connection
Connection.setAutoCommit(false) 手工的控制了事務,操作完成后,需要手工提交。 - Druid(C3P0、DBCP)作為連接池 —> 創建 Connection
Connection.setAutoCommit(true) 默認值為 true,保持自動控制事務,一條 sql 自動提交。
答案:因為 Spring 與 Mybatis 整合時,引入了外部連接池對象,保持自動的事務提交這個機制Connection.setAutoCommit(true),不需要手工進行事務的操作,也能進行事務的提交。
注意:實戰中,還是會手工控制事務(多條SQL一起成功,一起失敗),后續 Spring 通過 事務控制 解決這個問題
1. 事務回顧
事務的 4 大特點: A
、C
、I
、D
Atomicity
原子性Consistency
一致性Isolation
隔離性Durability
持久性
如何控制事務?(JDBC、Mybatis)
JDBC
Connection.setAutoCommit(false);
Connection.commit();
Connection.rollback();
Mybatis
Mybatis 自動開啟事務
sqlSession.commit();,底層還是調用的 Connection
sqlSession.rollback();,底層還是調用的 Connection
2. Spring 中控制事務的開發
3. Spring 中的事務屬性(Transaction Attribute)
什么是事務屬性?
描述物體特征的一系列值(性別、身高、體重)
事務屬性:描述事務特征的一系列值
如何添加事務屬性?
@Transactional(isolation=, propagation=, readOnly=,timeout=,rollbackFor,noRollbackFor=,)
隔離屬性(ISOLATION)
描述了事務解決並發問題的特征。
- 什么是並發?
多個事務(用戶)在同一時間,訪問操作了相同的數據。
同一時間:0.000 幾秒左右 - 並發會產生那些問題?
- 臟讀
- 不可重復讀
- 幻影讀
- 並發問題如何解決?
通過隔離屬性解決,隔離屬性中設置不同過的值,解決並發處理的過程中的問題。
事務並發產生的問題:
- 臟讀
一個事務,讀取了另一個事務中沒有提交的數據,會在本事務中產生數據不一樣的現象
解決方案:@Transaction(isolation = Isolation.READ_COMMITTED)
- 不可重復讀
一個事務中,多次讀取相同的數據,但是讀取結果不一樣,會在本事務中產生數據不一樣的現象
注意:1.不是臟讀 2.在一個事務中
解決方案:@Transaction(isolation = Isolation.REPEATABLE_READ)
本質:一把行鎖(對數據庫表的某一行加鎖) - 幻影讀
一個事務中,多次對整表進行查詢統計,但是結果不一樣,會在本事務中產生數據不一致的問題
解決方案:@Transaction(isolation = Isolation.SERIALIZABLE)
本質:表鎖(對數據庫某個表加鎖)
安全與效率對比:
- 並發安全:
SERIALIZABLE
>READ_ONLY
>READ_COMMITTED
- 運行效率:
READ_COMMITTED
>READ_ONLY
>SERIALIZABLE
默認的隔離屬性:
Spring 默認會指定為 ISOLATION_DEFAULT
,調用不同數據庫所設置的默認隔離屬性
- MySQL:
REPEATABLE_READ
- Oracle:
READ_COMMITTED
查看數據庫的默認隔離屬性:
- MySQL:
SELECT @@tx_isolation;
- Oracle:較麻煩,建議百度。
隔離屬性在實驗中的建議:
- 推薦使用 Spring 默認指定的
ISOLATION_DEFAULT
- 未來的實戰中,遇到並發訪問的情況,很少見
- 如果真的遇到並發問題,解決方案:樂觀鎖
Hibernate(JPA):version
MyBatis:通過攔截器自定義開發
傳播屬性(PROPAGATION)
概念:描述了事務解決 嵌套問題 的特征。
事務的嵌套
指的是一個大的事務中,包含了若干個小的事務。
事務嵌套產生的問題
大事務中融入了很多小的事務,他們彼此影響,最終就導致外部大的事務喪失了事務的原子性。
傳播屬性的值及其用法:
傳播屬性的值 | 外部不存在事務 | 外部存在事務 | 用法 | 備注 |
---|---|---|---|---|
REQUIRED(默認) | 開啟新的事務 | 融合到外部事務中 | @Transactional(propagation = Propagation.REQUIRED) | 增、刪、改方法 |
SUPPORTS | 不開啟事務 | 融合到外部事務中 | @Transactional(propagation = Propagation.SUPPORTS) | 查詢方法 |
REQUIRES_NEW | 開啟新的事務 | 掛起外部事務,創建新的事務 | @Transactional(propagation = Propagation.REQUIRES_NEW) | 日志記錄方法中 |
NOT_SUPPORTED | 不開啟事務 | 掛起外部事務 | @Transactional(propagation = Propagation.NOT_SUPPORTED) | 極其不常用 |
NEVER | 不開啟事務 | 拋出異常 | @Transactional(propagation = Propagation.NEVER) | 極其不常用 |
MANDATORY | 拋出異常 | 融合到外部事物中 | @Transactional(propagation = Propagation.MANDATORY) | 極其不常用 |
Spring 中傳播屬性的默認值是:REQUIRED
推薦傳播屬性的使用方式:
- 增刪改 方法:使用默認值 REQUIRED
- 查詢 方法:顯示指定傳播屬性的值為 SUPPORTS
只讀屬性(readOnly)
針對於 只進行查詢操作的業務方法,可以加入只讀屬性,提高運行效率。
默認值:false
@Transactional(readOnly = true)
超時屬性(timeout)
指定了事務等待的最長時間。
為什么事務會進行等待?
- 當前事務訪問數據時,有可能訪問的數據被別的事務進行加鎖的處理,那么此時本事務就必須進行等待。
- 等待時間:單位是 秒
- 如何使用:@Transactional(timeout = 2)
- 超時屬性的默認值:-1
-1 表示超時屬性由對應的數據庫來指定(一般不會主動指定,-1 即可)
異常屬性
Spring 事務處理過程中:
- 默認對於
RuntimeException
及其子類,采用 回滾 的策略。 - 默認對於
Exception
及其子類,采用 提交 的策略。
使用方法:
@Transactional(rollbackFor = java.lang.Exception.class, xxx, xxx)
@Transactional(noRollbackFor = java.lang.RuntimeException, xxx, xxx)
事務屬性常見配置總結
- 隔離屬性:默認值
- 傳播屬性:Required(默認值)增刪改、Supports 查詢操作
- 只讀屬性:readOnly=false 增刪改,true 查詢操作
- 超時屬性:默認值 -1
- 異常屬性:默認值
開發建議
增刪改操作:@Transactional
查詢操作:@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)