了解spring事务 看这篇文章就足够了 spring 数据库事务概述
事务的特点
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。事务具有的优点是:把逻辑相关的操作分成了一个组;在数据永久改变前,可以预览数据变化;能够保证数据的读一致性。
性质(ACID)
1.原子性(Atomicity):原子性是指一个事务中的操作,要么全部成功,要么全部失败,如果失败,就回滚到事务开始前的状态。
2.一致性(Consistency):一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。那转账举栗子,A账户和B账户之间相互转账,无论如何操作,A、B账户的总金额都必须是不变的。
3.隔离性(Isolation):隔离性是当多个用户 并发的 访问数据库时,如果操作同一张表,数据库则为每一个用户都开启一个事务,且事务之间互不干扰,也就是说事务之间的并发是隔离的。再举个栗子,现有两个并发的事务T1和T2,T1要么在T2开始前执行,要么在T2结束后执行,如果T1先执行,那T2就在T1结束后在执行。关于数据的隔离性级别,将在后文讲到。
4.持久性(Durability):持久性就是指如果事务一旦被提交,数据库中数据的改变就是永久性的,即使断电或者宕机的情况下,也不会丢失提交的事务操作。
传播行为
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。
传播行为主要是影响事务的提交状态。
比如有两个事务,事务A和事务B,事务B的过程中调用了事务A,当事务A发生了异常,事务B如何处理事务,这个就是我们要定义的事务传播行为。
而在实际的处理过程中,事务A和事务B都是虚拟的,只是为最后事务处理结果提供一个记录,我们通过事务传播行为来影响最终的结果。
-
PROPAGATION_REQUIRED
必须存在一个事务,如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。内部事务会影响外部事物;
内部事务会影响外部事物; -
PROPAGATION_SUPPORTS
如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。 -
PROPAGATION_REQUIRES_NEW
它会开启一个新的事务。如果一个事务已经存在,则先将这个存在的事务挂起。
独立的physical事务。使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作为事务管理器。 -
PROPAGATION_NOT_SUPPORTED
总是非事务地执行,并挂起任何存在的事务。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。 -
PROPAGATION_NEVER
总是非事务地执行,如果存在一个活动事务,则抛出异常。 -
PROPAGATION_NESTED
必须在事务中执行,如果不存在则开启一个事务,
事务之间户不影响(savepoints),每个事务之间有个savepoints,事务可以回滚到上一个savepoints。
如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。
需要JDBC 驱动的java.sql.Savepoint类。使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true(属性值默认为false)。 -
PROPAGATION_MANDATORY
如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
隔离级别
要了解隔离级别,我们应该先想清楚一下要解决的问题
- 脏读 : 一个事务读取到另一事务未提交的更新数据
- 不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据. 相反, "可重复读"在同一事务中多次读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据
- 幻读 : 一个事务读到另一个事务已提交的insert数据
Spring 隔离级别设置
-
ISOLATION_DEFAULT: 默认
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。 -
ISOLATION_READ_UNCOMMITTED: 读未提交
这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 -
ISOLATION_READ_COMMITTED: 读已提交
保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。 -
ISOLATION_REPEATABLE_READ: 可重复读
这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。 -
ISOLATION_SERIALIZABLE: 串行化
这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。
事务管理
事务管理方面我们会面临一些问题,比如:多事务管理,每个方法都需要写事务就很麻烦;组件使用方式不一致,不同的组件代码书写方式同,比如jdbc 和 hibernate,从而使得我们在切换组件时变得很困难。这时候我们就需要用到强大的spring事务管理了。spring事务管理提供了统一的事务编程模式以及编程式事务及声明式事务(AOP)。为我们解决数据库事务的管理问题。
spring事务原理
抽象接口 PlatformTransactionManager
public interface PlatformTransactionManager {
//事务管理器通过TransactionDefinition,获得“事务状态”,从而管理事务
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
//根据状态提交
void commit(TransactionStatus var1) throws TransactionException;
//根据状态回滚
void rollback(TransactionStatus var1) throws TransactionException;
}
- getTransaction方法(获取事务)
传入 TransactionDefinition 事务定时对象
返回 TransactionStatus 事务状态对象 - commit方法(事务提交)
传入 TransactionStatus 事务状态对象 - rollback方法(事务回滚)
传入 TransactionStatus 事务状态对象
具体实现
有两个重要的事务管理器 DataSourceTransactionManager 和 HibernateTransactionManager
DataSourceTransactionManager 是以JDBC实现的
HibernateTransactionManager 是以Hibernate实现的
TransactionDefinition是用来定义事务的,其中的方法包括:
- getName: 事务名称
- getIsolationLevel: 隔离级别
- getPropagationBehavior: 传播行为
- getTimeout: 超时时间
- isReadOnly:是否只读事务(JDBC 一般用不到)
TransactionStatus是用来存储和修改事务状态的,其中的方法包括:
- isNewTransaction: 是否是新事务
- hasSavepoint: 是否有检查点(诊断,NESTED)
- isComplated: 是否已完成
- isRollbackOnly: 事务结果是否是 rollbacl-only(不会commit)
- setRollbackOnly: 设置 rollback-only(TransactionTemplate)
spring 事务的使用方式
声明式事务
-
添加schema,需要在项目xml中添加aop 和 tx的schame
-
定义事务管理器,不同的连接用不同的事务管理器,如jdbc 和 hibernate
-
定义advice,来指定不同的事务性设置
-
定义pointcut,aop的切入点
注解式事务
@Transactional
事物传播行为介绍:
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
事物超时设置:
@Transactional(timeout=30) //默认是30秒
事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)
串行化
MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED