【spring】Spring事务
本篇内容由xmind转为md文档
Spring事务
事务的特性
原子性(A)
一致性(C)
隔离性(I)
持久性(D)
事务的配置方式
Spring支持编程式事务管理以及声明式事务管理两种方式。
编程式事务(入侵式)
2.侵入性事务管理
3.粒度为代码块:编程式事务管理是可以到代码块的;
- 1.使用TransactionTemplate(spring推荐)或PlatformTransactionManager
- 2.侵入性事务管理
- 3.粒度为代码块:编程式事务管理是可以到代码块的;
声明式事务(无入侵式)
1.Spring倡导的非入侵方式;
2.实现方式:配置文件中做事务声明/注解;
3.粒度为方法级别(不足);
4.可以通过提取方法的方式完成声明式事务管理的配置(解决粒度为方法级别的不足)
- 1.Spring倡导的非入侵方式,建立在AOP之上,其本质是对方法前后进行拦截;
- 2.实现方式:配置文件中做事务声明/注解;
- 3.粒度为方法级别(不足);
- 4.可以通过提取方法的方式完成声明式事务管理的配置(解决粒度为方法级别的不足)
事务的传播机制
1.事务的传播性一般用在事务嵌套的场景,比如一个事务方法里面调用了另外一个事务方法,那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行。
2.传播规则回答了这样一个问题:一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行。
PROPAGATION_REQUIRED(默认)
- Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行
PROPAGATION_REQUES_NEW
- 该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可
PROPAGATION_SUPPORT
- 如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务
PROPAGATION_NOT_SUPPORT
- 该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
PROPAGATION_NEVER
- 该传播机制不支持外层事务,即如果外层有事务就抛出异常
PROPAGATION_MANDATORY
- 与NEVER相反,如果外层没有事务,则抛出异常
PROPAGATION_NESTED
- 该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。
事务的隔离级别
事务的隔离级别定义一个事务可能受其他并发事务活动影响的程度,可以把事务的隔离级别想象为这个事务对于事物处理数据的自私程度。
并发事务引发问题
-
脏读(Dirty read)
- 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。
-
不可重复读(Nonrepeatable read)
- 不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。
-
幻读(Phantom reads)
- 幻读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。
隔离级别
-
ISOLATION_DEFAULT
- 使用后端数据库默认的隔离级别
-
ISOLATION_READ_UNCOMMITTED
- 允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。
-
ISOLATION_READ_COMMITTED
- (Oracle 默认级别)允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。
-
ISOLATION_REPEATABLE_READ
- (MYSQL默认级别)对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。
-
ISOLATION_SERIALIZABLE
- 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。
回滚规则
在默认设置下,事务只在出现运行时异常(runtime exception)时回滚,而在出现受检查异常(checked exception)时不回滚(这一行为和EJB中的回滚行为是一致的)。
不过,可以声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。
默认设置:事务只在出现运行时异常(runtime exception)时回滚
在出现受检查异常(checked exception)时不回滚(这一行为和EJB中的回滚行为是一致的)。
事务超时
为了使一个应用程序很好地执行,它的事务不能运行太长时间。因此,声明式事务的下一个特性就是它的超时。
假设事务的运行时间变得格外的长,由于事务可能涉及对数据库的锁定,所以长时间运行的事务会不必要地占用数据库资源。这时就可以声明一个事务在特定秒数后自动回滚,不必等它自己结束。
由于超时时钟在一个事务启动的时候开始的,因此,只有对于那些具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、ROPAGATION_NESTED)的方法来说,声明事务超时才有意义。
超时:声明式事务的一个特性
声明一个事务在特定秒数后自动回滚
- 事务可能涉及对数据库的锁定,所以长时间运行的事务会不必要地占用数据库资源,这时就可以声明一个事务在特定秒数后自动回滚,不必等它自己结束。
超时计时在一个事务启动的时候开始
- 只有对于那些具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、ROPAGATION_NESTED)的方法来说,声明事务超时才有意义。
只读
如果一个事务只对数据库执行读操作,那么该数据库就可能利用那个事务的只读特性,采取某些优化措施。通过把一个事务声明为只读,可以给后端数据库一个机会来应用那些它认为合适的优化措施。由于只读的优化措施是在一个事务启动时由后端数据库实施的, 因此,只有对于那些具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED)的方法来说,将事务声明为只读才有意义。
条件:事务只对数据库执行“读”操作
- 通过把一个事务声明为只读,可以给后端数据库一个机会来应用那些它认为合适的优化措施。
数据库对只读的优化措施是在一个事务启动时由后端数据库实施的
- 只有对于具有可能启动一个新事务的传播行为(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED)的方法来说,将事务声明为只读才有意义
Spring声明式事务配置参考
1.事务的传播性:
@Transactional(propagation=Propagation.REQUIRED)
2.事务的隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
3.只读:
@Transactional(readOnly=true)
- 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。
4.事务的超时性:
@Transactional(timeout=30)
5.回滚:
5.1 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
5.2 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
spring事务基于spring AOP实现,spring AOP底层实现原理是 jdk 动态代理,需要进一步了解jdk动态代理和cglib代理,以及spring源码
事务失效
方法不是 public 的
- @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
数据库引擎不支持事务
- 以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。
没有被 Spring 管理
- 若类没有被spring管理(比如类上面没有@Service、@Controller等修饰),事务不生效
自身调用问题
- 类内部方法互相调用时,会导致事务失效问题,可以在类或者每个方法加@Transactional注解,或者
数据源没有配置事务管理器
异常被吃
- 在添加@Transactional的方法中进行了try catch,没有将异常抛出
异常类型错误
- 事务默认回滚的是:RuntimeException,若要触发其他异常的回滚需要在注解上配置rollbackfor,如:@Transactional(rollbackFor =Exception.class)