Spring 事务 @Transaction 讲解

概述

  • spring 提了事务支持,使得事务操作变的更加方便供。

Spring 事务实现有哪些方式?

  • 声明式事务:声明式事务也有两种实现方式,基于 xml 配置文件的方式和注解方式(在类上添加 @Transaction 注解)。
  • 编码方式:提供编码的形式管理和维护事务。

说一下 spring 的事务隔离?

  • spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致;

  • ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;

  • ISOLATIONREADUNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);

  • ISOLATIONREADCOMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;

  • ISOLATIONREPEATABLEREAD:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;

  • ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

  • 「脏读」 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。

  • 「不可重复读」 :是指在一个事务内,多次读同一数据。

  • 「幻读」 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

@Transactional 属性

image.png

@Transactional(rollbackFor = Exception.class):深入解析 Spring 的事务管理

在Spring框架中,事务管理是一个非常重要的部分,它确保了在执行一系列数据库操作时,如果其中任何一个操作失败,那么整个事务都会被回滚,以保持数据的完整性和一致性。在Spring中,@Transactional注解是实现这一功能的关键。

@Transactional 注解可以应用于类或者方法上,用于声明该方法或该类中的所有方法都应在事务的上下文中执行。如果在事务执行期间发生异常,并且该异常是 rollbackFor 属性指定的类型或其子类,那么 Spring 将回滚该事务。

让我们深入了解一下@Transactional(rollbackFor = Exception.class)这个注解的含义和用法。
  1. @Transactional 注解的基本用法
    在 Spring 中,@Transactional 注解可以用来声明一个方法需要在事务的上下文中执行。如果方法执行成功,那么事务会被提交;如果方法抛出异常,并且这个异常是 rollbackFor 属性指定的类型或其子类,那么事务会被回滚。

例如,以下代码演示了如何在方法上使用 @Transactional 注解:

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
        // 其他业务逻辑
    }
}

在上面的代码中,createUser 方法被 @Transactional 注解标记,表示它应该在事务的上下文中执行。如果在 createUser 方法执行期间抛出异常,并且这个异常是 rollbackFor 属性指定的类型或其子类,那么整个事务都会被回滚。

  1. rollbackFor 属性的作用
    rollbackFor 属性是 @Transactional 注解的一个重要属性,它用于指定哪些异常类型会触发事务回滚。默认情况下,rollbackFor 属性的值为 RuntimeException.class,也就是说,只有当方法抛出 RuntimeException 或其子类异常时,事务才会被回滚。

然而,在某些情况下,我们可能希望在其他类型的异常发生时也触发事务回滚。这时,我们就可以通过 rollbackFor 属性来指定这些异常类型。

例如,以下代码演示了如何将 rollbackFor 属性设置为 Exception.class:

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional(rollbackFor = Exception.class)
    public void createUser(User user) {
        userRepository.save(user);
        // 其他业务逻辑
    }
}

在上面的代码中,rollbackFor 属性被设置为 Exception.class,表示当 createUser 方法抛出 Exception 或其子类异常时,事务都会被回滚。这意味着,即使方法抛出的是非运行时异常(如 IOException、SQLException 等),事务也会被回滚。

3、注意事项

  • @Transactional 注解只能应用于 public 方法上。如果将其应用于非 public 方法上,那么事务管理将不会生效。
  • 在使用 @Transactional 注解时,需要注意异常的处理方式。如果方法内部捕获了异常并且没有再次抛出,那么事务将不会被回滚。因此,在使用 @Transactional 注解时,应该尽量避免在方法内部捕获异常。
  • @Transactional 注解的生效需要依赖于 Spring 的 AOP(面向切面编程)功能。因此,在使用 @Transactional 注解时,需要确保 Spring 的 AOP 功能已经正确配置。

举例

1、@Transactional 注解只应用到 public 修饰的方法上,在 protected、private 修饰的方法上都不会起作用!(事务具有可见性)

2、一个类中假设 方法 A 使用了注解 @Transactional ,同一个类中的 方法 B 再去调用方法 A 时,事务不生效!(事务具有传递性)

上面的问题不用我说了吧,直接把 private 改成 public ,事务就生效了。

我们来看事务具有传递性的也就是第 2 点。例子如下:

public ReturnT transactionTest(UserDomain user) {

insertUser(user);

return new ReturnT(“success”);

}

@Transactional(rollbackFor = Exception.class)

public void insertUser(UserDomain user) {

user.setUserId(3);

user.setUserName(“riemann”);

user.setPassword(“root”);

user.setPhone(“13129535588”);

userDao.insert(user);

int i = 1 / 0;

System.out.println(i);

}

这个例子恰恰说明了第 2 点事务不生效。

那我们怎么解决呢?把事务加到上一层的方法中去就可以了,因为事务具有传递性。

@Transactional(rollbackFor = Exception.class)

public ReturnT transactionTest(UserDomain user) {

insertUser(user);

return new ReturnT(“success”);

}

public void insertUser(UserDomain user) {

user.setUserId(3);

user.setUserName(“riemann”);

user.setPassword(“root”);

user.setPhone(“13129535588”);

userDao.insert(user);

int i = 1 / 0;

System.out.println(i);

}

三、举一反三

后面再遇到了事务不生效的情况,可以从下面几点找原因:

1、检查你的方法是不是 public 修饰的。

2、检查是不是同一个类中的方法调用(如 a 方法调用同一个类中的 b 方法,在 b 方法上加的事务)。

3、你的异常类型是不是 unchecked 异常?如果我想 check 异常也想回滚怎么办,注解上面写明异常类型即可。

@Transactional(rollbackFor=Exception.class)

类似的还有 norollbackFor,自定义不回滚的异常

4、查看自己的数据的引擎,如果是 MySQL,注意表要使用支持事务的引擎,比如 innodb,如果是 myisam,事务是不起作用的。

查看 MySQL 的所有存储引擎:show engines;

查看 MySQL 的当前存储引擎:show variables like '%storage_engine%';

5、异常是不是被你 catch 住了

6、如果你是 ssm 框架,在用 xml 配置开启的事务,那你要看一下:

是否开启了对注解的解析

<tx:annotation-driven transaction-manager=“transactionManager” proxy-target-class=“true”/>

spring 是否扫描到你这个包,如下是扫描到 org.test 下面的包

<context:component-scan base-package=“org.test” ></context:component-scan>
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    942 引用 • 1458 回帖 • 121 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...