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

Spring詳細(xì)講解事務(wù)失效的場(chǎng)景

 更新時(shí)間:2022年07月12日 11:34:27   作者:少年.  
實(shí)際項(xiàng)目開(kāi)發(fā)中,如果涉及到多張表操作時(shí),為了保證業(yè)務(wù)數(shù)據(jù)的一致性,大家一般都會(huì)采用事務(wù)機(jī)制,好多小伙伴可能只是簡(jiǎn)單了解一下,遇到事務(wù)失效的情況,便會(huì)無(wú)從下手,下面這篇文章主要給大家介紹了關(guān)于Spring事務(wù)失效場(chǎng)景的相關(guān)資料,需要的朋友可以參考下

1)未被Spring管理

使用Spring事務(wù)的前提是:對(duì)象要被Spring管理,事務(wù)方法所在的類要被加載為bean對(duì)象

如果事務(wù)方法所在的類沒(méi)有被加載為一個(gè)bean,那么事務(wù)自然就失效了,示例:

//@Service
public class UserServiceImpl {
    @Transactional
    public void doTest() {
        // 業(yè)務(wù)代碼
    }
}

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

以MySQL為例,InnoDB引擎是支持事務(wù)的,而像MyISAM、MEMORY等是不支持事務(wù)的。

從MySQL5.5.5開(kāi)始默認(rèn)的存儲(chǔ)引擎是InnoDB,之前默認(rèn)都是MyISAM。所以在開(kāi)發(fā)過(guò)程中發(fā)現(xiàn)事務(wù)失效,不一定是Spring的鍋,最好確認(rèn)一下數(shù)據(jù)庫(kù)表是否支持事務(wù)。

3)事務(wù)方法沒(méi)有被public修飾

眾所周知,java的訪問(wèn)權(quán)限修飾符有:private、default、protectedpublic四種,

但是@Transactional注解只能作用于public修飾的方法上,

AbstractFallbackTransactionAttributeSource類(Spring通過(guò)這個(gè)類獲取@Transactional注解的配置屬性信息)的computeTransactionAttribute方法中有個(gè)判斷,如果目標(biāo)方法不是public,則TransactionAttribute返回null,即不支持事務(wù)。

@Nullable
	protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
		// Don't allow no-public methods as required.
		if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
			return null;
		}
        //………………
	}

其實(shí)想想動(dòng)態(tài)代理的原理就很好理解了,動(dòng)態(tài)代理是通過(guò)實(shí)現(xiàn)接口或者繼承來(lái)實(shí)現(xiàn)的,所以目標(biāo)方法必須是public修飾,并且不能是final修飾。

4)方法使用final修飾

如果一個(gè)方法不想被子類重寫,那么我們就可以把他寫成final修飾的方法

如果事務(wù)方法使用final修飾,那么aop就無(wú)法在代理類中重寫該方法,事務(wù)就不會(huì)生效

同樣的,static修飾的方法也無(wú)法通過(guò)代理變成事務(wù)方法

5)同一類中方法調(diào)用

假如在某個(gè)Service的方法中,調(diào)用了另外一個(gè)事務(wù)方法:

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    public void del(){
        doTest();
    }
    @Transactional
    public void doTest() {
        userMapper.deleteById(200108);
        int i = 10/0; //模擬發(fā)生異常
    }
}

像上面的代碼,doTest方法使用@Transactional注解標(biāo)注,在del()方法中調(diào)用了doTest()方法,在外部調(diào)用del()方法時(shí),事務(wù)也不會(huì)生效,因?yàn)檫@里del()方法中調(diào)用的是類本身的方法,而不是代理對(duì)象的方法。

那么如果確實(shí)有這樣的需求怎么辦呢?

引入自身bean

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Autowired
    UserServiceImpl userServiceImpl;
    public void del(){
        userServiceImpl.doTest();
    }
    @Transactional
    public void doTest() {
        userMapper.deleteById(200112);
        int i = 10/0; //模擬發(fā)生異常
    }
}

通過(guò)ApplicationContext引入bean

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Autowired
    ApplicationContext applicationContext;
    public void del(){
       ((UserServiceImpl)applicationContext.getBean("userServiceImpl")).doTest();
    }
    @Transactional
    public void doTest() {
        userMapper.deleteById(200112);
        int i = 10/0; //模擬發(fā)生異常
    }
}

通過(guò)AopContext獲取當(dāng)前代理類

在啟動(dòng)類上添加注解@EnableAspectJAutoProxy(exposeProxy = true),表示是否對(duì)外暴露代理對(duì)象,即是否可以獲取AopContext

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Autowired
    ApplicationContext applicationContext;
    public void del(){
        ((UserServiceImpl)AopContext.currentProxy()).doTest();
    }
    @Transactional
    public void doTest() {
        userMapper.deleteById(200112);
        int i = 10/0; //模擬發(fā)生異常
    }
}

