最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring事務(wù)@Transactional失效的8大場(chǎng)景與解決方法

 更新時(shí)間:2025年08月21日 09:06:12   作者:Micro麥可樂(lè)  
在我們開(kāi)發(fā)Spring Boot應(yīng)用中,很多小伙伴以為只要在方法上加一個(gè) @Transactional,但實(shí)際開(kāi)發(fā)中,事務(wù)經(jīng)常出現(xiàn)失效的情況,下面我們就來(lái)看看如何解決吧

1. 前言

在我們開(kāi)發(fā)Spring Boot應(yīng)用中,很多小伙伴以為只要在方法上加一個(gè) @Transactional,事務(wù)就能自動(dòng)回滾,保證數(shù)據(jù)一致性。但實(shí)際開(kāi)發(fā)中,事務(wù)經(jīng)常出現(xiàn)失效的情況:明明拋了異常,數(shù)據(jù)庫(kù)還是提交了。你肯定會(huì)疑惑:“為什么我加了注解,數(shù)據(jù)還是沒(méi)回滾?”

通過(guò)本文博主將徹底和小伙伴們說(shuō)清楚,讓大家別再踩坑!從 @Transactional 的參數(shù)詳解入手,再結(jié)合常見(jiàn)事務(wù)失效場(chǎng)景給出 正確寫(xiě)法 vs 錯(cuò)誤寫(xiě)法 對(duì)比,幫助小伙伴們徹底理解 Spring 事務(wù)機(jī)制。

2. @Transactional 核心參數(shù)解析

Spring的 @Transactional 提供了很多參數(shù),但日常最常用的主要有以下幾個(gè):

@Transactional(
    propagation = Propagation.REQUIRED, // 事務(wù)傳播行為
    isolation = Isolation.DEFAULT,      // 事務(wù)隔離級(jí)別
    rollbackFor = Exception.class,      // 回滾規(guī)則
    timeout = 30,                       // 超時(shí)時(shí)間(秒)
    readOnly = false                    // 是否只讀事務(wù)
)

2.1 propagation (事務(wù)傳播行為)

作用:定義了當(dāng)前事務(wù)方法與另一個(gè)事務(wù)方法相互調(diào)用時(shí),事務(wù)應(yīng)該如何傳播。

Spring的傳播屬性有以下幾種:

REQUIRED (默認(rèn)):如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。

假設(shè)現(xiàn)在有一個(gè)需求添加用戶(hù)的時(shí)候給用戶(hù)同時(shí)發(fā)放優(yōu)惠券

用戶(hù)ServiceuserService.saveUser() 方法 調(diào)用 優(yōu)惠券ServicecouponService.add()方法。

當(dāng)userService.saveUser() 運(yùn)行時(shí),先開(kāi)啟一個(gè)事務(wù),此時(shí)couponService.add()方法發(fā)現(xiàn)已經(jīng)存在一個(gè)事務(wù),就不會(huì)再開(kāi)啟事務(wù),只要任何一個(gè)方法報(bào)錯(cuò)則都會(huì)回調(diào)。

REQUIRES_NEW:創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。

依舊以上述添加用戶(hù)的時(shí)候給用戶(hù)同時(shí)發(fā)放優(yōu)惠券為例子

當(dāng)userService.saveUser()運(yùn)行時(shí),先開(kāi)啟一個(gè)事務(wù)A。當(dāng)運(yùn)行couponService.add()時(shí),把事務(wù)A掛起,然后開(kāi)啟事務(wù)B。就算事務(wù)A發(fā)生回滾,事務(wù)B依然能正常提交。

外部事務(wù)不會(huì)影響內(nèi)部事務(wù)的提交和回滾。

  • SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)方式繼續(xù)運(yùn)行。
  • NOT_SUPPORTED:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
  • MANDATORY:必須在一個(gè)已有的事務(wù)中運(yùn)行,否則拋出異常。
  • NEVER:必須在非事務(wù)方式下運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。
  • NESTED:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行;如果當(dāng)前沒(méi)有事務(wù),則行為同 REQUIRED。

2.2 isolation(隔離級(jí)別)

