那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

淺談Spring事務的那些坑

Android編程精選 ? 來源:稀土掘金技術社區(qū) ? 作者:蘇三說技術 ? 2022-10-11 10:31 ? 次閱讀

對于從事java開發(fā)工作的同學來說,spring的事務肯定再熟悉不過了。在某些業(yè)務場景下,如果同時有多張表的寫入操作,為了保證操作的原子性(要么同時成功,要么同時失敗)避免數(shù)據(jù)不一致的情況,我們一般都會使用spring事務。

沒錯,spring事務大多數(shù)情況下,可以滿足我們的業(yè)務需求。但是今天我要告訴大家的是,它有很多坑,稍不注意事務就會失效。

不信,我們一起看看。

1.錯誤的訪問權限

@Service
publicclassUserService{

@Autowired
privateUserMapper userMapper;

@Transactional
privatevoidadd(UserModel userModel){
userMapper.insertUser(userModel);
}
}

我們可以看到add方法的訪問權限被定義成了private,這樣會導致事務失效,spring要求被代理方法必須是public的。

AbstractFallbackTransactionAttributeSource類的computeTransactionAttribute方法中有個判斷,如果目標方法不是public,則TransactionAttribute返回null,即不支持事務。


protectedTransactionAttribute computeTransactionAttribute(Method method, @NullableClasstargetClass){
// Don't allow no-public methods as required.
if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
returnnull;
}

// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

// First try is the method in the target class.
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if(txAttr !=null) {
returntxAttr;
}

// Second try is the transaction attribute on the target class.
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if(txAttr !=null&& ClassUtils.isUserLevelMethod(method)) {
returntxAttr;
}

if(specificMethod != method) {
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if(txAttr !=null) {
returntxAttr;
}
// Last fallback is the class of the original method.
txAttr = findTransactionAttribute(method.getDeclaringClass());
if(txAttr !=null&& ClassUtils.isUserLevelMethod(method)) {
returntxAttr;
}
}

returnnull;
}

2.方法被定義成final的

@Service
publicclassUserService{

@Autowired
privateUserMapper userMapper;

@Transactional
publicfinalvoidadd(UserModel userModel){
userMapper.insertUser(userModel);
}
}

我們可以看到add方法被定義成了final的,這樣會導致spring aop生成的代理對象不能復寫該方法,而讓事務失效。

3.方法內(nèi)部調(diào)用

@Service
publicclassUserService{

@Autowired
privateUserMapper userMapper;

@Transactional
publicvoidadd(UserModel userModel){
userMapper.insertUser(userModel);
updateStatus(userModel);
}

@Transactional
publicvoidupdateStatus(UserModel userModel){
// doSameThing();
}
}

我們看到在事務方法add中,直接調(diào)用事務方法updateStatus。從前面介紹的內(nèi)容可以知道,updateStatus方法擁有事務的能力是因為spring aop生成代理了對象,但是這種方法直接調(diào)用了this對象的方法,所以updateStatus方法不會生成事務。

4.當前實體沒有被spring管理


//@Service
publicclassUserService{

@Autowired
privateUserMapper userMapper;

@Transactional
publicvoidadd(UserModel userModel){
userMapper.insertUser(userModel);
}
}

我們可以看到UserService類沒有定義@Service注解,即沒有交給spring管理bean實例,所以它的add方法也不會生成事務。

5.錯誤的spring事務傳播特性


@Service
publicclassUserService{

@Autowired
privateUserMapper userMapper;

@Transactional(propagation = Propagation.NEVER)
publicvoidadd(UserModel userModel){
userMapper.insertUser(userModel);
}

}

我們可以看到add方法的事務傳播特性定義成了Propagation.NEVER,這種類型的傳播特性不支持事務,如果有事務則會拋異常。只有這三種傳播特性才會創(chuàng)建新事務:PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED。

6.數(shù)據(jù)庫不支持事務


msql8以前的版本數(shù)據(jù)庫引擎是支持myslam和innerdb的。我以前也用過,對應查多寫少的單表操作,可能會把表的數(shù)據(jù)庫引擎定義成myslam,這樣可以提升查詢效率。但是,要千萬記得一件事情,myslam只支持表鎖,并且不支持事務。所以,對這類表的寫入操作事務會失效。

7.自己吞掉了異常


@Slf4j
@Service
public class UserService {

@Autowired
private UserMapper userMapper;

@Transactional
public void add(UserModel userModel) {
try{
userMapper.insertUser(userModel);
}catch(Exception e) {
log.error(e.getMessage(), e);
}
}
}

這種情況下事務不會回滾,因為開發(fā)者自己捕獲了異常,又沒有拋出。事務的AOP無法捕獲異常,導致即使出現(xiàn)了異常,事務也不會回滾。