6)未開(kāi)啟事務(wù)

如果是SpringBoot項(xiàng)目,那么SpringBoot通過(guò)DataSourceTransactionManagerAutoConfiguration自動(dòng)配置類幫我們開(kāi)啟了事務(wù)。

如果是傳統(tǒng)的Spring項(xiàng)目,則需要我們自己配置

<!--        配置事務(wù)管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>
<!--        配置事務(wù)通知-->
<tx:advice id="Advice" transaction-manager="transactionManager">
  <!--                配置事務(wù)屬性,即哪些方法要執(zhí)行事務(wù)-->
  <tx:attributes>
    <tx:method name="insert*" propagation="REQUIRED"/> <!-- 所有insert開(kāi)頭的方法,以下同理 -->
    <tx:method name="update*" propagation="REQUIRED"/>
    <tx:method name="delete*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>
<!--        配置事務(wù)切面-->
<aop:config>
  <aop:pointcut id="AdviceAop" expression="execution(* com.yy.service..*(..))"/> <!--要執(zhí)行的方法在哪個(gè)包,意思為:com.yy.service下的所有包里面的包含任意參數(shù)的所有方法-->
  <aop:advisor advice-ref="Advice" pointcut-ref="AdviceAop"/> <!-- 配置為AdviceAop執(zhí)行哪個(gè)事務(wù)通知 -->
</aop:config>

這樣在執(zhí)行service包下的增刪改操作的方法時(shí),就開(kāi)啟事務(wù)了,或者使用注解的方式

<!--        配置事務(wù)管理器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"/>
        </bean>
<!--        注解式事務(wù)聲明配置-->
        <tx:annotation-driven transaction-manager="transactionManager" />

7)多線程調(diào)用

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Transactional
    public void doTest() throws InterruptedException {
        userMapper.deleteById(200110);
        new Thread(()->{
            userMapper.deleteById(200112);
            int i = 10/0; //模擬發(fā)生異常
        }).start();
    }
}

在事務(wù)方法doTest中,啟動(dòng)了一個(gè)新的線程,并在新的線程中發(fā)生了異常,這樣doTest是不會(huì)回滾的。

因?yàn)閮蓚€(gè)操作不在一個(gè)線程中,獲取到的數(shù)據(jù)庫(kù)連接不一樣,從而是兩個(gè)不同的事務(wù),所以也不會(huì)回滾。

8)錯(cuò)誤的傳播行為

Spring定義了7種傳播行為,我們可以通propagation屬性來(lái)指定傳播行為參數(shù),目前只有REQUIRED、REQUIRES_NEW、NESTED會(huì)創(chuàng)建新的事務(wù),其他的則會(huì)以非事務(wù)的方式運(yùn)行或者拋出異常

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Transactional(propagation = Propagation.NEVER)
    public void doTest() throws InterruptedException {
        userMapper.deleteById(200114);
        int i = 10/0; //模擬發(fā)生異常
    }
}

9)自己try…catch…掉了異常

如果沒(méi)有異常拋出,則Spring認(rèn)為程序是正常的,就不會(huì)回滾

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Transactional
    public void doTest() {
        try{
            userMapper.deleteById(200115);
            int i = 10/0; //模擬發(fā)生異常
        }catch (Exception e){
            // 異常操作
        }
    }
}

10)手動(dòng)拋出了錯(cuò)誤的異常

Spring默認(rèn)只會(huì)回滾RuntimeExceptionError對(duì)于普通的Exception,不會(huì)回滾

如果你想觸發(fā)其他異常的回滾,需要在注解上配置一下,如:@Transactional(rollbackFor = Exception.class)

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Transactional
    public void doTest() throws Exception {
        try{
            userMapper.deleteById(200116);
            int i = 10/0; //模擬發(fā)生異常
        }catch (Exception e){
            // 異常操作
            throw new Exception();
        }
    }
}

11)自定義回滾異常

rollbackFor 用于指定能夠觸發(fā)事務(wù)回滾的異常類型,可以指定多個(gè)異常類型。

默認(rèn)是在RuntimeException和Error上回滾。

若異常非配置指定的異常類,則事務(wù)失效

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Transactional(rollbackFor = NullPointerException.class)
    public void doTest() throws MyException {
        userMapper.deleteById(200118);
        throw new MyException();
    }
}

即使rollbackFor有默認(rèn)值,但阿里巴巴開(kāi)發(fā)者規(guī)范中,還是要求開(kāi)發(fā)者重新指定該參數(shù)。

因?yàn)槿绻褂媚J(rèn)值,一旦程序拋出了Exception,事務(wù)不會(huì)回滾,這會(huì)出現(xiàn)很大的bug。所以,建議一般情況下,將該參數(shù)設(shè)置成:Exception或Throwable。

