在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

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

8個(gè)Spring事務(wù)失效的場(chǎng)景介紹

jf_78858299 ? 來(lái)源:JAVA旭陽(yáng) ? 作者:JAVA旭陽(yáng) ? 2023-05-11 10:41 ? 次閱讀

前言

作為Java開(kāi)發(fā)工程師,相信大家對(duì)Spring種事務(wù)的使用并不陌生。但是你可能只是停留在基礎(chǔ)的使用層面上,在遇到一些比較特殊的場(chǎng)景,事務(wù)可能沒(méi)有生效,直接在生產(chǎn)上暴露了,這可能就會(huì)導(dǎo)致比較嚴(yán)重的生產(chǎn)事故。今天,我們就簡(jiǎn)單來(lái)說(shuō)下Spring事務(wù)的原理,然后總結(jié)一下spring事務(wù)失敗的場(chǎng)景,并提出對(duì)應(yīng)的解決方案。

Spring事務(wù)原理

大家還記得在JDBC中是如何操作事務(wù)的嗎?偽代碼可能如下:

//Get database connection
Connection connection = DriverManager.getConnection();
//Set autoCommit is false
connection.setAutoCommit(false);
//use sql to operate database
.........
//Commit or rollback
connection.commit()/connection.rollback

connection.close();

需要在各個(gè)業(yè)務(wù)代碼中編寫代碼如commit()close()來(lái)控制事務(wù)。

但是Spring不樂(lè)意這么干了,這樣對(duì)業(yè)務(wù)代碼侵入性太大了,所有就用一個(gè)事務(wù)注解@Transactional來(lái)控制事務(wù),底層實(shí)現(xiàn)是基于切面編程AOP實(shí)現(xiàn)的,而Spring中實(shí)現(xiàn)AOP機(jī)制采用的是動(dòng)態(tài)代理,具體分為JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理兩種模式。圖片

  1. Springbean的初始化過(guò)程中,發(fā)現(xiàn)方法有Transactional注解,就需要對(duì)相應(yīng)的Bean進(jìn)行代理,生成代理對(duì)象。
  2. 然后在方法調(diào)用的時(shí)候,會(huì)執(zhí)行切面的邏輯,而這里切面的邏輯中就包含了開(kāi)啟事務(wù)、提交事務(wù)或者回滾事務(wù)等邏輯。

另外注意一點(diǎn)的是,Spring 本身不實(shí)現(xiàn)事務(wù),底層還是依賴于數(shù)據(jù)庫(kù)的事務(wù)。沒(méi)有數(shù)據(jù)庫(kù)事務(wù)的支持,Spring事務(wù)是不會(huì)生效的。

接下來(lái)我們進(jìn)入正題,看看哪些場(chǎng)景會(huì)導(dǎo)致Spring事務(wù)失敗。

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

1. 拋出檢查異常

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

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

如果@Transactional 沒(méi)有特別指定,Spring 只會(huì)在遇到運(yùn)行時(shí)異常RuntimeException或者error時(shí)進(jìn)行回滾,而IOException等檢查異常不會(huì)影響回滾。

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

解決方案:

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

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();
    }
}

這種場(chǎng)景下,事務(wù)失敗的原因也很簡(jiǎn)單,Spring是否進(jìn)行回滾是根據(jù)你是否拋出異常決定的,所以如果你自己捕獲了異常,Spring 也無(wú)能為力。

看了上面的代碼,你可能認(rèn)為這么簡(jiǎn)單的問(wèn)題你不可能犯這么愚蠢的錯(cuò)誤,但是我想告訴你的是,我身邊幾乎一半的人都被這一幕困擾過(guò)。

寫業(yè)務(wù)代碼的時(shí)候,代碼可能比較復(fù)雜,嵌套的方法很多。如果你不小心,很可能會(huì)觸發(fā)此問(wèn)題。舉一個(gè)非常簡(jiǎn)單的例子,假設(shè)你有一個(gè)審計(jì)功能。每個(gè)方法執(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();

    }
}

@Component
public class AuditAspect {

	@Autowired
	private auditService auditService;

    @Around(value = "execution (* com.alvin.*.*(..))")
    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);
        return null;
    }

}

在上面的示例中,事務(wù)將失敗。原因是Spring的事務(wù)切面優(yōu)先級(jí)最低,所以如果異常被切面捕獲,Spring自然不能正常處理事務(wù),因?yàn)槭聞?wù)管理器無(wú)法捕獲異常。

解決方案:

看,雖然我們知道在處理事務(wù)時(shí)業(yè)務(wù)代碼不能自己捕獲異常,但是只要代碼變得復(fù)雜,我們就很可能再次出錯(cuò),所以我們?cè)谔幚硎聞?wù)的時(shí)候要小心,還是不要使用聲明式事務(wù), 并使用編程式事務(wù)— transactionTemplate.execute()

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

@Service
public class DefaultTransactionService implement 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);
        throw new IOException();

    }
}

