- 事务的概念:比如插入数据库分两段插入,第二次插入错误,第一次插入操作也需要回退;
- 事务的属性
- Atomicity 原子性:指的是整个事务是一个独立的单元,要么操作成功,要么操作不成功
- Consistency 一致性:事务必须要保持和系统处于一致的状态(如果不一致会导致系统其它的方出现bug)
- Isolation 隔离性:事务是并发控制机制,他们的交错也需要一致性,隔离隐藏,一般通过悲观或者乐观锁实现
- Durability 持久性:一个成功的事务将永久性地改变系统的状态,所以在它结束之前,所有导致状态的变化都记录在一个持久的事务日志中
- 事务管理方式
- 编程式事务管理:编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
- 声明式事务管理:
- 声明式事务管理建立在AOP之上的,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
- 声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
- 编程式事务管理 和 声明式事务管理 对比
- 声明式事务管理要优于编程式事务管理,这正是因为 Spring 倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。
- 和编程式事务管理相比,声明式事务管理唯一不足地方是,声明式事务管理 的最细粒度只能作用到方法级别,无法做到像编程式事务管理那样可以作用到代码块级别。但即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等。
- 自动提交(AutoCommit)与连接关闭时的是否自动提交
- 自动提交:默认情况下,数据库处于自动提交模式。每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。对于正常的事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。不过,这个我们不用担心,spring会将底层连接的自动提交特性设置为false。
-
连接关闭时的是否自动提交:当一个连接关闭时,如果有未提交的事务应该如何处理?JDBC规范没有提及,C3P0默认的策略是回滚任何未提交的事务。这是一个正确的策略,但JDBC驱动提供商之间对此问题并没有达成一致。C3P0的autoCommitOnClose属性默认是false,没有十分必要不要动它。或者可以显式的设置此属性为false,这样会更明确。
- 在启动类上添加 @EnableTransactionManagement 注解。
- 在想要使用事物的 方法/类 上添加 @Transactional 注解。
- 用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
- 在项目中 @Transactional(rollbackFor=Exception.class),如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
- 在@Transactional注解中如果不配置rollbackFor属性,那么事物只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。
| 属性名 | 说明 |
|---|---|
| name | 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事物管理器。 |
| propagation | 事务的传播行为,默认值为 REQUIRED。 |
| isolation | 事务的隔离度,默认值采用 DEFAULT。 |
| timeout | 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
| read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
| rollback-for | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。 |
| no-rollback-for | 抛出 no-rollback-for 指定的异常类型,不回滚事务。 |
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
| 事务传播行为类型 | 说明 |
|---|---|
| PROPAGATION_REQUIRED | 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值 |
| PROPAGATION_REQUIRES_NEW | 创建一个新的事务,如果当前存在事务,则把当前事务挂起 |
| PROPAGATION_SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行 |
| PROPAGATION_NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则把当前事务挂起 |
| PROPAGATION_NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常 |
| PROPAGATION_MANDATORY | 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常 |
| PROPAGATION_NESTED | 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED |
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
| 事务传播行为类型 | 说明 |
|---|---|
| ISOLATION_DEFAULT | 这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED |
| ISOLATION_READ_UNCOMMITTED | 该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。 |
| ISOLATION_READ_COMMITTED | 该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值 |
| ISOLATION_REPEATABLE_READ | 该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读 |
| ISOLATION_SERIALIZABLE | 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别 |
- @Transactional 应用在非 public 修饰的方法上:事务拦截器在目标方法执行前后进行拦截,内部会调用方法来获取Transactional 注解的事务配置信息,调用前会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息
- @Transactional 注解属性 rollbackFor 设置错误:rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定rollbackFor属性。
- 同一个类中方法调用,导致@Transactional失效:开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。
- 异常被catch“吃了”导致@Transactional失效:如果手动的catch捕获这个异常并进行处理,事务管理器会认为当前事务应该正常commit,就会导致注解失效,如果非要捕获且不失效,就必须在代码块内throw new Exception抛出异常。
- 数据库引擎不支持事务:开启事务的前提就是需要数据库的支持,我们一般使用的Mysql引擎时支持事务的,所以一般不会出现这种问题。
- 开启多线程任务时,事务管理会受到影响:因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。
注:以上内容仅提供参考和交流,请勿用于商业用途,如有侵权联系本人删除!