作用:定義事務(wù)的隔離級(jí)別,解決并發(fā)事務(wù)可能帶來(lái)的臟讀、不可重復(fù)讀、幻讀等問(wèn)題。

  • READ_COMMITTED:避免臟讀,但可能有不可重復(fù)讀。
  • REPEATABLE_READ:避免臟讀和不可重復(fù)讀。
  • SERIALIZABLE:最高級(jí)別,完全串行化,性能差。

2.3 rollbackFor / noRollbackFor(回滾規(guī)則)

作用:指定哪些異常會(huì)觸發(fā)事務(wù)回滾

默認(rèn):僅遇到運(yùn)行時(shí)異常 RuntimeExceptionError 才回滾

如果要對(duì)檢查型異常(如 IOException)回滾,必須顯式配置 rollbackFor = Exception.class

2.4 timeout (超時(shí)時(shí)間)

作用:事務(wù)的超時(shí)時(shí)間,單位為秒。如果超過(guò)該時(shí)間事務(wù)尚未完成,則自動(dòng)回滾

2.5 readOnly(是否只讀事務(wù))

作用:提示數(shù)據(jù)庫(kù)該事務(wù)為只讀事務(wù),數(shù)據(jù)庫(kù)可能會(huì)進(jìn)行一些優(yōu)化。默認(rèn)為false

3. 事務(wù)失效的常見(jiàn)場(chǎng)景

下面列出最常見(jiàn)的 8大事務(wù)失效場(chǎng)景,并配合代碼示例對(duì)比

3.1 方法不是 public

Spring 默認(rèn)使用基于代理的 AOP,對(duì)于非 public 方法,代理對(duì)象無(wú)法攔截到其調(diào)用,導(dǎo)致注解失效。

錯(cuò)誤寫(xiě)法:

@Service
public class UserService {
    
    @Transactional
    void saveUser(User user) { // 不是public
        userMapper.insert(user);
        throw new RuntimeException("模擬異常");
    }
}

正確寫(xiě)法:

@Service
public class UserService {
    
    @Transactional
    public void saveUser(User user) {
        userMapper.insert(user);
        throw new RuntimeException("模擬異常");
    }
}

3.2 自調(diào)用(同類(lèi)方法內(nèi)部調(diào)用)

類(lèi)內(nèi)部的方法 A 調(diào)用另一個(gè)有 @Transactional 注解的方法 B,這屬于自調(diào)用。調(diào)用的是 this 對(duì)象本身的方法,而不是被 Spring 代理增強(qiáng)過(guò)的對(duì)象的方法,因此事務(wù)不會(huì)生效。

錯(cuò)誤寫(xiě)法:

@Service
public class UserService {

    @Transactional
    public void addUser(User user) {
        userMapper.insert(user);
        throw new RuntimeException("模擬異常");
    }

    public void process(User user) {
        // 自調(diào)用,繞過(guò)代理
        this.addUser(user);
    }
}

正確寫(xiě)法(通過(guò)代理調(diào)用):

@Service
public class UserService {

    @Transactional
    public void addUser(User user) {
        userMapper.insert(user);
        throw new RuntimeException("模擬異常");
    }
}

@Service
public class OrderService {

    @Autowired
    private UserService userService;

    public void process(User user) {
        // 通過(guò)Spring代理調(diào)用,事務(wù)才生效
        userService.addUser(user);
    }
}

3.3 異常被捕獲“吃掉”

如果你在方法中捕獲了異常,并且沒(méi)有重新拋出,那么 Spring 的事務(wù)攔截器就感知不到異常,自然不會(huì)觸發(fā)回滾

錯(cuò)誤寫(xiě)法:

@Transactional
public void saveUser(User user) {
    try {
        userMapper.insert(user);
        int i = 1 / 0; // 異常
    } catch (Exception e) {
        System.out.println("異常被吃掉");
    }
}

正確寫(xiě)法(繼續(xù)拋出):

@Transactional
public void saveUser(User user) {
    try {
        userMapper.insert(user);
        int i = 1 / 0;
    } catch (Exception e) {
        throw new RuntimeException("拋出異常,保證回滾", e);
    }
}

3.4 異常類(lèi)型不匹配

默認(rèn)情況下,只有 RuntimeExceptionError 會(huì)觸發(fā)回滾。如果你拋出了 IOException, SQLException受檢異常,事務(wù)依然會(huì)提交

