Spring事务详解

一、Spring中如何使用事务

1、编程式事务

    @Autowired
    private PlatformTransactionManager txManager;

    public void addRole(Role role) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = txManager.getTransaction(def);
        try {
            //执行业务代码

            //提交事务
            txManager.commit(status);
        } catch (Exception e) {
            //回滚事务
            txManager.rollback(status);
        }
    }

2、声明式事务

    @Transactional
    public void addRole(Role role) {
        //执行业务代码
    }

@Transactional的配置项:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	//定义事务管理器,它是Spring IOC容器里的一个Bean id,这个Bean需要实现接口PlatformTransactionManager
	@AliasFor("transactionManager")
	String value() default "";

	//同value
	@AliasFor("value")
	String transactionManager() default "";

	//传播行为,默认值为Propagation.REQUIRED,当方法调用时,如果不存在当前事务,那么就创建事务;如果之前的方法已经存在事务了,那么就沿用之前的事务
	Propagation propagation() default Propagation.REQUIRED;

	//隔离级别,默认值为数据库默认隔离级别
	Isolation isolation() default Isolation.DEFAULT;

	//超时时间,单位为秒,当超时时,会引发异常,默认会导致事务回滚
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	//是否开启只读事务,只有当方法产生所定义异常时,才回滚事务,否则就提交事务
	boolean readOnly() default false;

	//回滚事务的异常类定义
	Class<? extends Throwable>[] rollbackFor() default {};

	//作用同rollbackFor,只是通过类名定义
	String[] rollbackForClassName() default {};

	//当产生哪些异常不回滚事务
	Class<? extends Throwable>[] noRollbackFor() default {};

	//作用同noRollbackFor,只是通过类名定义
	String[] noRollbackForClassName() default {};

}

3、事务的传播行为

传播行为是指方法之间的调用事务策略的问题

传播行为含义备注
REQUIRED当方法调用时,如果不存在当前事务,那么就创建事务;如果之前当方法已经存在事务了,那么就沿用之前的事务Spring默认的传播行为
SUPPORTS当方法调用时,如果不存在当前事务,那么不启用事务;如果存在当前事务,那么就沿用当前事务
MANDATORY方法必须在事务内运行如果不存在当前事务,那么就抛出异常
REQUIRES_NEW无论是否存在当前事务,方法都会在新的事务中运行事务管理器会打开新的事务运行该方法
NOT_SUPPORTED不支持事务,如果不存在当前事务也不会创建事务;如果存在当前事务,则挂起它,直至该方法结束后才恢复当前事务
NEVER不支持事务,只有在没有事务的环境中才能运行它如果方法存在当前事务,则抛出异常
NESTED嵌套事务,也就是调用方法如果抛出异常只回滚自己内部执行的SQL,而不回滚主方法的SQL

二、Spring事务使用注意事项

1、@Transactional自调用失效问题

@Transactional的底层实现是Spring AOP技术,而Spring AOP技术使用的是动态代理。这就意味着对于静态方法和非public方法,@Transactional是失效的。而且自调用也会导致事务失效的问题,若同一类中的其他没有@Transactional注解的方法内部调用有@Transactional注解的方法,有@Transactional注解的方法的事务被忽略,不会发生回滚

2、过长时间占用事务

假设在插入角色后还需要操作一个文件,于是对insertRole方法进行了改造

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private FileService fileService;

    @Transactional
    public void addRole(Role role) {
        roleMapper.addRole(role);
        fileService.doSomethingForFile();
    }

当insertRole方法结束后Spring才会释放数据库事务资源,也就是说在运行doSomethingForFile方法时,Spring并没有释放数据库事务资源,而等到doSomethingForFile方法运行完成后,才会关闭数据库资源。当操作文件这步占用较长时间时,数据库事务将长期得不到释放。对此应该在Controller中操作文件

@RestController
@RequestMapping("/api/role")
public class RoleController {
    @Autowired
    private RoleService roleService;

    @Autowired
    private FileService fileService;

    @PostMapping("/add")
    public Role addRole(Role role) {
        roleService.addRole(role);
        fileService.doSomethingForFile();
        return role;
    }
}

3、错误捕获异常

    @Autowired
    private RoleMapper roleMapper;

    @Transactional
    public void addRole(Role role) {
        try {
            roleMapper.addRole(role);
        } catch (Exception e) {
            //记录异常日志
            log.error(e.getMessage());
        }
    }

Spring中事务异常回滚的机制就是靠一层一层向上抛出的异常,直到该异常被抛出到最先调用的地方。如果在异常被抛出的过程中被捕获,那么在抛出过程中影响到的方法产生的独立事务(被调用的方法产生的独立的事务,可以理解为嵌套事务)将会回滚,但是由于异常在往上抛出的过程中被catch住,将不会影响到被catch住异常的方法所在的事务(除非在catch块中throw了一个可以被回滚的异常对象),这也是编码中会影响到事务回滚失效的原因之一

    @Autowired
    private RoleMapper roleMapper;

    @Transactional
    public void addRole(Role role) {
        try {
            roleMapper.addRole(role);
        } catch (Exception e) {
            //记录异常日志
            log.error(e.getMessage());
            //自行抛出异常,让Spring事务管理流程获取异常,进行事务管理
            throw new RuntimeException(e);
        }
    }

2)、手动回滚

    @Autowired
    private RoleMapper roleMapper;

    @Transactional
    public void addRole(Role role) {
        try {
            roleMapper.addRole(role);
        } catch (Exception e) {
            //记录异常日志
            log.error(e.getMessage());
            //手动调用API回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

三、Spring事务实现原理

在应用系统调用声明@Transactional的目标方法时,Spring默认使用AOP代理,在代码运行时生成一个代理对象,根据@Transactional的属性配置信息,这个代理对象决定该声明@Transactional的目标方法是否由拦截器 TransactionInterceptor来使用拦截,在TransactionInterceptor拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager操作数据源DataSource提交或回滚事务

在这里插入图片描述

PlatformTransactionManager接口源码如下:

public interface PlatformTransactionManager {

	//获取事务状态
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;

	//提交事务
	void commit(TransactionStatus status) throws TransactionException;

	//回滚事务
	void rollback(TransactionStatus status) throws TransactionException;

}

事务管理的框架是由抽象事务管理器AbstractPlatformTransactionManager来提供的,而具体的底层事务处理实现,由 PlatformTransactionManager的具体实现类来实现。不同的事务管理器管理不同的数据资源DataSource,比如 DataSourceTransactionManager管理JDBC的Connection

在这里插入图片描述

参考:

https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邋遢的流浪剑客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值