欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java同一個(gè)類中,一個(gè)無(wú)事務(wù)方法調(diào)用一個(gè)有事務(wù)方法時(shí),事務(wù)失效問(wèn)題

 更新時(shí)間:2024年12月10日 15:22:57   作者:linab112  
本文詳細(xì)介紹了Spring框架中事務(wù)管理的實(shí)現(xiàn)原理,包括@Transactional注解的使用、事務(wù)的開啟、提交和回滾機(jī)制,以及代理對(duì)象的兩種實(shí)現(xiàn)方式(JDK動(dòng)態(tài)代理和CGLIB代理),文章還探討了在同一個(gè)類中調(diào)用有事務(wù)方法時(shí)事務(wù)失效的原因,并提供了解決方法

事務(wù)的使用

在spring項(xiàng)目的開發(fā)中,通過(guò)在方法上添加Transactional注解,實(shí)現(xiàn)事務(wù)的管理,在方法開始開啟事務(wù),出現(xiàn)異常進(jìn)行事務(wù)的回滾,方法結(jié)束前提交事務(wù)。

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

Transactional 注解是 Spring 框架用來(lái)實(shí)現(xiàn)聲明式事務(wù)管理的重要工具。其原理主要基于 AOP(面向切面編程),通過(guò)動(dòng)態(tài)代理在方法執(zhí)行前、后以及異常情況下進(jìn)行事務(wù)的處理。

當(dāng)你在一個(gè)方法上使用 @Transactional 注解時(shí),Spring 會(huì)在運(yùn)行時(shí)生成一個(gè)代理對(duì)象,該對(duì)象會(huì)攔截對(duì)該方法的調(diào)用。

在調(diào)用之前,代理會(huì)開始一個(gè)新的事務(wù);在方法執(zhí)行完成后,代理會(huì)提交或回滾事務(wù),具體取決于方法是否拋出了未處理的異常。

具體流程如下:

  • spring使用jdk動(dòng)態(tài)代理技術(shù)或者cglib代理來(lái)創(chuàng)建目標(biāo)類的代理對(duì)象
  • 當(dāng)方法被調(diào)用時(shí),實(shí)際上調(diào)用的是代理類中的增強(qiáng)方法,而不是直接調(diào)用目標(biāo)類中的方法
  • 在調(diào)用方法之前,代理會(huì)根據(jù)注解的屬性(傳播行為和隔離級(jí)別)從PlatformTransactionManager中獲取一個(gè)事務(wù),在調(diào)用目標(biāo)方法前開啟事務(wù)
  • 執(zhí)行目標(biāo)方法
  • 目標(biāo)方法執(zhí)行成功,則提交事務(wù),執(zhí)行失敗,則回滾事務(wù)

代理的兩種方式:

  • JDK 動(dòng)態(tài)代理:當(dāng)目標(biāo)類實(shí)現(xiàn)至少一個(gè)接口時(shí),Spring會(huì)使用JDK動(dòng)態(tài)代理。這種代理僅適用于基于接口的代理。原理是通過(guò)Java反射機(jī)制,在運(yùn)行時(shí)生成一個(gè)實(shí)現(xiàn)了目標(biāo)類接口的代理類。
  • CGLIB 代理:如果目標(biāo)類沒有實(shí)現(xiàn)任何接口,或者您強(qiáng)制配置為使用CGLIB,那么Spring會(huì)使用CGLIB庫(kù)生成一個(gè)目標(biāo)類的子類作為代理。原理是使用CGLIB庫(kù),通過(guò)繼承目標(biāo)類并重寫其方法來(lái)實(shí)現(xiàn)代理。

原因

在同一個(gè)類中,一個(gè)無(wú)事務(wù)方法直接調(diào)用有事務(wù)的方法時(shí),是通過(guò)this.方法名的方式調(diào)用。