8.拋出的異常不正確


@Slf4j
@Service
public class UserService {

@Autowired
private UserMapper userMapper;

@Transactional
public void add(UserModel userModel) throws Exception {
try{
userMapper.insertUser(userModel);
}catch(Exception e) {
log.error(e.getMessage(), e);
thrownewException(e);
}
}

}

這種情況下,開發(fā)人員自己捕獲了異常,又拋出了異常:Exception,事務也不會回滾。因為spring事務,默認情況下只會回滾RuntimeException(運行時異常)和Error(錯誤),不會回滾Exception。

9.多線程調(diào)用

@Slf4j
@Service
publicclassUserService{

@Autowired
privateUserMapper userMapper;
@Autowired
privateRoleService roleService;

@Transactional
publicvoidadd(UserModel userModel)throwsException{
userMapper.insertUser(userModel);
newThread(() -> {
roleService.doOtherThing();
}).start();
}
}

@Service
publicclassRoleService{

@Transactional
publicvoiddoOtherThing(){
System.out.println("保存role表數(shù)據(jù)");
}
}

我們可以看到事務方法add中,調(diào)用了事務方法doOtherThing,但是事務方法doOtherThing是在另外一個線程中調(diào)用的,這樣會導致兩個事務方法不在同一個線程中,獲取到的數(shù)據(jù)庫連接不一樣,從而是兩個不同的事務。如果想doOtherThing方法中拋了異常,add方法也回滾是不可能的。

如果看過spring事務源碼的朋友,可能會知道spring的事務是通過數(shù)據(jù)庫連接來實現(xiàn)的。當前線程中保存了一個map,key是數(shù)據(jù)源,value是數(shù)據(jù)庫連接。


privatestaticfinal ThreadLocal> resources =
newNamedThreadLocal<>("Transactional resources");

我們說的同一個事務,其實是指同一個數(shù)據(jù)庫連接,只有擁有同一個數(shù)據(jù)庫連接才能同時提交和回滾。如果在不同的線程,拿到的數(shù)據(jù)庫連接肯定是不一樣的,所以是不同的事務。

10.嵌套事務多回滾了

publicclassUserService{

@Autowired
privateUserMapper userMapper;

@Autowired
privateRoleService roleService;

@Transactional
publicvoidadd(UserModel userModel)throwsException{
userMapper.insertUser(userModel);
roleService.doOtherThing();
}
}

@Service
publicclassRoleService{

@Transactional(propagation = Propagation.NESTED)
publicvoiddoOtherThing(){
System.out.println("保存role表數(shù)據(jù)");
}
}

這種情況使用了嵌套的內(nèi)部事務,原本是希望調(diào)用roleService.doOtherThing方法時,如果出現(xiàn)了異常,只回滾doOtherThing方法里的內(nèi)容,不回滾 userMapper.insertUser里的內(nèi)容,即回滾保存點。。但事實是,insertUser也回滾了。

why?

因為doOtherThing方法出現(xiàn)了異常,沒有手動捕獲,會繼續(xù)往上拋,到外層add方法的代理方法中捕獲了異常。所以,這種情況是直接回滾了整個事務,不只回滾單個保存點。

怎么樣才能只回滾保存點呢?


@Slf4j
@Service
public class UserService {

@Autowired
private UserMapper userMapper;

@Autowired
private RoleService roleService;

@Transactional
public void add(UserModel userModel) throws Exception {

userMapper.insertUser(userModel);
try{
roleService.doOtherThing();
}catch(Exception e) {
log.error(e.getMessage(), e);
}
}

}

在代碼中手動把內(nèi)部嵌套事務放在try/catch中,并且不繼續(xù)往拋異常。

介紹到這里,你會發(fā)現(xiàn)spring事務的坑還是挺多的~

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2974

    瀏覽量

    105137
  • spring
    +關注

    關注

    0

    文章

    340

    瀏覽量

    14388

