詳解在SpringBoot中@Transactional事物操作和事物無(wú)效問(wèn)題排查
1.spring事務(wù)管理簡(jiǎn)述
兩種事務(wù)管理方式:
- 編碼式事務(wù)管理:將事務(wù)控制代碼編寫(xiě)在業(yè)務(wù)代碼之中。
- 聲明式事務(wù)管理:基于AOP(面向切面編程),事務(wù)管理與業(yè)務(wù)邏輯解耦。聲明式事務(wù)管理的兩種實(shí)現(xiàn):
- 在配置文件(xml)中配置。
- 基于@Transactional注解。
2.SpringBoot中使用@Transactional注解
2.1.開(kāi)啟事務(wù)注解
在項(xiàng)目主類(lèi)上,加上注解@EnableTransactionManagement,例如:
@EnableTransactionManagement
public class MySpringBootService extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(CoreService.class, args);
}
}
2.2.在目標(biāo)類(lèi)、方法上添加注解@Transactional
1. 如果將@Transactional添加到類(lèi)上,則表示此類(lèi)的所有方法都開(kāi)啟事務(wù)管理。如:
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
@Service
public class MyServiceImpl implements MyService {
//class body
}
2. 如果將@Transactional添加到方法上,則表示此方法開(kāi)啟事務(wù)管理。如:
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
@Override
public ActivityPo getActivityById(Long id){
//method body
}
3. 如果一個(gè)方法上存在@Transactional,且其所屬類(lèi)上同樣存在@Transactional,則以方法級(jí)別的事務(wù)配置為準(zhǔn)。
2.3.細(xì)化事務(wù)配置
關(guān)于@Transactional的可配置參數(shù)有很多,主要有propagation、rollbackFor等,可以適用于不同場(chǎng)景,這里不細(xì)說(shuō)。
3.@Transactional事務(wù)實(shí)現(xiàn)機(jī)制
3.1.整體事務(wù)控制流程
- 當(dāng)@Transactional注解的方法被類(lèi)外部的代碼調(diào)用時(shí),Spring在運(yùn)行時(shí)為方法所在類(lèi)生成一個(gè)AOP代理對(duì)象。
- 代理對(duì)象根據(jù)@Transactional的屬性,決定是否由事務(wù)攔截器TransactionInterceptor對(duì)此方法進(jìn)行事務(wù)攔截。
- 在進(jìn)行事務(wù)攔截時(shí),會(huì)先開(kāi)啟事務(wù),然后執(zhí)行業(yè)務(wù)代碼,根據(jù)執(zhí)行是否出現(xiàn)異常,通過(guò)抽象事務(wù)管理器AbstractPlatformTransactionManager來(lái)進(jìn)行rollback或者commit。
3.2.Spring AOP的兩種代理
- Spring AOP有兩種CglibAopProxy和JdkDynamicAopProxy,其中:
- CglibAopProxy在其內(nèi)部類(lèi)DynamicAdvisedInterceptor的intercept()方法中,判斷是否進(jìn)行事務(wù)攔截。
- JdkDynamicAopProxy在其invoke()方法中,判斷是否進(jìn)行事務(wù)攔截。
3.3.事務(wù)操作的底層實(shí)現(xiàn)
- 抽象事務(wù)管理器AbstractPlatformTransactionManager的rollback和commit都需要具體的實(shí)現(xiàn)類(lèi)進(jìn)行實(shí)現(xiàn)。
- 抽象事務(wù)管理器AbstractPlatformTransactionManager的父級(jí)接口是PlatformTransactionManager。
- 存在很多事務(wù)管理器實(shí)現(xiàn)類(lèi),例如DataSourceTransactionManager等。
- 不同的事務(wù)管理器管理不同的數(shù)據(jù)資源 DataSource,比如DataSourceTransactionManager管理者JDBC數(shù)據(jù)源。
- 應(yīng)確保被調(diào)用方法中使用的數(shù)據(jù)源都加載了事務(wù)管理器。
4.@Transactional使用注釋實(shí)現(xiàn)及問(wèn)題排查
4.1.數(shù)據(jù)庫(kù)引擎是否支持事務(wù)?
- MySql的引擎MyIsam不支持事務(wù)。
- 如需事務(wù)控制生效,則庫(kù)和表的引擎必須是InnoDB。
4.3.注解所在的類(lèi)是否被加載成Bean?
- 章節(jié)3.1中第1條提到,需要在運(yùn)行時(shí)為類(lèi)生成代理對(duì)象。那么前提是這個(gè)類(lèi)一定被Spring管理并加載成了一個(gè)Bean對(duì)象。
- 確保所在類(lèi)是否被@Component、@Service、@Controller等等注解注釋。
4.2.注解所在方法是否為public修飾的?
- 章節(jié)3.2中,提到兩種AOP代理分別在intercept()和invoke()方法判斷是否進(jìn)行事務(wù)攔截。
- 這兩個(gè)方法都會(huì)間接調(diào)用AbstractFallbackTransactionAttributeSource類(lèi)的computeTransactionAttribute方法來(lái)獲取事務(wù)控制的相關(guān)屬性。這其中有以下一段代碼:
/**
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
* <p>As of 4.1.8, this method can be overridden.
* @since 4.1.8
* @see #getTransactionAttribute
*/
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
//...
}
- 這段代碼會(huì)導(dǎo)致no-public的方法無(wú)法進(jìn)入事務(wù)控制。
- 所以,一定要確保自己需要進(jìn)行事務(wù)控制的方法包含public修飾符。
4.5.是否發(fā)生了自調(diào)用問(wèn)題?
- 章節(jié)3.1中第1條強(qiáng)調(diào):只有當(dāng)事務(wù)方法被當(dāng)前類(lèi)以外的代碼調(diào)用時(shí),才會(huì)才由 Spring 生成的代理對(duì)象來(lái)管理。
- 上述邏輯會(huì)造成自調(diào)用問(wèn)題:當(dāng)事務(wù)方法被本類(lèi)內(nèi)部方法調(diào)用時(shí),@Transactional并不生效。
- 自調(diào)用示例代碼:
@Service
public class PersonServiceImpl implements PersonService{
@Resource
private PersonDao personDao;
public void insertPerson(Person person){
//自調(diào)用
personService.insert(person);
//其他代碼
personDao.insertLog...
}
@Transactional(rollbackFor = Exception.class)
public void insert(Person person){
personDao.insert(person);
}
}
- 上述代碼中,如果業(yè)務(wù)邏輯從非事務(wù)方法insertPerson()開(kāi)始,在其中調(diào)用了事務(wù)方法insert(),則當(dāng)insert()異常時(shí),事務(wù)控制無(wú)效。
- 簡(jiǎn)單說(shuō),就是在同一類(lèi)中,非事務(wù)方法A調(diào)用了事務(wù)方法B,則當(dāng)事務(wù)方法B異常,事務(wù)控制無(wú)效,A和B都不會(huì)回滾。
- 那么,在同一類(lèi)中,事務(wù)方法A調(diào)用了非事務(wù)方法B,然后非事務(wù)方法B調(diào)用了事務(wù)方法C,事務(wù)是否生效?答案:是。因?yàn)槭聞?wù)方法A在被外部代碼調(diào)用時(shí),已經(jīng)開(kāi)啟了事務(wù)管理。
4.6.所用數(shù)據(jù)源是否加載了事務(wù)管理器?
- 章節(jié)3.3中第5條提到:應(yīng)確保被調(diào)用方法中使用的數(shù)據(jù)源都加載了事務(wù)管理器。
- 在SpringBoot項(xiàng)目中,如果是單數(shù)據(jù)源,那么系統(tǒng)會(huì)默認(rèn)為單數(shù)據(jù)源配置事務(wù)管理器DataSourceTransactionManager。
- 在SpringBoot項(xiàng)目中,如果是多數(shù)據(jù)源,則一定確保所有的數(shù)據(jù)源都配置了事務(wù)管理器。
- 關(guān)于多數(shù)據(jù)源的配置方法可以參考: https://blog.csdn.net/hanchao5272/article/details/81209552
- 事務(wù)管理器的手動(dòng)配置方法,可以參考如下:
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager(@Qualifier("sqlDataSource") DataSource sqlDataSource) {
return new DataSourceTransactionManager(sqlDataSource);
}
4.4.觸發(fā)回滾的異常是否配置正確?
- 默認(rèn)情況下,事務(wù)回歸針對(duì)的是uncheck的異常(運(yùn)行時(shí)異常)或ERROR。
- 默認(rèn)情況下,check的異常并不會(huì)觸發(fā)回滾,如FileNotFoundException。
- 如果想要簡(jiǎn)單的配置成針對(duì)所有異常都回滾,可以這么做:
@Transactional(rollbackFor = Exception.class)
4.5.@Transactional的擴(kuò)展配置propagation是否正確?
- 一般情況下,propagation屬性無(wú)需配置。其會(huì)使用默認(rèn)配置,即:Propagation.REQUIRED。
- 有些propagation屬性會(huì)導(dǎo)致事務(wù)不會(huì)觸發(fā),一定要注意:
- SUPPORTS: 如果存在事務(wù),則進(jìn)入事務(wù);否則,以非事務(wù)方式運(yùn)行。
- NOT_SUPPORTED: 如果存在事務(wù),則掛起事務(wù),并以非事務(wù)方式運(yùn)行。
- NEVER: 以非事務(wù)形式運(yùn)行,如果存在事務(wù),則拋出異常。
4.7.事務(wù)管理的可選配置是否正確?
在SpringBoot中,關(guān)于事務(wù)的配置有兩個(gè)可選配置(一般無(wú)需配置):
- Springboot啟動(dòng)類(lèi)的@EnableTransactionManagement。
- Springboot配置文件的rollback-on-commit-failure屬性:
# yaml配置
spring:
transaction:
rollback-on-commit-failure: true
# properties配置
spring.transaction.rollback-on-commit-failure=true
請(qǐng)確保上述配置都是正確的(或者未配置)。
到此這篇關(guān)于詳解在SpringBoot中@Transactional事物操作和事物無(wú)效問(wèn)題排查的文章就介紹到這了,更多相關(guān)SpringBoot使用@Transactional內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決mybatisplus MetaObjectHandler 失效的問(wèn)題
本文主要介紹了解決mybatisplus MetaObjectHandler 失效的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
淺談java中為什么實(shí)體類(lèi)需要實(shí)現(xiàn)序列化
下面小編就為大家?guī)?lái)一篇淺談java中為什么實(shí)體類(lèi)需要實(shí)現(xiàn)序列化。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
Spring?Boot項(xiàng)目部署命令java?-jar的各種參數(shù)及作用詳解
這篇文章主要介紹了Spring?Boot項(xiàng)目部署命令java?-jar的各種參數(shù)及作用的相關(guān)資料,包括設(shè)置內(nèi)存大小、垃圾回收器、線(xiàn)程棧大小、系統(tǒng)屬性等,還介紹了SpringBoot專(zhuān)用參數(shù),如修改端口、指定配置文件等,需要的朋友可以參考下2025-04-04
spring 整合kafka監(jiān)聽(tīng)消費(fèi)的配置過(guò)程
這篇文章主要介紹了spring 整合kafka監(jiān)聽(tīng)消費(fèi)的配置過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
jar命令修改jar包中的application.yml配置文件
本文主要介紹了jar命令修改jar包中的application.yml配置文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
Springboot使用@RefreshScope注解實(shí)現(xiàn)配置文件的動(dòng)態(tài)加載
本文主要介紹了Springboot使用@RefreshScope注解實(shí)現(xiàn)配置文件的動(dòng)態(tài)加載,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Java設(shè)計(jì)模式之適配器模式(Adapter模式)介紹
這篇文章主要介紹了Java設(shè)計(jì)模式之適配器模式(Adapter模式)介紹,本文講解了為何使用適配器模式、如何使用適配器模式等內(nèi)容,需要的朋友可以參考下2015-03-03
列舉java語(yǔ)言中反射的常用方法及實(shí)例代碼
反射機(jī)制指的是程序在運(yùn)行時(shí)能夠獲取自身的信息。這篇文章主要介紹了列舉java語(yǔ)言中反射的常用方法,需要的朋友可以參考下2019-07-07