這也是一個(gè)容易出錯(cuò)的場(chǎng)景。事務(wù)失敗的原因也很簡(jiǎn)單,因?yàn)?code>Spring的事務(wù)管理功能是通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)的,而Spring默認(rèn)使用JDK動(dòng)態(tài)代理,而JDK動(dòng)態(tài)代理采用接口實(shí)現(xiàn)的方式,通過(guò)反射調(diào)用目標(biāo)類。簡(jiǎn)單理解,就是saveUser()方法中調(diào)用this.doInsert(),這里的this是被真實(shí)對(duì)象,所以會(huì)直接走doInsert的業(yè)務(wù)邏輯,而不會(huì)走切面邏輯,所以事務(wù)失敗。

解決方案:

方案一 :解決方法可以是直接在啟動(dòng)類中添加@Transactional注解saveUser()

方案二@EnableAspectJAutoProxy(exposeProxy = true)在啟動(dòng)類中添加,會(huì)由Cglib代理實(shí)現(xiàn)。

4. 方法使用 final 或 static關(guān)鍵字

如果Spring使用了Cglib代理實(shí)現(xiàn)(比如你的代理類沒(méi)有實(shí)現(xiàn)接口),而你的業(yè)務(wù)方法恰好使用了final或者static關(guān)鍵字,那么事務(wù)也會(huì)失敗。更具體地說(shuō),它應(yīng)該拋出異常,因?yàn)?code>Cglib使用字節(jié)碼增強(qiáng)技術(shù)生成被代理類的子類并重寫被代理類的方法來(lái)實(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ú)法被代理。

總而言之,方法連代理都沒(méi)有,那么肯定無(wú)法實(shí)現(xiàn)事務(wù)回滾了。

解決方案:

想辦法去掉final或者static關(guān)鍵字

5. 方法不是public

如果方法不是publicSpring事務(wù)也會(huì)失敗,因?yàn)?code>Spring的事務(wù)管理源碼AbstractFallbackTransactionAttributeSource中有判斷computeTransactionAttribute()。如果目標(biāo)方法不是公共的,則TransactionAttribute返回null

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

解決方案:

是將當(dāng)前方法訪問(wèn)級(jí)別更改為public

6. 錯(cuò)誤使用傳播機(jī)制

Spring事務(wù)的傳播機(jī)制是指在多個(gè)事務(wù)方法相互調(diào)用時(shí),確定事務(wù)應(yīng)該如何傳播的策略。Spring提供了七種事務(wù)傳播機(jī)制:REQUIREDSUPPORTSMANDATORYREQUIRES_NEWNOT_SUPPORTEDNEVERNESTED。如果不知道這些傳播策略的原理,很可能會(huì)導(dǎo)致交易失敗。

@Service
public class TransactionService {


    @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)檫@里使用的傳播是REQUIRES_NEW,傳播機(jī)制REQUIRES_NEW的原理是如果當(dāng)前方法中沒(méi)有事務(wù),就會(huì)創(chuàng)建一個(gè)新的事務(wù)。如果一個(gè)事務(wù)已經(jīng)存在,則當(dāng)前事務(wù)將被掛起,并創(chuàng)建一個(gè)新事務(wù)。在當(dāng)前事務(wù)完成之前,不會(huì)提交父事務(wù)。如果父事務(wù)發(fā)生異常,則不影響子事務(wù)的提交。

事務(wù)的傳播機(jī)制說(shuō)明如下:

  • REQUIRED 如果當(dāng)前上下文中存在事務(wù),那么加入該事務(wù),如果不存在事務(wù),創(chuàng)建一個(gè)事務(wù),這是默認(rèn)的傳播屬性值。
  • SUPPORTS 如果當(dāng)前上下文存在事務(wù),則支持事務(wù)加入事務(wù),如果不存在事務(wù),則使用非事務(wù)的方式執(zhí)行。
  • MANDATORY 如果當(dāng)前上下文中存在事務(wù),否則拋出異常。
  • REQUIRES_NEW 每次都會(huì)新建一個(gè)事務(wù),并且同時(shí)將上下文中的事務(wù)掛起,執(zhí)行當(dāng)前新建事務(wù)完成以后,上下文事務(wù)恢復(fù)再執(zhí)行。
  • NOT_SUPPORTED 如果當(dāng)前上下文中存在事務(wù),則掛起當(dāng)前事務(wù),然后新的方法在沒(méi)有事務(wù)的環(huán)境中執(zhí)行。
  • NEVER 如果當(dāng)前上下文中存在事務(wù),則拋出異常,否則在無(wú)事務(wù)環(huán)境上執(zhí)行代碼。
  • NESTED 如果當(dāng)前上下文中存在事務(wù),則嵌套事務(wù)執(zhí)行,如果不存在事務(wù),則新建事務(wù)。

解決方案 :

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

7. 沒(méi)有被Spring管理

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

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

解決方案 :

需要保證每個(gè)事務(wù)注解的每個(gè)Bean被Spring管理。

8. 多線程

@Service
public class 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
public class RoleService {

    @Transactional
    public void doOtherThing() {
         try {
             int i = 1/0;
             System.out.println("保存role表數(shù)據(jù)");
         }catch (Exception e) {
            throw new RuntimeException();
        }
    }
}

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

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

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

解決方案:

這里就有點(diǎn)分布式事務(wù)的感覺(jué)了,盡量還是保證在同一個(gè)事務(wù)中處理。

總結(jié)

本文簡(jiǎn)單闡述了下Spring中事務(wù)實(shí)現(xiàn)的原理,同時(shí)列舉了8種Spring事務(wù)失敗的場(chǎng)景,相信很多朋友可能都遇到過(guò), 失敗的原因也有詳細(xì)說(shuō)明。希望大家對(duì)Spring事務(wù)有一個(gè)新的認(rèn)識(shí)。

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

    關(guān)注

    19

    文章

    2967

    瀏覽量

    104749
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    340

    瀏覽量

    14343
  • 開(kāi)發(fā)工程師

    關(guān)注

    1

    文章

    91

    瀏覽量

    14934
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Spring事務(wù)失效的十種常見(jiàn)場(chǎng)景

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

    Spring事務(wù)實(shí)現(xiàn)原理

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

    什么是java spring

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

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

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

    Spring事務(wù)分析的實(shí)現(xiàn)方式

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

    詳解Spring事務(wù)管理

    在學(xué)習(xí)spring事務(wù)管理時(shí),我忍不住要問(wèn),spring為什么進(jìn)行事務(wù)管理,spring怎么進(jìn)行的事務(wù)
    發(fā)表于 07-12 06:54

    Spring事務(wù)管理詳解說(shuō)明

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

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

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

    Spring認(rèn)證是什么?

    ,例如:配置、組件掃描、AOP、數(shù)據(jù)訪問(wèn)和事務(wù)、REST、安全、自動(dòng)配置、執(zhí)行器、 Spring boot測(cè)試等。
    的頭像 發(fā)表于 07-04 10:19 ?1318次閱讀
    <b class='flag-5'>Spring</b>認(rèn)證是什么?

    淺談Spring事務(wù)的那些坑

    對(duì)于從事java開(kāi)發(fā)工作的同學(xué)來(lái)說(shuō),spring事務(wù)肯定再熟悉不過(guò)了。在某些業(yè)務(wù)場(chǎng)景下,如果同時(shí)有多張表的寫入操作,為了保證操作的原子性(要么同時(shí)成功,要么同時(shí)失敗)避免數(shù)據(jù)不一致的情況,我們一般都會(huì)使用
    的頭像 發(fā)表于 10-11 10:31 ?751次閱讀

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

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

    淺談Spring事務(wù)底層原理

    開(kāi)啟Spring事務(wù)本質(zhì)上就是增加了一個(gè)Advisor,但我們使用@EnableTransactionManagement注解來(lái)開(kāi)啟Spring事務(wù)
    的頭像 發(fā)表于 12-06 09:56 ?695次閱讀

    Spring事務(wù)在哪幾種情況下會(huì)不生效?

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

    spring事務(wù)失效的一些場(chǎng)景

    對(duì)于從事java開(kāi)發(fā)工作的同學(xué)來(lái)說(shuō),spring事務(wù)肯定再熟悉不過(guò)了。 在某些業(yè)務(wù)場(chǎng)景下,如果一個(gè)請(qǐng)求中,需要同時(shí)寫入多張表的數(shù)據(jù)。為了保證操作的原子性(要么同時(shí)成功,要么同時(shí)失敗)
    的頭像 發(fā)表于 10-08 14:27 ?447次閱讀
    <b class='flag-5'>spring</b><b class='flag-5'>事務(wù)</b><b class='flag-5'>失效</b>的一些<b class='flag-5'>場(chǎng)景</b>

    Spring事務(wù)傳播性的相關(guān)知識(shí)

    本文主要介紹Spring事務(wù)傳播性的相關(guān)知識(shí)。
    的頭像 發(fā)表于 01-10 09:29 ?447次閱讀
    <b class='flag-5'>Spring</b><b class='flag-5'>事務(wù)</b>傳播性的相關(guān)知識(shí)
    主站蜘蛛池模板: 欧美乱乱| 月夜免费观看完整视频| 亚洲国产成人在线| 狠狠色丁香久久综合网| 人人玩人人弄人人曰| 精品国内视频| 奇米影视四色7777久久精品| 日本免费在线一区| 欧美一欧美一区二三区性| 午夜片在线观看| 人人干人| 最新黄色大片| 久久精品视频观看| a毛片免费观看完整| 午夜免费啪啪| 最近2018中文字幕免费看2019| 女人张开腿 让男人桶视频| 国产精品虐乳在线播放| 天天做天天爱天天一爽一毛片| 68日本xxxxxxx18| 亚洲wuma| 免费的毛片| 老湿成人影院| 亚洲国产婷婷香蕉久久久久久| 国产女人在线视频| 久久天天躁夜夜躁狠狠| 在线天堂视频| bt种子搜索在线| 欧美日本免费| 国产麻豆成人传媒免费观看| 欧美性区| 美国色网站| 98色花堂永久地址国产精品| 色视频免费国产观看| 天天操天天添| 性欧美高清强烈性视频| 夜夜骑首页| 中文字幕11页| 自拍中文字幕| 在线三级网| 一级特黄a视频|