国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Spring事務(wù)失效的九大場(chǎng)景,你踩過幾個(gè)?

開發(fā) 前端
本文簡(jiǎn)要闡述了 Spring 事務(wù)的實(shí)現(xiàn)原理,并列出了 9 種 Spring 事務(wù)失效的場(chǎng)景。相信很多朋友可能都遇到過這些問題。文章也詳細(xì)解釋了失效的原因,希望大家對(duì) Spring 事務(wù)有新的理解。

前言

在日常開發(fā)中,我們經(jīng)常使用Spring事務(wù)。最近,一個(gè)朋友去面試,被問到了這樣一個(gè)面試題:在什么情況下,Spring 事務(wù)會(huì)失效?

今天,我將和大家聊聊Spring事務(wù)失效的 9 種場(chǎng)景。

1. 拋出檢查異常(checked exceptions)

例如,你的事務(wù)控制代碼如下:

@Transactional
public void transactionTest() throws IOException {
    User user = new User();
    UserService.insert(user);
    throw new IOException();
}

如果沒有特別指定@Transactional,Spring 默認(rèn)只會(huì)在遇到運(yùn)行時(shí)異常RuntimeException或錯(cuò)誤時(shí)回滾,而檢查異常如IOException不會(huì)觸發(fā)回滾。

public boolean rollbackOn(Throwable ex) {
    return (ex instanceof RuntimeException || ex instanceof Error);
}

解決方案:

知道原因后,解決方案也很簡(jiǎn)單。配置rollbackFor屬性,例如:@Transactional(rollbackFor = Exception.class)

@Transactional(rollbackFor = Exception.class)
public void transactionTest() throws IOException {
    User user = new User();
    UserService.insert(user);
    throw new IOException();
}

2. 業(yè)務(wù)方法本身捕獲并處理了異常

@Transactional(rollbackFor = Exception.class)
public void transactionTest() {
    try {
        User user = new User();
        UserService.insert(user);
        int i = 1 / 0;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

在這個(gè)場(chǎng)景中,事務(wù)失效的原因也很簡(jiǎn)單。Spring 是否回滾事務(wù)取決于你是否拋出了異常。如果你自己捕獲了異常,Spring 就無法處理事務(wù)了。

看了上面的代碼,你可能會(huì)覺得這么簡(jiǎn)單的問題,自己不可能犯這種低級(jí)錯(cuò)誤。但我想告訴你,我身邊幾乎有一半的人都曾因此困擾過。

在編寫業(yè)務(wù)代碼時(shí),代碼可能會(huì)更復(fù)雜,有很多嵌套的方法。稍不注意,就很容易觸發(fā)這個(gè)問題。舉個(gè)簡(jiǎn)單的例子,假設(shè)你有一個(gè)審計(jì)功能,每次方法執(zhí)行完后,將審計(jì)結(jié)果保存到數(shù)據(jù)庫(kù)中。那么代碼可能會(huì)寫成這樣:

@Service
public class TransactionService {
    @Transactional(rollbackFor = Exception.class)
    public void transactionTest() throws IOException {
        User user = new User();
        UserService.insert(user);
        throw new IOException();
    }
}

下面的切面會(huì)作用于TransactionService

@Component
publicclass AuditAspect {
    @Autowired
    private AuditService auditService;

    @Around(value = "execution (* com.dylan.service.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            Audit audit = new Audit();
            Signature signature = pjp.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            String[] strings = methodSignature.getParameterNames();
            audit.setMethod(signature.getName());
            audit.setParameters(strings);
            Object proceed = pjp.proceed();
            audit.success(true);
            return proceed;
        } catch (Throwable e) {
            log.error("{}", e);
            audit.success(false);
        }
        auditService.save(audit);
        returnnull;
    }
}

在上面的例子中,如果程序執(zhí)行異常,事務(wù)也會(huì)失效。原因是Spring的事務(wù)切面優(yōu)先級(jí)最低。如果異常被切面捕獲,Spring 自然無法正確處理事務(wù),因?yàn)槭聞?wù)管理器無法捕獲到異常。

