總結(jié)一下Spring中事務(wù)失效的八種場(chǎng)景
1. 數(shù)據(jù)庫(kù)引擎不支持事務(wù)
這里以 MySQL為例,MyISAM引擎是不支持事務(wù)操作的,一般要支持事務(wù)都會(huì)使用InnoDB引擎,根據(jù)MySQL 的官方文檔說(shuō)明,從MySQL 5.5.5 開始的默認(rèn)存儲(chǔ)引擎是 InnoDB,之前默認(rèn)的都是 MyISAM,所以這一點(diǎn)要值得注意,如果底層引擎不支持事務(wù),那么再怎么設(shè)置也沒(méi)有用。
2.沒(méi)有被 Spring 管理
示例如下:
public class OrderServiceImpl implements OrderService{
@Transactional
public void updateOrder(Order order){
//update order
}
}
如果此時(shí)把@Service注解注釋掉,那么這個(gè)類就不會(huì)被加載成一個(gè)Bean,這個(gè)類就不會(huì)Spring管理了,事務(wù)自然就失效了。
3. 方法不是 public 的
@Transactional注解只能用干public 的方法上,否則事多不會(huì)生效,如果要用在非public的方法上,則可以開啟基于 AspcetJ 框架的靜態(tài)代理模式。
4.發(fā)生自身調(diào)用
示例如下:
@Service
public class OrderServiceImpl implements OrderService {
public void update(Order order) {
updateOrder(order);
}
}
@Transactional
public void updateOrder(0rder order) {
// update order
}
}
update 方法上面沒(méi)有加 @Transactional 注解,如果調(diào)用有 @Transactional 注解的updateOrder 方法,那么 updateOrder 方法上的事務(wù)還可以生效嗎? 這里大家可以先想一想,后面會(huì)揭曉答案。
再來(lái)看下面這個(gè)例子:
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
updateOrder(order);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateOrder(0rder order) {
updateOrder(order);
}
}
這次在 update 方法上加了 @Transactional, 如果在 updateOrder 上加了 REOUIRES_NEW新開啟一個(gè)事務(wù),那么新開啟的事務(wù)可以生效嗎?
這兩個(gè)例子中的事務(wù)都不會(huì)生效,因?yàn)樗鼈儼l(fā)生了自身調(diào)用,就調(diào)用了該類自己的方法,而沒(méi)有經(jīng)過(guò)Spring的代理類,默認(rèn)只有調(diào)用外部代理類的方法,事務(wù)才會(huì)生效,這也是老生常談的問(wèn)題了。
這個(gè)問(wèn)題的解決方案之一就是在事務(wù)所在的類中注入自己,用往入的對(duì)象再調(diào)用另外一個(gè)方法,這個(gè)不太優(yōu)雅,在Spring 中可以在當(dāng)前線程中暴露并獲取當(dāng)前代理類,通過(guò)在啟動(dòng)類上添加以下注解來(lái)啟用暴露代理類,如下面的示例所示。
@EnableAspectJAutoProxy(exposeProxy = true)
然后通過(guò)以下代碼獲取當(dāng)前代理類,并調(diào)用代理類的事務(wù)方法:
((0rderService) AopContext.currentProxy()).updateOrder();
Spring 默認(rèn)只有調(diào)用 Spring代理類的public 方法,事務(wù)才能生效。
5.沒(méi)有配置事務(wù)管理器
如果沒(méi)有配置以下DataSourceTransactionManager數(shù)據(jù)源事務(wù)管理器,那么事務(wù)也不會(huì)生效 :
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
但在 Spring Boot 中只要引入了 spring-boot-starter-data-jdbc 啟動(dòng)器依賴就會(huì)自動(dòng)配置DataSourceTransactionManager數(shù)據(jù)源事務(wù)管理器,所以 Spring Boot框架不存在這個(gè)問(wèn)題,但在傳統(tǒng)的 Spring 框架中需要注意。
6. 設(shè)置了不支持事務(wù)
示例如下:
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
updateOrder(order);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void updateOrder(Order order) {
//update order
}
}
這里的Propagation.NOT_SUPPORTED表示當(dāng)前方法不以事務(wù)方式運(yùn)行,當(dāng)前若存在事務(wù)則掛起,這就是主動(dòng)不支持以事務(wù)方式運(yùn)行了。
7. 異常沒(méi)有被拋出
示例如下:
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
try{
// update order
}catch{
}
}
}
這個(gè)方法把異常給捕獲了,但沒(méi)有拋出來(lái),所以事務(wù)不會(huì)回滾,只有捕捉到異常事務(wù)才會(huì)生效。
8. 異常類型不匹配
示例如下:
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
try{
// update order
}catch{
throw new Exception("更新失敗");
}
}
}
因?yàn)?Spring 默認(rèn)回滾的是 RuntimeException 異常,和程序拋出的 Exception 異常不匹配,所以事務(wù)也是不生效的。如果要觸發(fā)默認(rèn) RuntimeException之外異常的回滾,則需要在 @Transactiona事務(wù)注解上指定異常類,示例如下:
@Transactional(rollbackFor = Exception.class)
在今天的文章中總結(jié)了使用 @Transactional注解導(dǎo)致事務(wù)失效的幾個(gè)常見場(chǎng)景,如果 @Transactional事務(wù)不生效,則可以根據(jù)這幾種情形排查一下,其實(shí)次數(shù)最多的也就是發(fā)生自身調(diào)用、異常被捕獲、異常拋出類型不匹配這幾種場(chǎng)景。