事务的使用
在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 的增强
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论