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)
数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:
- 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
- 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
- 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
- 串行化(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注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果。