原文標題:Spring事務的這10種坑,坑坑致命!

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Spring事務失效的十種常見場景

    Spring針對Java Transaction API (JTA)、JDBC、Hibernate和Java Persistence API(JPA)等事務 API,實現(xiàn)了一致的編程模型,而
    的頭像 發(fā)表于 12-11 15:03 ?957次閱讀

    Spring事務實現(xiàn)原理

    作者:京東零售 范錫軍 1、引言 springspring-tx模塊提供了對事務管理支持,使用spring事務可以讓我們從復雜的
    的頭像 發(fā)表于 11-08 10:10 ?872次閱讀
    <b class='flag-5'>Spring</b><b class='flag-5'>事務</b>實現(xiàn)原理

    什么是java spring

    。在SSH項目中管理事務以及對象的注入Spring是非侵入式的:基于Spring開發(fā)的系統(tǒng)中的對象一般不依賴于Spring的類。組成 Spring
    發(fā)表于 09-11 11:16

    Spring的兩種方式事務管理和API接口介紹

    Spring事務管理
    發(fā)表于 03-21 06:52

    Spring事務分析的實現(xiàn)方式

    Spring事務原理分析
    發(fā)表于 07-02 15:19

    詳解Spring事務管理

    在學習spring事務管理時,我忍不住要問,spring為什么進行事務管理,spring怎么進行的事務
    發(fā)表于 07-12 06:54

    Spring事務管理詳解說明

    Spring事務管理詳解
    發(fā)表于 05-20 13:46

    關于 STM32 時鐘配置的那些

    關于STM32時鐘配置的那些
    的頭像 發(fā)表于 03-08 12:06 ?6007次閱讀

    spring中聲明式事務實現(xiàn)原理猜想

    ? @Transactional注解簡介 @Transactional 是spring中聲明式事務管理的注解配置方式,相信這個注解的作用大家都很清楚。 @Transactional 注解可以幫助
    的頭像 發(fā)表于 10-13 09:20 ?1663次閱讀

    發(fā)現(xiàn)一個Spring事務的巨bug 你必須要小心了

    1.錯誤的訪問權限 2.方法被定義成final的 3.方法內(nèi)部調(diào)用 4.當前實體沒有被spring管理 5.錯誤的spring事務傳播特性 6.數(shù)據(jù)庫不支持事務 7.自己吞掉了異常 8
    的頭像 發(fā)表于 10-11 18:17 ?896次閱讀

    淺談Spring事務底層原理

    開啟Spring事務本質(zhì)上就是增加了一個Advisor,但我們使用@EnableTransactionManagement注解來開啟Spring事務是,該注解代理的功能就是向
    的頭像 發(fā)表于 12-06 09:56 ?731次閱讀

    Spring事務在哪幾種情況下會不生效?

    日常開發(fā)中,我們經(jīng)常使用到spring事務。最近星球一位還有去美團面試,被問了這么一道面試題: Spring 事務在哪幾種情況下會不生效?
    的頭像 發(fā)表于 05-10 17:53 ?964次閱讀
    <b class='flag-5'>Spring</b><b class='flag-5'>事務</b>在哪幾種情況下會不生效?

    8個Spring事務失效的場景介紹

    作為Java開發(fā)工程師,相信大家對Spring事務的使用并不陌生。但是你可能只是停留在基礎的使用層面上,在遇到一些比較特殊的場景,事務可能沒有生效,直接在生產(chǎn)上暴露了,這可能就會導致比較嚴重的生產(chǎn)
    的頭像 發(fā)表于 05-11 10:41 ?711次閱讀
    8個<b class='flag-5'>Spring</b><b class='flag-5'>事務</b>失效的場景介紹

    spring事務失效的一些場景

    不一致的情況,我們一般都會用到spring事務。 確實,spring事務用起來賊爽,就用一個簡單的注解: @Transactional ,就能輕松搞定
    的頭像 發(fā)表于 10-08 14:27 ?485次閱讀
    <b class='flag-5'>spring</b><b class='flag-5'>事務</b>失效的一些場景

    Spring事務傳播性的相關知識

    本文主要介紹了Spring事務傳播性的相關知識。
    的頭像 發(fā)表于 01-10 09:29 ?490次閱讀
    <b class='flag-5'>Spring</b><b class='flag-5'>事務</b>傳播性的相關知識
    百家乐官网书| qq百家乐官网网络平台| 大发888真钱棋牌软件| 博之道百家乐技巧| 三元玄空24山坐向开门| 百家乐官网赌博工具| 百家乐官网正确的打法| 大发888完整客户端| 大丰收百家乐的玩法技巧和规则 | 罗盘24方位| 海王星百家乐官网的玩法技巧和规则| 百家乐官网娱乐城会员| 镇沅| 德州扑克辅助软件| 全讯网找a3322.com| 百家乐十赌九诈| 百家乐打法内容介绍| 新乐园百家乐官网娱乐城| 豪博百家乐官网娱乐城| 百家乐官网游戏机出千| 斗六市| 狮威国际娱乐| 棋牌游戏平台有哪些| 太阳城代理最新网址| 诺贝尔百家乐的玩法技巧和规则| E世博百家乐娱乐城| 百家乐模拟投注器| 百家乐官网下注技巧| 游戏机百家乐官网的玩法技巧和规则| 百家乐官网赌博经历| 澳门百家乐官网庄闲和| 真钱百家乐官网注册送| 芜湖县| 特克斯县| 太子娱乐城官网| 菲律宾赌球| 奇博国际娱乐网| 新全讯网网站112| 全讯网图库| 大发888娱乐城赢钱| 现场百家乐机|