this方式:它直接訪問(wèn)的是當(dāng)前對(duì)象的實(shí)現(xiàn),如果當(dāng)前類被Spring AOP代理,那么使用this調(diào)用的方法將不會(huì)觸發(fā)AOP切面。也就是說(shuō),切面(如事務(wù)管理、日志記錄等)不會(huì)生效,因?yàn)槟阒苯诱{(diào)用了目標(biāo)對(duì)象的方法,而不是代理對(duì)象的方法。

通過(guò)Autowired注解注入的bean進(jìn)行調(diào)用的方式:是通過(guò)spring容器管理的代理對(duì)象進(jìn)行調(diào)用,這種情況下AOP特性可以正常工作,例如事務(wù)、日志等會(huì)生效。

Spring容器管理的代理對(duì)象的生成條件和時(shí)機(jī)

在Spring中,代理對(duì)象的生成通常與AOP(面向切面編程)相關(guān),當(dāng)類上使用@Component、@Service、@Repository@Controller等注解,并且方法上使用AOP相關(guān)的注解時(shí),Spring會(huì)創(chuàng)建代理對(duì)象,以便能夠在調(diào)用這些方法時(shí)執(zhí)行切面邏輯。

常見的AOP注解包括:

  • @Transactional
  • @Cacheable
  • @Async
  • @Scheduled

事務(wù)失效代碼

package com.ruoyi.system.service.impl;

import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.TestTransactionalService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Author linaibo
 * @Date 2024/8/3 15:36
 * @Version 1.0
 */
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private SysDeptMapper deptMapper;

    @Autowired
    private SysConfigMapper configMapper;


    @Override
    public int insertDept(SysDept dept) {
        dept.setAncestors("123123");
        deptMapper.insertDept(dept);
        insertConfig();
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertConfig() {
        SysConfig config;
        for (int i = 0; i < 5; i++) {
            config = new SysConfig();
            config.setConfigName("配置" + i);
            configMapper.insertConfig(config);
            if (i == 2) {
                throw new RuntimeException();
            }
        }
        return 1;
    }
}

解決方法

①自己autowire自己(也可以將方法放到另外一個(gè)service中,然后注入該service進(jìn)行調(diào)用)

package com.ruoyi.system.service.impl;

import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.TestTransactionalService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Author linaibo
 * @Date 2024/8/3 15:36
 * @Version 1.0
 */
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private SysDeptMapper deptMapper;
    @Autowired
    private SysConfigMapper configMapper;
    @Autowired
    private TestTransactionalService testTransactionalService;


    @Override
    public int insertDept(SysDept dept) {
        dept.setAncestors("123123");
        deptMapper.insertDept(dept);
        testTransactionalService.insertConfig();
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertConfig() {
        SysConfig config;
        for (int i = 0; i < 5; i++) {
            config = new SysConfig();
            config.setConfigName("配置" + i);
            configMapper.insertConfig(config);
            if (i == 2) {
                throw new RuntimeException();
            }
        }
        return 1;
    }
}

②通過(guò)spring上下文獲取到當(dāng)前代理類

package com.ruoyi.system.service.impl;

import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.TestTransactionalService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Author linaibo
 * @Date 2024/8/3 15:36
 * @Version 1.0
 */
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private SysDeptMapper deptMapper;
    @Autowired
    private SysConfigMapper configMapper;
    @Autowired
    private TestTransactionalService testTransactionalService;
    @Autowired
    private ApplicationContext applicationContext;


    @Override
    public int insertDept(SysDept dept) {
        dept.setAncestors("123123");
        deptMapper.insertDept(dept);
        TestTransactionalService service = applicationContext.getBean(TestTransactionalService.class);
        service.insertConfig();
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertConfig() {
        SysConfig config;
        for (int i = 0; i < 5; i++) {
            config = new SysConfig();
            config.setConfigName("配置" + i);
            configMapper.insertConfig(config);
            if (i == 2) {
                throw new RuntimeException();
            }
        }
        return 1;
    }
}

③使用AopContext獲取到當(dāng)前代理類,需要在啟動(dòng)類加上EnableAspectJAutoProxy(exposeProxy = true),exposeProxy = true用于控制AOP框架公開代理,公開后才可以通過(guò)AopContext獲取到當(dāng)前代理類。

package com.ruoyi.system.service.impl;

import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.service.TestTransactionalService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;

/**
 * @Author linaibo
 * @Date 2024/8/3 15:36
 * @Version 1.0
 */
@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private SysDeptMapper deptMapper;
    @Autowired
    private SysConfigMapper configMapper;


    @Override
    public int insertDept(SysDept dept) {
        dept.setAncestors("123123");
        deptMapper.insertDept(dept);
        TestTransactionalService service = Objects.nonNull(AopContext.currentProxy()) ? (TestTransactionalService)AopContext.currentProxy() : this;
        service.insertConfig();
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertConfig() {
        SysConfig config;
        for (int i = 0; i < 5; i++) {
            config = new SysConfig();
            config.setConfigName("配置" + i);
            configMapper.insertConfig(config);
            if (i == 2) {
                throw new RuntimeException();
            }
        }
        return 1;
    }
}

總結(jié)

使用 AOP 注解

  • 如果 TestTransactionalService 類上使用了 AOP 相關(guān)的注解(如 @Transactional, @Aspect, @Around 等)
  • 通過(guò) applicationContext.getBean(TestTransactionalService.class) 獲取的對(duì)象將是一個(gè)代理對(duì)象

代理對(duì)象的創(chuàng)建是為了在方法調(diào)用前后加入額外的行為,比如事務(wù)管理、日志記錄等。

未使用 AOP 注解

  • 如果該類沒有任何 AOP 相關(guān)的注解
  • 獲取的對(duì)象就是普通的 Bean
  • 沒有經(jīng)過(guò) AOP 的增強(qiáng)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • RocketMQ中的消息發(fā)送與消費(fèi)詳解

    RocketMQ中的消息發(fā)送與消費(fèi)詳解

    這篇文章主要介紹了RocketMQ中的消息發(fā)送與消費(fèi)詳解,RocketMQ是一款高性能、高可靠性的分布式消息中間件,消費(fèi)者是RocketMQ中的重要組成部分,消費(fèi)者負(fù)責(zé)從消息隊(duì)列中獲取消息并進(jìn)行處理,需要的朋友可以參考下
    2023-10-10
  • Elasticsearch Join字段類型簡(jiǎn)單快速上手教程

    Elasticsearch Join字段類型簡(jiǎn)單快速上手教程

    這篇文章主要為大家介紹了Elasticsearch Join字段類型簡(jiǎn)單快速上手教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • IDEA在一個(gè)項(xiàng)目空間下管理多個(gè)項(xiàng)目的操作方法

    IDEA在一個(gè)項(xiàng)目空間下管理多個(gè)項(xiàng)目的操作方法

    這篇文章主要介紹了IDEA如何在一個(gè)項(xiàng)目空間下管理多個(gè)項(xiàng)目,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • SpringBoot快速搭建TCP服務(wù)端和客戶端全過(guò)程

    SpringBoot快速搭建TCP服務(wù)端和客戶端全過(guò)程

    這篇文章主要介紹了SpringBoot快速搭建TCP服務(wù)端和客戶端全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-05-05
  • 詳解Java中類的加載順序

    詳解Java中類的加載順序

    Java中什么時(shí)候類加載,第一次需要使用類信息時(shí)加載。類加載的原則:延遲加載,能不加載就不加載。下面這篇文章主要介紹了Java中類的加載順序,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    2017-01-01
  • 詳解Maven POM(項(xiàng)目對(duì)象模型)

    詳解Maven POM(項(xiàng)目對(duì)象模型)

    這篇文章主要介紹了Maven POM(項(xiàng)目對(duì)象模型)的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • Java使用connectTo方法提高代碼可續(xù)性詳解

    Java使用connectTo方法提高代碼可續(xù)性詳解

    這篇文章主要介紹了Java使用connectTo方法提高代碼可續(xù)性,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • MyBatis使用動(dòng)態(tài)SQL標(biāo)簽的小陷阱

    MyBatis使用動(dòng)態(tài)SQL標(biāo)簽的小陷阱

    MyBatis是一個(gè)支持普通SQL查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架,MyBatis越來(lái)越受大家的喜愛了。下面給大家分享MyBatis使用動(dòng)態(tài)SQL標(biāo)簽的小陷阱,感興趣的朋友一起看看吧
    2016-10-10
  • MyBatis-Plus數(shù)據(jù)權(quán)限插件的簡(jiǎn)單使用

    MyBatis-Plus數(shù)據(jù)權(quán)限插件的簡(jiǎn)單使用

    在MyBatis-Plus中,通過(guò)DataPermissionInterceptor插件實(shí)現(xiàn)數(shù)據(jù)權(quán)限控制,首先需要?jiǎng)?chuàng)建自定義注解和處理類,利用JSQLParser庫(kù)動(dòng)態(tài)修改SQL,實(shí)現(xiàn)按角色權(quán)限過(guò)濾數(shù)據(jù),配置類中注冊(cè)攔截器,確保只有授權(quán)用戶能訪問(wèn)指定數(shù)據(jù),感興趣的可以了解一下
    2024-10-10
  • java回調(diào)機(jī)制實(shí)例詳解

    java回調(diào)機(jī)制實(shí)例詳解

    這篇文章主要介紹了java回調(diào)機(jī)制實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-05-05

最新評(píng)論