概述
事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。SpringFramework对事物管理提供了一致的抽象。
SpringFramework提供的抽象特点:
- 为不同的事物API提供一致的编程模型,比如:JTA(Java Transaction API)、jdbc、Hibernate、JPA(Java Persistence API)、JDO(Java Data Objects)
- 支持声明式事务管理,特别是基于注解的声明式事务管理,简单易用。
- 提供比其他事务API,如:JTA更简单的编程式事务管理API
- 与spring数据访问抽象的完美集成。
事务管理方式
spring支持编程式事务管理和声明式事务管理两种方式。编程式事务管理
使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。声明式事务管理
声明式事务管理建立在AOP之上。
本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后跟据执行情况提交或者回滚事务。
声明式事务管理的最大优点就是不需要通过编程的方法管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
使用方式:
- 基于tx和aop名字空间的xml配置文件。
- 基于@Transactional注解。
差别
声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。
声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。与编程式事务管理相比,声明式事务管理唯一不足的地方是:后者的最细粒度只能作用到方法级别,无法做到像编程式事务管理那样可以作用到代码块级别。自动提交(AutoCommit)与连接关闭时的是否自动提交
默认情况下,数据库处于自动提交状态下。每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
对于正常的事务管理,是一组相关的操作处于一个事务中,因此必须关闭数据库的自动提交模式。不过,这个不用我们担心,spring会将底层连接的自动提交特性设置为false。// switch to manual commit if necessary. this is very expensive in some jdbc drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getautocommit()) {
txobject.setmustrestoreautocommit(true);
if (logger.isdebugenabled()) {
logger.debug("switching jdbc connection [" + con + "] to manual commit");
}
con.setautocommit(false);
}
基于注解的声明式事务管理配置
```xml
还要在applicationContext.xml中添加tx名字空间:
```xml
...
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
...
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
...
MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可。否则事务管理会不起作用。
spring事务特性
事务隔离级别
概念:
若干个并发的事务之间的隔离程度。
TransactionDefinition接口中定义了五个表示隔离级别的常量:
- TransactionDefinition.ISOLATION_DEFAULT(默认值)
- 表示使用底层数据库的默认隔离级别。
- 对于大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED
- 表示一个事务可以读取另一个事务修改但还没有提交的数据。
- 该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。
- 比如PostgreSQL实际上并没有此级别。
- TransactionDefinition.ISOLATION_READ_COMMITTED
- 表示一个事务只能读取另一个事务已经提交的数据。
- 该级别可以防止脏读,这也是大多数情况下的推荐值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ
- 表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。
- 该级别可以防止脏读和不可重复读。
TransactionDefinition.ISOLATION_SERIALIZABLE
TransactionDefinition.PROPAGATION_REQUIRED(默认)
- 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW
- 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_SUPPORTS
- 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED
- 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- TransactionDefinition.PROPAGATION_NEVER
- 以非事务方式运行,如果当前存在事务,则抛出异常。
- TransactionDefinition.PROPAGATION_MANDATORY
- 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED
- 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
事务超时
概念:
指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。
在TransactionDefinition中以int的值来表示超时时间,单位是秒。
默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。spring事务回滚规则
spring使用声明式事务管理,默认情况下,如果被注解的数据库操作方法中出现了unchecked异常,所有的数据库操作将rollback(回滚),如果发生的异常是checked异常,默认情况下数据库操作还是会提交的。checked异常(Exception)
表示无效,不是程序中可以预测的。
比如:
- 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED
文件不存在
- 无效的用户输入
- 网络或者数据库链接错误
必须在try-catch块处理,或者给所在的方法加上throws说明,将异常抛到栈的上一层。继承自java.lang.Exception(java.lang.RuntimeException除外)
unchecked异常(RuntimeException)
表示错误,程序的逻辑错误。
是RuntimeException的子类。
比如:
- IllegalArgumentException
- NullPointerException
- IllegalStateException
不需要在代码中显式地铺货unchecked异常做处理。继承自java.lang.RuntimeException
注意
默认情况下,spring会对unchecked(RunTimeException、error)异常进行事务回滚;但是checked(Exception)异常则不回滚。
如何让checked异常也回滚?
配置@Transactional(rollbackFor = Exception.class)Exception是RunTimeException的父类,这样配置就包含了(checked和unchecked)。
@Transactional注解
属性 | 类型 | 描述 |
---|---|---|
value | String | 可选的限定修饰符,指定使用的事务管理器 |
propagation | enum:Propagation | 可选的事务传播行为设置 |
isolation | enum:Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int(in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须集成自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
注意
- @Transactional可以作用于接口、接口方法、类、类方法上。当做用类上时,该类的所有public方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
- 虽然@Transactional注解可以作用于接口、接口方法、类、类方法上,但是spring建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。
- @Transactional注解应该只被应用在public方法上,这是由spring AOP的本质决定的。如果在protected、private或默认可见的方法上使用@Transactional注解,这将被忽略,也不会抛出异常。
- 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。
转载
https://juejin.cn/post/6844903873207861256#heading-11