当前位置: 代码网 > it编程>编程语言>Java > java同一个类中,一个无事务方法调用一个有事务方法时,事务失效问题

java同一个类中,一个无事务方法调用一个有事务方法时,事务失效问题

2024年12月11日 Java 我要评论
事务的使用在spring项目的开发中,通过在方法上添加transactional注解,实现事务的管理,在方法开始开启事务,出现异常进行事务的回滚,方法结束前提交事务。事务的实现原理transactio

事务的使用

在spring项目的开发中,通过在方法上添加transactional注解,实现事务的管理,在方法开始开启事务,出现异常进行事务的回滚,方法结束前提交事务。

事务的实现原理

transactional 注解是 spring 框架用来实现声明式事务管理的重要工具。其原理主要基于 aop(面向切面编程),通过动态代理在方法执行前、后以及异常情况下进行事务的处理。

当你在一个方法上使用 @transactional 注解时,spring 会在运行时生成一个代理对象,该对象会拦截对该方法的调用。

在调用之前,代理会开始一个新的事务;在方法执行完成后,代理会提交或回滚事务,具体取决于方法是否抛出了未处理的异常。

具体流程如下:

  • spring使用jdk动态代理技术或者cglib代理来创建目标类的代理对象
  • 当方法被调用时,实际上调用的是代理类中的增强方法,而不是直接调用目标类中的方法
  • 在调用方法之前,代理会根据注解的属性(传播行为和隔离级别)从platformtransactionmanager中获取一个事务,在调用目标方法前开启事务
  • 执行目标方法
  • 目标方法执行成功,则提交事务,执行失败,则回滚事务

代理的两种方式:

  • jdk 动态代理:当目标类实现至少一个接口时,spring会使用jdk动态代理。这种代理仅适用于基于接口的代理。原理是通过java反射机制,在运行时生成一个实现了目标类接口的代理类。
  • cglib 代理:如果目标类没有实现任何接口,或者您强制配置为使用cglib,那么spring会使用cglib库生成一个目标类的子类作为代理。原理是使用cglib库,通过继承目标类并重写其方法来实现代理。

原因

在同一个类中,一个无事务方法直接调用有事务的方法时,是通过this.方法名的方式调用。

this方式:它直接访问的是当前对象的实现,如果当前类被spring aop代理,那么使用this调用的方法将不会触发aop切面。也就是说,切面(如事务管理、日志记录等)不会生效,因为你直接调用了目标对象的方法,而不是代理对象的方法。

通过autowired注解注入的bean进行调用的方式:是通过spring容器管理的代理对象进行调用,这种情况下aop特性可以正常工作,例如事务、日志等会生效。

spring容器管理的代理对象的生成条件和时机

在spring中,代理对象的生成通常与aop(面向切面编程)相关,当类上使用@component@service@repository@controller等注解,并且方法上使用aop相关的注解时,spring会创建代理对象,以便能够在调用这些方法时执行切面逻辑。

常见的aop注解包括:

  • @transactional
  • @cacheable
  • @async
  • @scheduled

事务失效代码

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自己(也可以将方法放到另外一个service中,然后注入该service进行调用)

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

②通过spring上下文获取到当前代理类

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获取到当前代理类,需要在启动类加上enableaspectjautoproxy(exposeproxy = true),exposeproxy = true用于控制aop框架公开代理,公开后才可以通过aopcontext获取到当前代理类。

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

总结

使用 aop 注解

  • 如果 testtransactionalservice 类上使用了 aop 相关的注解(如 @transactional, @aspect, @around 等)
  • 通过 applicationcontext.getbean(testtransactionalservice.class) 获取的对象将是一个代理对象

代理对象的创建是为了在方法调用前后加入额外的行为,比如事务管理、日志记录等。

未使用 aop 注解

  • 如果该类没有任何 aop 相关的注解
  • 获取的对象就是普通的 bean
  • 没有经过 aop 的增强

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com