Spring-TX

Spring-TX

编程式事务

参考 JDBC -> JDBC-事务

编程式事务是指手动编写程序来管理事务,即通过代码的方式直接控制事务的提交和回滚

在之前的学习中,我们使用JDBC中的连接来控制事务的提交和回滚

但是编程式实现方式存在缺陷:

  • 细节没有被屏蔽:具体操作过程各种,所有细节都需要程序员来自己完成,比较繁琐
  • 代码复用性低

声明式事务

声明式事务是指用注解或XML的方式来控制事务的提交和回滚

我们可以使用AOP的思路来写事务增强,但Spring对事务的AOP进行了再次的封装,更加简化了我们的操作


Spring事务管理器

声明式事务.PNG

在Spring的底层实现中,创建了一个事务增强,又制作了一个事务管理器统一规范的接口

通过事务增强去调用事务管理器的接口完成事务的具体操作(开启,提交,回滚)

又针对于不同的框架制作了不同的事务管理器的具体实现类

当我们选择某一个具体框架的事务管理器实现类时,Spring会将这个实现类自动注入到事务增强调用的事务管理器的接口

  • TransactionManager接口:事务管理器的统一接口
  • PlatformTansactionManager:统一接口的子接口,扩展实现
    • DataSourceTransactionManager:实现类
      • JDBC方式
      • JdbcTemplate方式
      • Mybatis方式
    • HibernateTransactionManager:实现类
      • hibernate方式
    • JpaTransactionManager
      • jpa方式

注:DataSourceTransactionManager由spring-jdbc依赖提供,HibernateTransactionManager和JpaTransactionManager由spring-orm依赖提供


DataSourceTransactionManager

我们学习的事务管理器实现是DataSourceTransactionManager,用以整合JDBC、jdbcTemplate、Mybatis方式的事务实现

DataSourceTransactionManager的主要方法:

  • doBegin():开启事务
  • doSuspend():挂起事务
  • doResume():恢复挂起的事务
  • doCommit():提交事务
  • doRollback():回滚事务

然而以上的方法我们都不会自己使用,因为框架帮我们完成了一切,我们只需要使用注解声明即可


基于注解的声明式事务

导入依赖:

  • spring-jdbc
  • spring-tx

从6.1.8版本开始,tx为jdbc自动导入的依赖,所以无需导入

首先,我们在配置类中将事务管理器的实现添加到IoC容器中

事务管理器的实现中,要进行对事务的操作,需要使用连接池对象

@Bean
public TransactionManager transactionManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}

@EnableTransactionManagement

在配置类中加@EnableTransactionManagement注解,开启对事务注解的支持


@Transactional

这个注解的作用就是开启事务,它可以加载方法和类上:

  • 方法:方法开启事务
  • 类:类中所有方法开启事务

此时,方法就被Spring的事务管理器以AOP的方式接管了,如果方法执行有异常,则整个方法回滚,如果方法执行顺利,则事务提交


readOnly

只读模式可以提高查询事务的效率,如果事务中只有查询代码,使用只读模式

@Transactional(readOnly = true)

当@Transactional注解到类时,推荐将类中调用查询的方法加入此注解和属性


timeout

事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库

使用timeout属性设置事务的超时时间

@Transactional(timeout = 5) // 单位:秒

如果事务超时,则会报异常:TransactionTimedOutException

注:当类的注解有timeout属性时,方法如果也有注解但没有属性,方法的注解会覆盖类的注解!!!


rollbackFor&norollbackFor

事务的默认是发生运行时异常才会回滚,其他异常不会回滚(比如IO异常)

使用rollbackFor属性,调整为无论发生什么异常时都回滚事务

@Transactional(rollbackFor = Exception.class)

norollbackFor可以放行某些异常,使某些异常发生时不回滚事务


事务隔离级别

数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:

  • 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。

  • 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。

  • 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。

  • 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。

不同的隔离级别适用于不同的场景,需要根据实际业务需求进行选择和调整

MySQL的默认隔离级别是可重复读(Repeatable Read)

使用isolation属性设置隔离级别,推荐设置为读已提交(Read Committed)

@Transactional(isolation = Isolation.READ_COMMITTED)

事务传播行为

事务之间也存在互相调用的情况,以事务的传播行为来决定事物之间调用的影响程度

事务传播行为的属性设定,是设置在子事务(方法)上的

使用propagation 属性设置事务的传播行为

@Transactional(propagation = Propagation.REQUIRES_NEW)
名称含义
REQUIRED 默认值如果父方法有事务,就加入,如果没有就新建自己独立!
REQUIRES_NEW不管父方法是否有事务,我都新建事务,都是独立的!

如果父方法调用子方法,父方法有事务,使用REQUIRED时,子方法会加入父方法的事务中,形成一个事务

如果使用REQUIRES_NEW属性,方法之间事务独立,相互不影响

事务的传播行为一般都推荐用默认值,只有面试的情况下才会问相关问题!!!

在同一个类中,事务的传播行为不会生效,这是因为Spring框架中实际上是使用了代理模式实现事务机制

而代理是根据类来生成增强类,同一个类间的调用就不经过代理