解決方案:

只需移除try-catch。雖然我們知道在處理事務(wù)時(shí),業(yè)務(wù)代碼不能自己捕獲異常,但只要代碼變得復(fù)雜,我們很容易不小心犯錯(cuò)。

3. 同一個(gè)類中的方法調(diào)用

@Service
publicclass DefaultTransactionService implements Service {

    public void saveUser() throws Exception {
        // do something
        doInsert();
    }

    @Transactional(rollbackFor = Exception.class)
    public void doInsert() throws IOException {
        User user = new User();
        UserService.insert(user);
        thrownew IOException();
    }
}

這也是一個(gè)容易出錯(cuò)的場(chǎng)景。事務(wù)失效的原因也很簡(jiǎn)單。因?yàn)?Spring 的事務(wù)管理功能是通過動(dòng)態(tài)代理實(shí)現(xiàn)的,而 Spring 默認(rèn)使用 JDK 動(dòng)態(tài)代理,JDK 動(dòng)態(tài)代理通過接口實(shí)現(xiàn),并通過反射調(diào)用目標(biāo)類。簡(jiǎn)單理解,在saveUser()方法中,調(diào)用this.doInsert()時(shí),this是真實(shí)對(duì)象,因此會(huì)直接執(zhí)行doInsert的業(yè)務(wù)邏輯,而不是代理邏輯,從而導(dǎo)致事務(wù)失效。

解決方案:

方案 1:直接在saveUser方法上添加@Transactional注解。

方案 2:可以將這兩個(gè)方法拆分到不同的類中。

方案 3:不使用注解實(shí)現(xiàn)事務(wù),而是使用編程式事務(wù)來包裹需要開啟事務(wù)的代碼塊。例如:transactionTemplate.execute()

public void doInsert() throws IOException {
    transactionTemplate.execute(() -> {
        User user = new User();
        UserService.insert(user);
        throw new IOException();
    });
}

4. 方法使用了finalstatic關(guān)鍵字

如果 Spring 使用 Cglib 代理實(shí)現(xiàn)(當(dāng)你的代理類沒有實(shí)現(xiàn)接口時(shí)),而你的業(yè)務(wù)方法恰好使用了finalstatic關(guān)鍵字,那么事務(wù)控制也會(huì)失效。因?yàn)?Cglib 使用字節(jié)碼增強(qiáng)技術(shù)生成被代理類的子類,并重寫被代理類的方法來實(shí)現(xiàn)代理。如果被代理的方法使用了finalstatic關(guān)鍵字,子類就無法重寫被代理的方法。

如果 Spring 使用 JDK 動(dòng)態(tài)代理實(shí)現(xiàn),JDK 動(dòng)態(tài)代理是基于接口實(shí)現(xiàn)的,那么被finalstatic修飾的方法也無法被代理。

總之,如果方法連代理都沒有,那么事務(wù)回滾肯定無法實(shí)現(xiàn)。

解決方案:

盡量移除方法上的finalstatic關(guān)鍵字。

5. 方法不是public

如果方法不是public,Spring 事務(wù)也會(huì)失效,因?yàn)樵?Spring 事務(wù)管理的源碼AbstractFallbackTransactionAttributeSource中,computeTransactionAttribute()方法會(huì)判斷目標(biāo)方法是否是public。如果不是public,則返回null

// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
    return null;
}

解決方案:

將當(dāng)前方法的訪問級(jí)別改為public

6. 傳播機(jī)制使用不當(dāng)

Spring事務(wù)的傳播機(jī)制指的是當(dāng)多個(gè)事務(wù)方法相互調(diào)用時(shí),事務(wù)應(yīng)該如何傳播的策略。Spring提供了七種事務(wù)傳播機(jī)制:REQUIREDSUPPORTSMANDATORYREQUIRES_NEWNOT_SUPPORTEDNEVERNESTED。如果你不了解這些傳播策略的原理,很容易導(dǎo)致事務(wù)失效。

