Spring声明式事务@Transactional的一些问题的测试及求证

1.Spring的声明式事务@Transactional问题

前提:有两个方法,a方法对a表做修改操作,b方法对b表做修改操作

a方法调用b方法,然后a方法报错,伪代码如下

public void a() {
    //数据库修改操作
    CompensateLogDO compensateLogDO = new CompensateLogDO();
    compensateLogDO.setId(IdWorker.getId());
    compensateLogDO.setCompensateLogType("123");
    compensateLogMapper.insert(compensateLogDO);

    fullCalcErrorService.b();

    int i = 1 / 0;
}

public void b() {
    //数据库修改操作
    FullCalcErrorDO fullCalcErrorDO = new FullCalcErrorDO();
    fullCalcErrorDO.setId(IdWorker.getId());
    fullCalcErrorDO.setRetryCount(0);
    fullCalcErrorDO.setProcessFlag(0);
    fullCalcErrorMapper.insert(fullCalcErrorDO);
}

a方法调用b方法,然后b方法报错,伪代码如下

public void a() {
    //数据库修改操作
    CompensateLogDO compensateLogDO = new CompensateLogDO();
    compensateLogDO.setId(IdWorker.getId());
    compensateLogDO.setCompensateLogType("123");
    compensateLogMapper.insert(compensateLogDO);

    fullCalcErrorService.b();
}

public void b() {
    //数据库修改操作
    FullCalcErrorDO fullCalcErrorDO = new FullCalcErrorDO();
    fullCalcErrorDO.setId(IdWorker.getId());
    fullCalcErrorDO.setRetryCount(0);
    fullCalcErrorDO.setProcessFlag(0);
    fullCalcErrorMapper.insert(fullCalcErrorDO);
    
    int i = 1 / 0;
}

直接上结果:

  • √:数据库插入成功
  • ×:数据库插入失败
    Spring声明式事务@Transactional的一些问题的测试及求证_第1张图片

在 Spring Boot 中,默认情况下,对于单个数据库操作(即单个 SQL 语句的执行),是采用自动提交的方式。这意味着每次执行单个数据库操作后,都会立即将数据提交到数据库,不会等待其他操作或事务的完成。

2.@Transactional数据库连接问题

声明式事务管理建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

也就是方法上加了@Transactional,在调用方法时就会获取一个数据库连接,并占用连接到方法结束;而不加@Transactional,只有执行到数据库(读或写)操作时才会获取一个数据库连接,数据库操作执行完就会释放连接

测试验证:

1.在两个方法都不加在@Transactional注解测试,分别在com.zaxxer.hikari.pool.HikariPool#getConnection()org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection打上断点,执行到第一个数据库操作,进入断点,可以看到数据库连接为HikariProxyConnection@1230471455,释放断点,继续执行到第二个数据库操作,又会进入断点,可以看到数据库连接为HikariProxyConnection@1135602287,可以证明只有执行到数据库(读或写)操作时才会获取一个数据库连接,数据库操作执行完就会释放连接

Spring声明式事务@Transactional的一些问题的测试及求证_第2张图片
Spring声明式事务@Transactional的一些问题的测试及求证_第3张图片

2.在a方法上加@Transactional注解,b方法不加@Transactional注解,还是分别在com.zaxxer.hikari.pool.HikariPool#getConnection()org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin打上断点,方法一执行,就会获取数据库连接,这里是用代理获取的,可以看到数据库连接为HikariProxyConnection@1868392374,释放断点继续执行,可以发现后续数据库操作不会再获取连接,可以证明方法上加了@Transactional,在调用方法时就会获取一个数据库连接,并占用连接到方法结束

Spring声明式事务@Transactional的一些问题的测试及求证_第4张图片

3.@Transactional(readOnly = true)问题

@Transactional(readOnly = true) 是Spring Framework中用来声明只读事务的注解。当一个方法被标记为@Transactional(readOnly = true)时,Spring将会确保在该方法的执行过程中,不会对数据库进行写操作,即只进行读操作。进行写操作会报错!!!

虽然@Transactional(readOnly = true)的主要目的是为了告诉Spring该方法只涉及读操作,但是它在底层仍然会占用一个数据库连接,这是因为Spring需要通过数据库连接来执行查询操作。然而,由于该方法只进行读取操作,Spring会尽可能地使用数据库的只读连接,从而减少对数据库的负担。

@Transactional(readOnly = true)主要的优势是在于告诉Spring该方法只进行读取操作,从而让Spring能够优化数据库连接的使用。它适用于那些纯粹的查询方法或者只读取数据而不涉及更新的业务逻辑。

什么时候使用@Transactional(readOnly = true)呢?

  • 纯读取操作: 如果某个方法完全是只读操作,不涉及数据库的写入(INSERT、UPDATE、DELETE),那么可以使用 @Transactional(readOnly = true) 来标记这个方法。这样Spring将会优化数据库连接的使用,使用只读连接,从而减少对数据库的负担。

  • 提高并发性能: 在高并发读取密集的场景下,通过将一些只读的查询操作标记为 @Transactional(readOnly = true) 可以提高并发性能。因为只读事务通常会使用数据库的共享锁(Shared Lock),不会阻塞其他只读事务,这样可以减少数据库的锁竞争,提高并发能力。

  • 避免意外修改: 使用 @Transactional(readOnly = true) 可以防止开发人员在只读方法中意外执行了写入操作。如果在只读事务中尝试执行写入操作,会抛出异常,从而避免数据的修改。

你可能感兴趣的:(Springboot,spring,Transactional,声明式事务)