一、事務特性
@Transactional注解是用於事務控制的,需要知道事務的ACID特征:即原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。
事務是用來控制數據的ACID特性的,用於保證數據的正確性和完整性。
@Transactional注解有兩種使用方式:
(1)標注在類上面:當作用於類上時,該類的所有public方法將都具有該類型的事務屬性,同時,我們也可以在方法級別使用該標注來覆蓋類級別的定義。
(2)標注在方法上面:當作用於方法上時,只有當該方法發生了異常才會進行回滾,其他的方法不受影響。
在項目中使用,@Transactional(rollbackFor=Exception.class),如果類加了這個注解,那么這個類里面的方法拋出異常,就會回滾,數據庫里面的數據也會回滾。在@Transactional注解中如果不配置rollbackFor屬性,那么事物只會在遇到RuntimeException的時候才會回滾,加上rollbackFor=Exception.class,可以讓事物在遇到非運行時異常時也回滾。
二、事務傳播行為與隔離級別
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
(1)事物傳播行為介紹:
@Transactional(propagation=Propagation.REQUIRED) :如果有事務, 那么加入事務, 沒有的話新建一個(默認情況下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不為這個方法開啟事務
@Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事務,都創建一個新的事務,原來的掛起,新的執行完畢后繼續執行老的事務
@Transactional(propagation=Propagation.MANDATORY) :必須在一個已有的事務中執行,否則拋出異常
@Transactional(propagation=Propagation.NEVER) :必須在一個沒有的事務中執行,否則拋出異常(與Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) :如果其他bean調用這個方法,在其他bean中聲明事務那就用事務,如果其他bean沒有聲明事務,那就不用事務
(2)事物超時設置:
@Transactional(timeout=30) //默認是30秒
(3)事務隔離級別:
@Transactional(isolation = Isolation.READ_UNCOMMITTED):讀取未提交數據(會出現臟讀, 不可重復讀) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED):讀取已提交數據(會出現不可重復讀和幻讀)
@Transactional(isolation = Isolation.REPEATABLE_READ):可重復讀(會出現幻讀)
@Transactional(isolation = Isolation.SERIALIZABLE):串行化
MYSQL: 默認為REPEATABLE_READ級別
SQLSERVER: 默認為READ_COMMITTED
臟讀 : 一個事務讀取到另一事務未提交的更新數據。
不可重復讀 : 在同一事務中,多次讀取同一數據返回的結果有所不同,即后續讀取可能讀到另一事務已提交的更新數據, 相反, "可重復讀"在同一事務中多次
讀取數據時能夠保證所讀數據一樣,即后續讀取不能讀到另一事務已提交的更新數據。
幻讀 : 一個事務讀到另一個事務已提交的insert數據。
三、spring支持編程式事務管理和聲明式事務管理兩種方式
(1)編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,spring推薦使用TransactionTemplate。
(2)聲明式事務管理建立在AOP之上的。其本質是對方法前后進行攔截,然后在目標方法開始之前創建或者加入一個事務,在執行完目標方法之后根據執行情況提交或者回滾事務。聲明式事務管理也有兩種常用的方式:一種是基於tx和aop名字空間的xml配置文件;另一種是基於@Transactional注解。顯然基於注解的方式更簡單易用,更清爽。
使用注解形式的方式使用事務需要在xml配置文件中配置如下信息
<!-- 服務事務管理 -->
<bean id="demoTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="demoDataSource"/>
</bean>
<!-- 啟用事務注解 -->
<tx:annotation-driven transaction-manager="demoTransactionManager"/>
四、需要注意的幾點如下
(1)@Transactional 注解只能被應用到public方法上,對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能。
(2)用spring事務管理器,由spring來負責數據庫的打開、提交、回滾,默認遇到運行期異常(throw new RuntimeException("注釋");)會回滾,即遇到不受檢查(unchecked)的異常時執行回滾;而遇到需要捕獲的異常(throw new Exception("注釋");)不會回滾,即遇到受檢查的異常(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢異常)時,需要我們指定方式來讓事務回滾。如果要想所有異常都回滾,則要加上@Transactional( rollbackFor=Exception.class),
如果讓unchecked異常不回滾: @Transactional(notRollbackFor=RunTimeException.class)。如下代碼所示:
@Transactional(rollbackFor=Exception.class) //指定回滾,遇到異常Exception時回滾
public void methodName() {
throw new Exception("注釋");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滾,遇到運行期例外(throw new RuntimeException("注釋");)會回滾
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注釋");
}
(3)@Transactional 注解應該只被應用到public可見度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯, 但是這個被注解的方法將不具有事務功能。
(4)@Transactional 注解可以被應用於接口定義和接口方法、類定義和類的 public 方法上。然而,請注意僅僅 @Transactional 注解的出現不足於開啟事務行為,它僅僅是一種元數據,能夠被可以識別。上面的例子中,其實正是元素的出現開啟了事務行為。也就是說要有具體的bean出現時事務才會起作用。
(5)Spring團隊建議在具體的類(或類的方法)上使用@Transactional 注解,而不要使用在類所要實現的任何接口上。你當然可以在接口上使用 @Transactional 注解,但是這將只能當你設置了基於接口的代理時它才生效。因為注解是不能繼承的,這就意味着如果你正在使用基於類的代理時,那么事務的設置將不能被基於類的代理所識別。因此,請接受Spring團隊的建議,必須接受啊!
五、@Transactional注解所具有的屬性
屬性 | 類型 | 描述 |
---|---|---|
value | String | 可選的限定描述符,指定使用的事務管理器 |
propagation | enum: Propagation | 可選的事務傳播行為設置 |
isolation | enum: Isolation | 可選的事務隔離級別設置 |
readOnly | boolean | 讀寫或只讀事務,默認讀寫 |
timeout | int (in seconds granularity) | 事務超時時間設置 |
rollbackFor | Class對象數組,必須繼承自Throwable | 導致事務回滾的異常類數組 |
rollbackForClassName | 類名數組,必須繼承自Throwable | 導致事務回滾的異常類名字數組 |
noRollbackFor | Class對象數組,必須繼承自Throwable | 不會導致事務回滾的異常類數組 |
noRollbackForClassName | 類名數組,必須繼承自Throwable | 不會導致事務回滾的異常類名字數組 |
六、事務可能導致鎖表
事務有可能導致鎖表,從而導致查詢操作超時。鎖表的原因是多個事務同時操作某一張表。
mysql事務鎖表參考博文: https://blog.csdn.net/wo541075754/article/details/50717842 (mysql事務鎖表導致查詢超時)
mysql中主要是注意以下三張表:
select * from information_schema.INNODB_LOCKS;
select * from information_schema.INNODB_LOCK_WAITS;
select * from information_schema.INNODB_TRX;
參考博文:
(1) https://www.cnblogs.com/clwydjgs/p/9317849.html (Spring中的@Transactional(rollbackFor = Exception.class)屬性詳解)
(2) https://www.cnblogs.com/caoyc/p/5632963.html (事物注解方式: @Transactional)
(3) https://www.cnblogs.com/jasonboren/p/11361971.html (mysql事務特性)
(4) https://www.cnblogs.com/jasonboren/p/13366130.html (Java 異常)