@Service
publicclass TransactionsService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private AddressMapper addressMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void doInsert(User user, Address address) throws Exception {
        // do something
        userMapper.insert(user);
        saveAddress(address);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveAddress(Address address) {
        // do something
        addressMapper.insert(address);
    }
}

在上面的例子中,如果用戶插入失敗,不會(huì)導(dǎo)致saveAddress()回滾,因?yàn)檫@里使用的傳播機(jī)制是REQUIRES_NEWREQUIRES_NEW的原理是,如果當(dāng)前方法沒有事務(wù),則創(chuàng)建一個(gè)新事務(wù)。如果當(dāng)前方法已經(jīng)有事務(wù),則掛起當(dāng)前事務(wù)并創(chuàng)建一個(gè)新事務(wù)。父事務(wù)會(huì)等到當(dāng)前事務(wù)完成后才提交。如果父事務(wù)發(fā)生異常,不會(huì)影響子事務(wù)的提交。

解決方案:

將事務(wù)傳播策略改為默認(rèn)值REQUIREDREQUIRED的原理是,如果當(dāng)前有事務(wù),則加入該事務(wù)。如果沒有事務(wù),則創(chuàng)建一個(gè)新事務(wù)。父事務(wù)和被調(diào)用的事務(wù)處于同一個(gè)事務(wù)中。即使被調(diào)用的事務(wù)捕獲了異常,整個(gè)事務(wù)仍然會(huì)回滾。

7. 沒有被 Spring 管理

// @Service
public class OrderServiceImpl implements OrderService {
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
}

如果此時(shí)@Service注解被注釋掉,這個(gè)類就不會(huì)被 Spring 加載為 Bean,那么這個(gè)類就不會(huì)被 Spring 管理,事務(wù)自然也會(huì)失效。

解決方案:

確保每個(gè)使用事務(wù)注解的Service都被 Spring 管理。

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

@Service
publicclass UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleService roleService;

    @Transactional
    public void add(UserModel userModel) throws Exception {
        userMapper.insertUser(userModel);
        new Thread(() -> {
            try {
                test();
            } catch (Exception e) {
                roleService.doOtherThing();
            }
        }).start();
    }
}

@Service
publicclass RoleService {

    @Transactional
    public void doOtherThing() {
        try {
            int i = 1 / 0;
            System.out.println("save role table data");
        } catch (Exception e) {
            thrownew RuntimeException();
        }
    }
}

我們可以看到,在事務(wù)方法add中,調(diào)用了事務(wù)方法doOtherThing,但doOtherThing是在另一個(gè)線程中被調(diào)用的。

這會(huì)導(dǎo)致兩個(gè)方法不在同一個(gè)線程中,獲取的數(shù)據(jù)庫(kù)連接也不同,因此是兩個(gè)不同的事務(wù)。如果在doOtherThing方法中拋出異常,add方法是不可能回滾的。

我們所說的同一個(gè)事務(wù),實(shí)際上指的是同一個(gè)數(shù)據(jù)庫(kù)連接。只有在同一個(gè)數(shù)據(jù)庫(kù)連接下,才能同時(shí)提交和回滾。如果在不同的線程中,獲取的數(shù)據(jù)庫(kù)連接肯定不同,因此它們是不同的事務(wù)。

解決方案:

這有點(diǎn)像分布式事務(wù)。盡量確保在同一個(gè)事務(wù)中處理。

9. 沒有配置開啟事務(wù)

如果在項(xiàng)目中沒有配置 Spring 的事務(wù)管理器,即使使用了 Spring 的事務(wù)管理功能,Spring 的事務(wù)也不會(huì)生效。例如,如果你是一個(gè) Spring Boot 項(xiàng)目,并且沒有在 Spring Boot 項(xiàng)目中配置以下代碼:

@EnableTransactionManagement

解決方案:

確保在項(xiàng)目中正確配置了事務(wù)管理器。

總結(jié)

