SpringBoot - @Transactional注解详解

简介

Spring中的@Transactional注解,基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷的解决在开发中碰到的问题,@Transactional 的事务开启 ,或者是基于接口的或者是基于类的代理被创建。Spring为了更好的支撑数据库操作,在框架中支持了两种事务管理的方式:编程式事务和声明式事务,能够保证作单个逻辑工作单元执行的一系列操作,要么同时成功,要么同时失败。

如何使用

(1)可以作用在类和方法上。
(2)建议不要作用在接口或接口的方法上,因为只有使用基于接口的代理时注解才有效。建议作用在实现类的方法上。
(3)当作用在类上时,该类的PUBLIC方法都具有该类型是事务属性,但是使用作用在方法上的注解来覆盖类级别的定义。
(4)方法的访问权限为PUBLIC。
(5)@Transactional(rollbackFor=Exception.class),如果方法上加了这个注解,那么当这个方法抛出异常时(运行时和非运行时)就会回滚,数据库里面的数据也会回滚。如果不配置rollbackFor属性,那么事物只会在遇到运行时异常才会回滚。
(6)默认情况下,SPRING只有在抛出的异常为运行时且为unchecked 异常才会回滚事务,也就是抛出的异常为RuntimeException 的子类(Errors也会)时才会回滚事务,而抛出 checked 异常则不会回滚事务 ,当然可以通过 @Transactional rollbackFor进行配置。

(6.1)checked异常: 一般是指程序不能直接控制的外界情况,是指在编译的时候就需要检查的一类异常,用户程序中必须采用try-catch机制处理或者通过throws交由调用者来处理。这类异常主要是指除了Error以及RuntimeException及其子类之外的异常。
(6.2)unchecked异常:一般是那些不在编译的时就要处理的一类异常。在JAVA体系里,所有的Error以及RuntimeException及其子类都是unchecked异常。
在这里插入图片描述

1. 源码定义
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	// 可选的限定描述符,指定使用的事务管理器
	@AliasFor("transactionManager")
	String value() default "";

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

	String[] label() default {};

	// 可选的事务传播机制
	Propagation propagation() default Propagation.REQUIRED;

	// 可选的事务隔离级别
	Isolation isolation() default Isolation.DEFAULT;

	// 事务超时时间设置
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	String timeoutString() default "";

	boolean readOnly() default false;

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

    // 导致事务回滚的异常类名称
	String[] rollbackForClassName() default {};
	
	// 不会导致事务回滚的异常类
	Class<? extends Throwable>[] noRollbackFor() default {};

    // 不会导致事务回滚的异常类名称
	String[] noRollbackForClassName() default {};

}
2. 传播机制
序号机制名称描述
1REQUIRED默认值,如果当前存在事务,则加入该事务,如果当前没有事务,则创建一个新的事务。
2REQUIRES_NEW首先创建一个新的事务,如果当前存在事务,则把当前事务挂起。
3SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
4NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起。
5NEVER以非事务方式运行,如果当前存在事务,则抛出异常。
6MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
7NESTED如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行; 如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
3. 隔离级别
序号名称描述
1DEFAULT使用后端数据库默认的隔离级别
2READ_UNCOMMITTED读取未提交数据(会出现脏读, 不可重复读)
3READ_COMMITTED读取已提交数据(会出现不可重复读和幻读)
4REPEATABLE_READ可重复读(会出现幻读)
5SERIALIZABLE串行化

使用场景

1. 同一个类
场景1

同一类中有A和B两个方法,A方法上没有@Transactional注解,B方法上有,上层应用通过A方法间接调用B方法,B方法抛出异常,此时A方法和B方法操作都不会回滚。


public void a() {
	b();
}
@Transactional(rollbackFor = Exception.class)
public void b() {
}
场景2

同一类中有A和B两个方法,A方法上有@Transactional注解,B方法上没有,上层应用通过A方法间接调用B方法,B方法抛出异常,此时A方法和B方法操作全部回滚。

