spring-事务

Spring 声明式事务

事务是一组操作,被视为一个不可分割的工作单元,要么全部完成,要么全部失败回滚,来确保数据的一致性和完整性。Spring事务管理允许我们在应用程序中声明式地或编程式地管理事务,它提供了一个事务管理抽象层,使得事务的使用和配置更加简单和灵活。Spring事务管理不直接管理数据库事务,而是通过委托给底层的数据库事务管理器,如JDBC或Hibernate的事务管理器,来实现对数据库事务的控制。

Spring事务的使用

使用@EnableTransactionManagement来开启spring事务功能(SpringBoot中不用显示设置)

使用@Transactional注解来标记某个方法使用事务功能,作用在类上则所有方法都使用事务

@Service
public class UserService {

    @Transactional //开启事务
    public void createUser(User user) {
        // 业务逻辑代码
    }
}

@Configuration
@EnableTransactionManagement // 开启基于注解的自动化事务管理
public class AppConfig {
   // 其他配置
}

事务的底层和原理

Spring事务管理器

transactionManager:事务管理器; 控制事务的获取、提交、回滚。

HibernateTransactionManager 是 Spring 框架中的一个类,用于为 Hibernate 提供事务管理。它将 Hibernate 与 Spring 的事务管理功能集成,使得开发者能够以声明性或编程方式管理事务。

主要特点:

与 Spring 集成:允许使用 Spring 的事务管理特性与 Hibernate 一起工作。

声明式事务:可以使用 `@Transactional` 注解定义事务边界。

回滚支持:在发生异常时支持事务的回滚。

多数据源支持:可以管理跨多个 Hibernate 会话的事务。


JpaTransactionManager 是 Spring 框架中的一个类,用于为 JPA(Java Persistence API)提供事务管理。它将 JPA 与 Spring 的事务管理功能集成,使得开发者能够以声明性或编程方式管理事务。

主要特点:

与 Spring 集成:允许使用 Spring 的事务管理特性与 JPA 一起工作。

声明式事务:可以使用 `@Transactional` 注解定义事务边界。

回滚支持:在发生异常时支持事务的回滚。

多数据源支持:可以管理跨多个 JPA 实体管理器的事务。


JdbcTransactionManager 是 Spring 框架中用于管理基于 JDBC 的事务的一种事务管理器。它实现了 PlatformTransactionManager 接口,允许开发者在 Spring 应用中方便地进行事务管理。

底层默认使用 JdbcTransactionManager;

实现原理

1、事务管理器:TransactionManager; 控制提交和回滚

2、事务拦截器:TransactionInterceptor: 控制何时提交和回滚
    completeTransactionAfterThrowing(txInfo, ex);  在这个时候回滚
    commitTransactionAfterReturning(txInfo);  在这个时候提交

事务属性

在 Spring 框架中,事务属性(Transaction Attributes)用于定义事务的行为和特点。通过在方法上使用 @Transactional 注解,开发者可以指定这些事务属性。

只读(readOnly)

对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化

// readOnly = true把当前事务设置为只读 默认是false!
@Transactional(readOnly = true)

超时时间(timeout)【同timeoutString】

指定事务的超时时间(以秒为单位)。如果事务在超时前没有完成,将会被回滚。

 @Transactional(readOnly = false,timeout = 3)

回滚异常(rollbackFor)【同rollbackForClassName】

指定哪些异常类型应导致事务回滚。可以使用类名作为指定条件。默认是 RuntimeException and Error 异常方可回滚!

/**
 * rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
 * noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
 */
@Transactional(rollbackFor = Exception.class)  //指定所有异常都回滚
public void changeInfo() throws FileNotFoundException {
   //方法体
}

不回滚异常(noRollbackFor)【同noRollbackForClassName】

指定哪些异常类型不会导致事务回滚。默认编译时异常都不回滚

 @Transactional(noRollbackFor = FileNotFoundException.class) //指定异常不回滚+编译时异常都不回滚
    public void changeInfo() throws FileNotFoundException {
      //方法体
}

总结

rollbackFor(同rollbackForClassName):指明哪些异常需要回滚。不是所有异常都一定引起事务回滚。

异常:

运行时异常(unchecked exception【非受检异常】)