本文簡(jiǎn)要闡述了 Spring 事務(wù)的實(shí)現(xiàn)原理,并列出了 9 種 Spring 事務(wù)失效的場(chǎng)景。相信很多朋友可能都遇到過這些問題。文章也詳細(xì)解釋了失效的原因,希望大家對(duì) Spring 事務(wù)有新的理解。

責(zé)任編輯:武曉燕 來源: 程序猿技術(shù)充電站
相關(guān)推薦

2022-04-26 21:49:55

Spring事務(wù)數(shù)據(jù)庫(kù)

2024-04-01 08:05:27

Go開發(fā)Java

2022-02-14 16:53:57

Spring項(xiàng)目數(shù)據(jù)庫(kù)

2022-09-29 09:35:56

線程池

2018-09-11 09:14:52

面試公司缺點(diǎn)

2025-04-29 10:17:42

2019-08-09 15:03:53

2019-09-25 15:30:15

2024-09-09 08:29:25

2024-01-29 08:28:01

Spring事務(wù)失效

2024-05-07 08:23:03

Spring@Async配置

2025-10-30 07:45:06

2021-12-13 11:12:41

Spring事務(wù)失效

2021-09-04 07:56:44

Spring事務(wù)失效

2025-04-15 02:00:00

API版本項(xiàng)目

2023-07-05 08:45:18

Spring事務(wù)失效場(chǎng)景

2025-08-06 00:00:01

事務(wù)失效聲明式

2025-12-01 01:29:00

2023-09-08 08:52:12

Spring注解事務(wù)