@Transactional(rollbackFor = Exception.class)
public void a() {
	b();
}
public void b() {
}
场景3

同一类中有A和B两个方法,A方法和B方法上都有@Transactional注解,上层应用通过A方法间接调用B方法,B方法抛出异常,此时A方法和B方法操作全部回滚。

@Transactional(rollbackFor = Exception.class)
public void a() {
	b();
}
@Transactional(rollbackFor = Exception.class)
public void b() {
}
2. 不同的类
场景1

类1中有A方法,类2中有B方法,A方法上有@Transactional注解,B方法上没有,上层应用通过A方法间接调用B方法,B方法抛出异常,此时A方法和B方法操作全部回滚。

@Transactional(rollbackFor = Exception.class)
public void a() {
	b();
}
public void b() {
}
场景2

类1中有A方法,类2中有B方法,A方法和B方法上都有@Transactional注解,上层应用通过A方法间接调用B方法,B方法抛出异常,此时A方法和B方法操作全部回滚。

@Transactional(rollbackFor = Exception.class)
public void a() {
	b();
}
@Transactional(rollbackFor = Exception.class)
public void b() {
}
场景3

类1中有A方法,类2中有B方法,A方法和B方法上都有@Transactional注解,上层应用通过A方法间接调用B方法,B方法抛出异常,但是A方法捕获了异常,此时B方法操作回滚,A方法抛出异常。

@Transactional(rollbackFor = Exception.class)
public void a() {
	try {
	    b();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
@Transactional(rollbackFor = Exception.class)
public void b() {
}
场景4

类1中有A方法,类2中有B方法,A方法和B方法上都有@Transactional注解,上层应用通过A方法间接调用B方法,B方法抛出异常,但是A方法捕获了异常,此时B方法操作回滚,A方法操作成功。

@Transactional(rollbackFor = Exception.class)
public void a() {
	try {
	    b();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void b() {
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
@Transactional注解Spring框架中用于管理事务的注解,它可以将一个方法标记为事务性方法,该方法被调用时,Spring会自动开启一个事务,当方法执行完毕后,Spring会根据方法的执行情况决定是提交事务还是回滚事务。在避免死锁方面,@Transactional注解可以通过以下两种方式来实现: 1.调整事务隔离级别 在@Transactional注解中,可以通过设置isolation属性来调整事务的隔离级别。事务隔离级别是指多个事务同时操作同一个数据时,事务之间的隔离程度。Spring支持的事务隔离级别包括READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE。其中,READ_COMMITTED是默认的隔离级别。如果应用程序中存在死锁问题,可以尝试将事务隔离级别调整为REPEATABLE_READ或SERIALIZABLE,这样可以减少死锁的发生。 2.使用悲观锁 在@Transactional注解中,可以通过设置propagation属性来指定事务的传播行为。如果将propagation属性设置为REQUIRES_NEW,表示该方法需要开启一个新的事务,并且在该事务中使用悲观锁来避免死锁。悲观锁是指在操作数据时,先将数据锁定,然后再进行操作。这样可以避免多个事务同时对同一数据进行操作,从而减少死锁的发生。 下面是一个使用@Transactional注解避免死锁的示例: ```java @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE) public void transfer(int fromUserId, int toUserId, double amount) { User fromUser = userDao.getUserById(fromUserId); User toUser = userDao.getUserById(toUserId); synchronized (fromUser) { synchronized (toUser) { if (fromUser.getBalance() >= amount) { fromUser.setBalance(fromUser.getBalance() - amount); toUser.setBalance(toUser.getBalance() + amount); userDao.updateUser(fromUser); userDao.updateUser(toUser); } else { throw new RuntimeException("Insufficient balance"); } } } } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

cloneme01

谢谢您的支持与鼓励!

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

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

打赏作者

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

抵扣说明:

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

余额充值