1. 事务本身 ACID属性
-
原子性 Atomicity
- 事务所包含的全部操作是一个不可分割的整体,这些操作要么全部提交成功,要么只要其中一个操作失败,就全部失败。
-
一致性 Consistency
- 事务所包含的操作不能违反数据资源的一致性检查,数据资源在事务执行之前处于某个数据一致性状态,那么,事务执行之后也依然需要保持数据间的一致性状态。
-
隔离性 Isolation
- 主要规定了各个事务之间相互影响的程度。隔离性概念主要面向对数据资源的并发访问(Concurrency),并兼顾影响事务的一致性
-
隔离级别
- Read Uncommitted
- 一个事务可以读取另一个事务没有提交的更新结果
- Read Committed
- 一个事务的更新操作结果只有在该事务提交之后,另一个事务才可能读取到同一笔数据更新后的结果
- Repeatable Read
- 保证在整个事务的过程中,对同一笔数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,也不管其他事务对同一笔数据的更新提交与否
- Serializable
- 所有的事务操作都必须依次顺序执行,可以避免其他隔离级别遇到的所有问题,是最为安全的隔离级别。但同时也是性能最差的隔离级别
- Read Uncommitted
-
事务问题
- 脏读
- 如果一个事务对数据进行了更新,但事务还没有提交,另一个事务就可以“看到”该事务没有提交的更新结果。这样造成的问题就是,如果第一个事务回滚,那么第二个事务在此之前所“看到”的数据就是一笔脏数据。
- 不可重复读取
- 指同一个事务在整个事务过程中对同一笔数据进行读取,每次读取结果都不同。如果事务1在事务2的更新操作之前读取一次数据,在事务2的更新操作之后再读取同一笔数据一次,两次结果是不同的。 行级锁可以解决
- 幻读
- 幻读是指同样一个查询在整个事务过程中多次执行后,查询所得的结果集是不一样的。幻读针对的是多笔记录 表级锁才可以解决
- 一个事务在执行过程中读取到了另一个事务已提交的插入数据;即在第一个事务开始时读取到一批数据,但此后另一个事务又插入了新数据并提交,此时第一个事务又读取这批数据但发现多了一条,即好像发生幻觉一样。
- 脏读
-
持久性 Durability
- 一旦整个事务操作成功提交,对数据所做的变更将被记载并不可逆转,
2. Spring 事务
2.1 基本原则:让事务管理的关注点与数据访问关注点相分离
-
当在业务层使用事务的抽象API进行事务界定的时候,不需要关心事务将要加诸于上的事务资源是什么,对不同的事务资源的管理将由相应的框架实现类来操心
-
当在数据访问层对可能参与事务的数据资源进行访问的时候,只需要使用相应的数据访问API进行数据访问,不需要关心当前的事务资源如何参与事务或者是否需要参与事务。这同样将由事务框架类来打理
-
JDBC数据访问方式的局部事务
- 对于层次划分清晰的应用来说,我们通常都是将事务管理放在Service层,而将数据访问逻辑放在DAO层。这样做的目的是
- 可以不用因为将务管理代码放在DAO层,而降低数据访问逻辑的重用性
- 也可以在Service层根据相应逻辑,来决定提交或者回滚事务
- 对于层次划分清晰的应用来说,我们通常都是将事务管理放在Service层,而将数据访问逻辑放在DAO层。这样做的目的是
2.2 Spring支持事务管理的核心是事务管理器抽象,对于不同的数据访问框架(如Hibernate)通过实现策略接口PlatformTransactionManager,从而能支持各种数据访问框架的事务管理:
Spring事务抽象接口关系图-
TransactionDefinition 负责定义事务相关属性,包括隔离级别、传播行为等
- 事务的隔离(Isolation)级别
- 事务的传播行为(Propagation Behavior)
- 事务的超时时间(Timeout)
- 是否为只读(ReadOnly)事务
-
TransactionDefinition内定义了5个常量,隔离级别
- ISOLATION_DEFAULT 表示使用数据库默认的隔离级别
- ISOLATION_READ_UNCOMMITTED 无法避免脏读,不可重复读和幻读
- ISOLATION_READ_COMMITTED 可以避免脏读,无法避免不可重复读和幻读 oracle,sql server 默认
- ISOLATION_REPEATABLE_READ 可以避免脏读和不可重复读,无法避免幻读 mysql默认
- ISOLATION_SERIALIZABLE 以避免所有的脏读,不可重复读以及幻读,但并发性效率最低。
-
TransactionDefinition内定义了7个常量,传播行为(表示整个事务处理过程所跨越的业务对象,将以什么样的行为参与事务)
- PROPAGATION_REQUIRED 有事务就用已有的,没有就重新开启一个
- PROPAGATION_SUPPORTS 有事务就用已有的,没有也不会重新开启, 些查询方法适合
- 如果当前方法直接执行,那么不需要事务的支持。如果当前方法被其他方法调用,而其他方法启动了一个事务,使用PROPAGATION-SUPPORTS可以保证当前方法能够加入当前事务,并洞察当前事务对数据资源所做的更新。
- 如, A.service()会首先更新数据库,然后调用B.service()进行查询,那么,如果B.service()是PROPAGATION-SUPPORTS的传播行为,就可以读取A.service()之前所做的最新更新结果而如果使用稍后所提到的PROPAGATTON-NOT-SUPPORTED, 则B.service()将无法读取最新的更新结果,因为A.service()的事务在这时还没有提交(除非隔离级别是Read Uncommitted)。
- 如果当前方法直接执行,那么不需要事务的支持。如果当前方法被其他方法调用,而其他方法启动了一个事务,使用PROPAGATION-SUPPORTS可以保证当前方法能够加入当前事务,并洞察当前事务对数据资源所做的更新。
- PROPAGATION_MANDATORY 必须要有事务,没事务抛异常
-适合某个方法需要事务支持,但自身又不管理事务提交或者回滚 - PROPAGATION_REQUIRES_NEW 开启新事务,若当前已有事务,挂起当前事务
- 适合某个业务对象所做的事情不想影响到外层事务
- 如 假设当前的业务方法需要向数据库中更新某些日志信息,但即使这些日志信息更新失败,我们也不想因为该业务方法的事务回滚而影响到外层事务的成功提交,前业务方法的事务成功与否对外层事务来说是无关紧要的
- 适合某个业务对象所做的事情不想影响到外层事务
- PROPAGATION_NOT_SUPPORTED 不需要事务,若当前已有事务,挂起当前事务
- PROPAGATION_NEVER 不需要事务,若当前已有事务,抛出异常
- PROPAGATION_NESTED 嵌套事务,如果外部事务回滚,则嵌套事务也会回滚!!!外部事务提交的时候,嵌套它才会被提交。嵌套事务回滚不会影响外部事务。
-
TransactionDefinition 定义TIMEOUT_DEFAULT用来指定事务的超时时间,默认值为-1,这会采用当前事务系统默认的超时时间
-
TransactionDefinition isReadOnly方法是否事一个只读事务。只读事务给相应的ResourceManager提供一种优化的提示,但最终是否提供优化,则由具体的ResourceManager来决定,对于一些查询来说,我们通常会希望它们采用只读事务
-
TransactionDefinition 相关实现
- TransactionDefinition的相关实现类按照编程式事务场景和声明式事务场景两个分支
-
TransactionDefinition的相关实现类按照编程式事务场景和声明式事务场景两个分支继承层次图
- TransactionTemplate 是Spring提供的进行编程式事务管理的模板方法类
- TransactionAttribute 主要面向使用Spring AOP进行声明式事务管理的场合。
- boolean rollbackOn(Throwable var1); 声明的方式指定业务方法在抛出哪些的异常的情况下可以回滚事务。
- DefaultTransactionDefinition 增加了rollbackOn方法实现当异常类型为unchecked-exception的情况下将回滚事务
public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {
//unchecked-exception(将派生于Error或者RuntimeException的异常称为unchecked异常)
public boolean rollbackOn(Throwable ex) {
return ex instanceof RuntimeException || ex instanceof Error;
}
//.....
}
-
Transactionstatus 可以通过Transactionstatus对事务进行有限的控制。
- image.png
- TransactionStatus提供的相应方法查询事务状态。
- setRollbackOnly();方法标记当前事务以使其回滚
- 如果相应的PlatformTransactionManager支持Savepoint,可以通过Transactionstatus在当前事务中创建内部嵌套事务。
public interface TransactionStatus extends SavepointManager, Flushable {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}
-
PlatformTransactionManager 界定事务边界,为应用程序提供事务界定统一方式
- PlatformTransactionManager整个抽象体系基于策略模式,PlatformTransactionManager对事务界定进行统一抽象,而具体的界定策略的实现则交由具体的实现类。
-
PlatformTransactionManager实现类
- 面向局部事务
- 面向全局事务
-
面向局部事务的PlatformTransactionManager实现类
数据库访问技术 PlatformTransactionManager实现类实现类 JDBC/mybatis DataSourceTransactionManager hibernate HibernateTransactionManager jpa JpaTransactionManager rabbitmq RabbitTransactionManager jms JmsTransactionManager -
面向全局事务的PlatformrangactionManager实现类
- JtaTransactionManager 对各种JTA实现提供的分布式事务支持进行了统一封装,只不过它的所有的事务管理操作,最终都会委派给具体的JTA实现来完成
2.3 编程式事务管理
- Spring进行编程式事务管理有两种方式,要么直接使用DlatformmransactionManager,要么使用更方便的TransactionTemplate
- 不足 : 事务管理代码与业务逻辑代码相互混杂
- 将来用到再补充
2.4 声明式事务管理
- 描述:事务管理本身就是一种横切关注点,我们可以提供相应的Advice实现,然后织入到系统中需要该横切逻辑的joinpoint处,从而达到将事务管理逻辑从业务逻辑实现中剥离出来的目的
- 优点 : 事务管理代码不用再去影响具体业务逻辑的实现
- 方式
- xml元数据驱动的方式
- 注解元数据驱动的方式
- @Transactional
- 不生效场景
- 非public方法
- spring扫描会检查,方法的修饰符是否为public,非public时不会获取@Transactional的属性信息
- 同一个类,非@Transactional标识方法调用被@Transactional标识的方法
- 异常被service内部处理
- rollbackFor 属性设置不对,默认DefaultTransactionAttribute.rollbackOn(unchecked-exception)
- 数据库引擎不支持事务 如mysql 存储引擎 MYISAM
- 非public方法
- 参数说明
- 不生效场景
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
//传播行为
Propagation propagation() default Propagation.REQUIRED;
//事务隔离级别
Isolation isolation() default Isolation.DEFAULT; //数据库的默认隔离级别
//事务的超时秒数
int timeout() default -1;
//是否为只读事务
boolean readOnly() default false;
//设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚
Class<? extends Throwable>[] rollbackFor() default {};
//需要进行回滚的异常类名称数组 @Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
String[] rollbackForClassName() default {};
//不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) Exception.class})
Class<? extends Throwable>[] noRollbackFor() default {};
//不需要进行回滚的异常类名称数组 @Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})
String[] noRollbackForClassName() default {};
}
- xml元数据驱动的方式 demo
<tx:advice id="txManager" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED"
read-only="false" />
<tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED"
read-only="false" />
<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED"
read-only="false" />
<tx:method name="Update*" isolation="DEFAULT" propagation="REQUIRED"
read-only="false" />
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED"
read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.gerp.common.service.ServiceSupport+.*(..))"
id="perform" />
<aop:advisor pointcut-ref="perform" advice-ref="txManager" />
</aop:config>
网友评论