錯(cuò)誤寫(xiě)法:

@Transactional
public void saveUser(User user) throws IOException {
    userMapper.insert(user);
    throw new IOException("檢查型異常");
}

正確寫(xiě)法:

@Transactional(rollbackFor = Exception.class)
public void saveUser(User user) throws IOException {
    userMapper.insert(user);
    throw new IOException("檢查型異常");
}

3.5 數(shù)據(jù)庫(kù)引擎不支持事務(wù)

以 MySQL 為例:InnoDB 支持事務(wù)MyISAM 不支持事務(wù)

如果表使用了 MyISAM,即使 @Transactional 生效,也不會(huì)回滾

-- 檢查并修改表引擎
SHOW TABLE STATUS LIKE ‘your_table_name';
ALTER TABLE your_table_name ENGINE = InnoDB;

3.6 在異步方法中使用

@Async 標(biāo)記的異步方法中,操作是在一個(gè)新的線(xiàn)程中執(zhí)行的。此時(shí)的事務(wù)上下文和新線(xiàn)程的事務(wù)上下文是不同的,需要配置特殊的事務(wù)管理器來(lái)支持,否則容易失效

// 需要特殊配置的場(chǎng)景
@Service
public class AsyncService {

    @Async
    @Transactional // 普通配置下,此事務(wù)很可能失效
    public void asyncTask() {
        // 異步事務(wù)操作
    }
}

3.7 未被Spring容器管理

這也是一些小伙伴粗心容易犯的錯(cuò),類(lèi)沒(méi)有加上 @Service, @Component 等注解,它就不會(huì)被 Spring 掃描并創(chuàng)建為 Bean,那么它上面的 @Transactional 注解自然無(wú)效

// 錯(cuò)誤示例:只是一個(gè)普通類(lèi),不是Spring Bean
// @Service 注釋掉了
public class MyService {

    @Transactional // 這個(gè)注解完全沒(méi)用
    public void doSomething() {
        // ...
    }
}

3.8 propagation 參數(shù)設(shè)置錯(cuò)誤

錯(cuò)誤地設(shè)置傳播行為可能導(dǎo)致意外情況。例如,在已經(jīng)存在事務(wù)的方法中,以 NOT_SUPPORTEDNEVER 模式調(diào)用新方法

// 可能非預(yù)期的行為
@Service
public class MainService {

    @Transactional
    public void mainMethod() {
        // ... 主邏輯
        subService.subMethod(); // 子方法掛起了當(dāng)前事務(wù)
    }
}

@Service
public class SubService {

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void subMethod() {
        // 此方法以非事務(wù)方式運(yùn)行,即使操作失敗,也不影響mainMethod的事務(wù)
        // 如果這里的操作需要原子性,那就出了問(wèn)題
    }
}

4. 結(jié)語(yǔ)

小伙伴們通過(guò)本文的講解,是否讓你對(duì)Spring事務(wù)@Transactional注解有了一個(gè)更深的認(rèn)識(shí)?實(shí)際上我們大部分開(kāi)發(fā)場(chǎng)景都使用默認(rèn)配置即可,同時(shí)建議都按照 @Transactional(rollbackFor = Exception.class) 進(jìn)行注解,業(yè)務(wù)處理過(guò)程避免拋出受檢異常IOException, SQLException 等,導(dǎo)致失效!

到此這篇關(guān)于Spring事務(wù)@Transactional失效的8大場(chǎng)景與解決方法的文章就介紹到這了,更多相關(guān)Spring事務(wù)@Transactional失效內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

菏泽市| 仙桃市| 繁峙县| 佛山市| 宝鸡市| 阳曲县| 祥云县| 兰州市| 张家川| 霍林郭勒市| 望奎县| 桦甸市| 宁安市| 建昌县| 牡丹江市| 民县| 苍南县| 克什克腾旗| 山东| 尼勒克县| 东乌珠穆沁旗| 都昌县| 镇平县| 安福县| 东山县| 扶余县| 西和县| 大姚县| 株洲县| 黑龙江省| 连山| 长泰县| 玉溪市| 鄄城县| 威信县| 定远县| 南华县| 德化县| 陆川县| 东光县| 德保县|