12)嵌套事務(wù)回滾多了

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Transactional
    public void doTest() {
        userMapper.deleteById(200118);
        ((UserServiceImpl)AopContext.currentProxy()).test02();
    }
    @Transactional(propagation = Propagation.NESTED)
    public void test02(){
        userMapper.deleteById(200119);
        int i = 10 / 0; //模擬發(fā)生異常
    }
}

test02()方法出現(xiàn)了異常,沒(méi)有手動(dòng)捕獲,會(huì)繼續(xù)往上拋,到外層doTest()方法的代理方法中捕獲了異常。所以,這種情況是直接回滾了整個(gè)事務(wù),不只回滾單個(gè)保存點(diǎn)。

如果只回滾單個(gè)保存點(diǎn),可以將內(nèi)部嵌套事務(wù)放在try/catch中,類似于上面的自己try…catch…掉異常,并且不繼續(xù)往上拋異常。這樣就能保證,如果內(nèi)部嵌套事務(wù)中出現(xiàn)異常,只回滾內(nèi)部事務(wù),而不影響外部事務(wù)。

@Service
public class UserServiceImpl {
    @Autowired
    UserMapper userMapper;
    @Transactional
    public void doTest() {
        userMapper.deleteById(200118);
        try{
            ((UserServiceImpl)AopContext.currentProxy()).test02();
        }catch (Exception e){
            
        }
    }
    @Transactional(propagation = Propagation.NESTED)
    public void test02(){
        userMapper.deleteById(200119);
        int i = 10 / 0; //模擬發(fā)生異常
    }
}

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

相關(guān)文章

  • Java創(chuàng)建多線程局域網(wǎng)聊天室實(shí)例

    Java創(chuàng)建多線程局域網(wǎng)聊天室實(shí)例

    這篇文章主要介紹了Java創(chuàng)建多線程局域網(wǎng)聊天室實(shí)例,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • SpringBoot項(xiàng)目nohup啟動(dòng)運(yùn)行日志過(guò)大的解決方案

    SpringBoot項(xiàng)目nohup啟動(dòng)運(yùn)行日志過(guò)大的解決方案

    這篇文章主要介紹了SpringBoot項(xiàng)目nohup啟動(dòng)運(yùn)行日志過(guò)大的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • Thymeleaf中th:each及th:if使用方法解析

    Thymeleaf中th:each及th:if使用方法解析

    這篇文章主要介紹了Thymeleaf中th:each及th:if使用方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java進(jìn)程內(nèi)緩存框架EhCache詳解

    Java進(jìn)程內(nèi)緩存框架EhCache詳解

    這篇文章主要介紹了Java進(jìn)程內(nèi)緩存框架EhCache,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2021-12-12
  • mybatis if test判斷BigDecimal遇到的坑及解決

    mybatis if test判斷BigDecimal遇到的坑及解決

    這篇文章主要介紹了mybatis if test判斷BigDecimal遇到的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • spring-boot讀取props和yml配置文件的方法

    spring-boot讀取props和yml配置文件的方法

    本篇文章主要介紹了spring-boot讀取props和yml配置文件的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • Spring Boot JDBC 連接數(shù)據(jù)庫(kù)示例

    Spring Boot JDBC 連接數(shù)據(jù)庫(kù)示例

    本篇文章主要介紹了Spring Boot JDBC 連接數(shù)據(jù)庫(kù)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-02-02
  • JAVA日常開(kāi)發(fā)中讀寫XML的方法詳解

    JAVA日常開(kāi)發(fā)中讀寫XML的方法詳解

    這篇文章主要介紹了JAVA日常開(kāi)發(fā)中讀寫XML的相關(guān)資料,詳細(xì)講解了在Java中如何使用DOM(文檔對(duì)象模型)和SAX(簡(jiǎn)單API?for?XML)兩種方式讀取XML文件,以及如何使用DOM和JAXB(Java?Architecture?for?XML?Binding)兩種方式寫入XML文件,需要的朋友可以參考下
    2024-12-12
  • Spring boot外部配置(配置中心化)詳解

    Spring boot外部配置(配置中心化)詳解

    這篇文章主要給大家介紹了關(guān)于Spring boot外部配置(配置中心化)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • Zookeeper和Eureka哪個(gè)更好?

    Zookeeper和Eureka哪個(gè)更好?

    今天小編就為大家分享一篇關(guān)于Zookeeper和Eureka哪個(gè)更好?,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02

最新評(píng)論

桓仁| 大姚县| 马鞍山市| 寻乌县| 北辰区| 许昌县| 娄底市| 西华县| 明星| 广南县| 宁安市| 乌苏市| 县级市| 五河县| 汨罗市| 新野县| 石阡县| 体育| 收藏| 伊吾县| 台东市| 同江市| 宁远县| 方山县| 中西区| 邹城市| 长宁区| 涞水县| 清丰县| 安化县| 重庆市| 民丰县| 离岛区| 清河县| 抚州市| 隆德县| 廊坊市| 西藏| 于田县| 应城市| 丁青县|