编译时异常(checked exception【受检异常】)

【【回滚的默认机制】】

运行时异常:回滚

编译时异常:不回滚

【可以指定哪些异常需要回滚】;

【回滚 = 运行时异常 + 指定回滚异常】


noRollbackFor(同 noRollbackForClassName):指明哪些异常不需要回滚。

【不回滚 = 编译时异常 + 指定不回滚异常】

事务隔离级别(isolation)

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

  1. 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
  2. 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
  3. 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
  4. 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。

READ_UNCOMMITTED:允许读取未提交的数据(最低的隔离级别)。

READ_COMMITTED:只允许读取已提交的数据。

REPEATABLE_READ:确保在事务执行期间多次读取的数据是一致的。[MySQL默认]

SERIALIZABLE:最高的隔离级别,强制对事务进行完全序列化。

级别问题 脏读 不可重复读 幻读
读未提交
读已提交 ×
可重复读 × ×
串行化 × × ×
@RequestMapping("/save")
@Transactional(isolation = Isolation.SERIALIZABLE) public Object save(User user) { //隔离级别为==》串行化
    // 业务实现
}

传播行为(propagation)

在 Spring 框架中,事务传播行为(propagation)定义了一个方法在事务中的执行方式,尤其是当该方法被其他事务方法调用时的行为。

具体来说,传播行为决定了方法如何与当前的事务交互,例如如何使用现有的事务或是否需要创建新的事务。

REQUIRED(默认行为): 如果存在当前事务,则加入该事务;如果没有,则创建新的事务。

REQUIRES_NEW: 始终创建一个新的事务,暂停当前事务。

NESTED: 如果存在当前事务,则在其内部创建一个嵌套事务。嵌套事务可以回滚到外层事务。

SUPPORTS: 如果存在当前事务,则加入该事务;如果没有,则以无事务方式执行。

NOT_SUPPORTED: 始终以无事务执行,并且如果存在当前事务则将其挂起。

MANDATORY: 必须在一个已存在的事务中执行,如果没有事务则会抛出异常。

NEVER: 方法必须在无事务环境下执行,如果当前存在事务,则抛出异常。

传播行为 产生效果
REQUIRED 支持当前事务,如果不存在则创建一个新的事务
SUPPORTS 支持当前事务,如果不存在则非事务性执行
MANDATORY 支持当前事务,如果不存在则抛出异常
REQUIRES_NEW 创建一个新事务,并在存在当前事务时挂起当前事务
NESTED 如果当前存在事务,则在嵌套事务中执行,否则像 REQUIRED 一样运行
NOT_SUPPORTED 非事务执行,如果存在当前事务则暂停
NEVER 非事务性地执行,如果存在事务则抛出异常
@Service
public class StudentService {

    @Autowired
    private StudentDao studentDao;
    /**
     * 声明两个独立修改数据库的事务业务方法
     */
    @Transactional(propagation = Propagation.REQUIRED)  //支持当前事务,如果不存在则创建一个新的事务  
    public void changeAge(){
        studentDao.updateAgeById(99,1);
    }

    @Transactional(propagation = Propagation.REQUIRED) //支持当前事务,如果不存在则创建一个新的事务  
    public void changeName(){
        studentDao.updateNameById("test2",1);
        int i = 1/0; //模拟异常
    }
}
//事务传播
@Service
public class TopService {

    @Autowired
    private StudentService studentService;

    @Transactional
    public void  topService(){
        studentService.changeAge();
        studentService.changeName(); //changeName发生异常了,整个topService都会有异常。【异常会向上转发】
        //整个topService都会事务回滚。
    }
}

//测试
@SpringBoot
public class TxTest {

    @Autowired
    private StudentService studentService;

    @Autowired
    private TopService topService;

    @Test
    public void  testTx() throws FileNotFoundException {
        topService.topService(); //事务回滚
    }
}

注意:【一定关注异常的传播链】

在同一个类中,对于@Transactional注解的方法调用,事务传播行为不会生效。这是因为Spring框架中使用代理模式实现了事务机制,在同一个类中的方法调用并不经过代理,而是通过对象的方法调用,因此@Transactional注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果。

posted @ 2024-09-25 09:40  CH_song  阅读(4)  评论(0编辑  收藏  举报