2023-08-29 10:51:44

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美三级韩国三级日本三斤| 丝袜美腿亚洲色图| 性感美女久久精品| 亚洲一区二区精品在线| 亚洲欧洲av| 亚洲女成人图区| 一级黄色在线| av电影一区二区| 亚洲精品在线视频观看| 加勒比久久综合| 久久久精品亚洲| 欧美videossex| 日韩欧美国产骚| 捆绑紧缚一区二区三区在线观看| 国产成人福利片| 日本一区二区三区四区高清视频 | 国产日韩欧美激情| 国产精品啪啪啪视频| 夜久久久久久| 国产精品夜色7777狼人| 国产videos久久| 日本最新高清不卡中文字幕| aiss精品大尺度系列| 精品国产区一区二区三区在线观看| 尤物在线网址| 日韩精品中文字幕一区二区三区 | 亚洲免费观看高清完整版在线观| 欧美尺度大的性做爰视频| 99久久久国产精品免费调教网站| 日韩精品视频在线免费观看| 国产在线二区| 精品国产污污免费网站入口 | 成人美女在线视频| 欧美精品卡一卡二| 国产精品天美传媒| 日本视频二区| 欧美丰满嫩嫩电影| av网站网址在线观看| 一区二区三区导航| 免费在线看黄色片| 91网站黄www| 一个人看的www一区| 3d动漫精品啪啪一区二区竹菊| jizzjizz中国精品麻豆| 色综合视频网站| 国产韩国精品一区二区三区| 成人av蜜桃| 91理论电影在线观看| 亚洲欧美一区二区三区在线播放| 91精品一区二区三区久久久久久| 免费一级欧美片在线观看网站| 国产精品极品美女在线观看免费| 亚洲一区中文| 男人插女人下面免费视频| 五月天中文字幕一区二区| 欧美24videosex性欧美| 神马久久久久久| 影音先锋日韩在线| 国产自偷自偷免费一区| 欧美videofree性高清杂交| 精品国产一区一区二区三亚瑟| 五月天丁香综合久久国产 | 香蕉视频禁止18| 欧美成人vr18sexvr| 天堂一区二区三区四区| 天天成人综合网| 91麻豆精品国产自产在线观看一区| 国产精品久久免费视频| 亚洲精品在线免费| 91精品国产入口| 日韩啪啪电影网| 激情婷婷综合网| 亚洲国产日韩一区| 在线视频观看日韩| 国产91色在线|免| 一色屋精品亚洲香蕉网站| 国产成人精品一区二区三区视频| 999精品在线观看| 精品高清一区二区三区| 色先锋久久影院av| 波多野结衣天堂| 国产成人高清激情视频在线观看| 久久美女艺术照精彩视频福利播放| 忘忧草在线影院两性视频| 久久久久久99| 日韩黄在线观看| 精品制服美女丁香| 亚洲精品一级二级| 免费无码国产v片在线观看| 亚洲欧洲日产国产网站| 国产精品1区2区3区在线观看| 成人国产电影在线观看| 色综合电影网| 伊人伊成久久人综合网站 | 日韩精品不卡| 久久久999精品免费| 国产午夜亚洲精品午夜鲁丝片| 久久久久久久久久久久久久久久久久久久 | 国产成人精品午夜| 疯狂欧美牲乱大交777| 视频一区二区不卡| 亚洲综合视频| 国产福利电影在线观看| 97人人模人人爽人人少妇| 欧美丰满一区二区免费视频| 人人精品人人爱| 亚洲欧美日本国产| 国产精品一二三区视频| 中文字幕免费在线不卡| 久久视频在线直播| 欧美精品中文| 大片免费在线观看| 国产视频在线视频| 成人黄色在线播放| 亚洲精品视频二区| 一区二区三区鲁丝不卡| 国产一本一道久久香蕉| 蜜桃a∨噜噜一区二区三区| 麻豆最新免费在线视频| aaa大片免费观看| 日韩精品免费一区| 波多野结衣精品久久| 久久夜色精品国产欧美乱| 在线视频你懂得一区二区三区| 91麻豆.com| 国产一区三区三区| 亚洲视频狠狠| 欧美一区二区三区激情视频| 成人在线爆射| 成人高清免费在线播放| 色婷婷综合久久久久中文字幕| 欧美亚视频在线中文字幕免费| 青春有你2免费观看完整版在线播放高清 | 毛片一区二区| 林ゆな中文字幕一区二区| 97精品国产99久久久久久免费| 免费在线观看av| 嫩草精品影院| 日本电影一区二区在线观看| 涩涩视频免费网站| siro系绝美精品系列| 色总=综合色| 日本999视频| 成人3d动漫一区二区三区| 日韩一级特黄毛片| 中文字幕色一区二区| 一区二区三区四区欧美| 蜜桃久久精品乱码一区二区| 久久精彩视频| 日本精品国语自产拍在线观看| 狼狼综合久久久久综合网| 日本欧美色综合网站免费| 国产伦精品一区二区三区视频黑人 | 国产精品麻豆99久久久久久| 久久精品一区蜜桃臀影院| 国产精品天美传媒| 亚洲v中文字幕| 91精品久久久久久久91蜜桃| 日韩免费看网站| www.日韩av.com| 欧美在线视频a| 国产日韩一区欧美| 国产精品啪啪啪视频| 黄a大片av永久免费| 一起操在线观看| 在线观看日韩精品| 久久久亚洲网站| 爱福利视频一区| 色综合久久久久久中文网| 欧美精品亚州精品| 91亚洲精品视频| 国产一级精品aaaaa看| 国产精品视频免费一区二区三区| 91在线无精精品一区二区| 欧美一区亚洲二区| 先锋在线亚洲| 日韩精品系列| 91精品国产综合久久久久久豆腐| 亚洲插插视频| 精品免费在线| 久热综合在线亚洲精品| 国产精品国产三级国产a| 午夜视频一区在线观看| 亚洲欧美变态国产另类| 午夜精品一区二区三区在线播放| 国产一区福利视频| 亚洲乱码中文字幕久久孕妇黑人| 97人澡人人添人人爽欧美| 在线看的av网站| 在线国产视频| 精品久久久久久久久久久下田 | 久久久com| www欧美xxxx| 久久久久久一二三区| zzzwww在线看片免费| yellow在线观看网址| 欧美人xxx| 亚洲欧美春色| 国产精品jizz在线观看美国| 在线视频精品|