事务具有最重要的两个特性:Spring事务的传播级别和数据库事务的隔离级别。传播级别定义控制范围,隔离级别定义数据库的读写等方面。
Spring事务的传播级别:
1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启,。适用与大多数场景。
2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
3. PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
4. PROPAGATION_REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。新建事务完成之后再恢复执行。
5. PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
6. PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
7. PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中, 如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
数据库事务的隔离级别:
事务的隔离级别是基于并发情况下操作数据遇到的异常而言的。这些异常情况有:
1、 Dirty reads---读脏数据:也就是说,事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
2、 non-repeatable reads---数据不可重复读:比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
3、 phantom reads---幻读数据:和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据)
4、 Lost update---更新丢失:两个事务都同时更新一行数据但是第二个事务却中途失败退出导致对数据两个修改都失效了这是系统没有执行任何锁操作因此并发事务并没有被隔离开来.
5、 Second lost updates problem---两次更新:无法重复读取的特例,有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交而另一个也进行了修改提交,这就会造成第一次的写操作失效
对于解决以上问题,数据库设置了隔离级别相应的来处理这些问题:

×:表示解决的问题 √:表示可能引起的问题
各级别详解:
第1级别:Read Uncommitted (读取未提交内容)
(1) 所有事务都可以看到其他未提交事务的执行结果
(2) 本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少
(3) 该级别引发的问题是——脏读(Dirty Read):读取到了未提交的数据
#首先,修改隔离级别 set tx_isolation='READ-UNCOMMITTED'; select @@tx_isolation; +-------------------------------+ | @@tx_isolation | +-------------------------------+ | READ-UNCOMMITTED | +-------------------------------+ |
#事务A:启动一个事务 start transaction; select * from tx; +---------+--------+ | id | num | +---------+--------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +---------+--------+ | #事务B:也启动一个事务,在事务B中执行更新语句,不提交 start transaction; update tx set num=10 where id=1; select * from tx; +--------+--------+ | id | num | +---------+--------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +---------+--------+ |
#事务A:那么这时候事务A能看到这个更新了的数据吗? select * from tx; +--------+--------+ | id | num | +---------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +->可以看到,说明我们读到了事务B还没有提交的数据 | #事务B:事务B回滚,仍然未提交 rollback; select * from tx; +--------+--------+ | id | num | +---------+---------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +----------+--------+ |
#事务A:在事务A里面看到的也是B没有提交的数据 select * from tx; +---------+--------+ | id | num | +---------+--------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +-->脏读意味着我在这个事务中(A中),事务B虽然没有提交,但它任何一条数据变化,我都可以看到! |
第2级别:Read Committed(读取提交内容)
(1) 这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)
(2) 它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变
(3) 这种隔离级别出现的问题是——不可重复读(Nonrepeatable Read):不可重复读意味着我们在同一个事务中执行完全相同的select语句时可能看到不一样的结果。
导致这种情况的原因可能有:①有一个交叉的事务有新的commit,导致了数据的改变; ②一个数据库被多个实例操作时,同一事务的其他实例在该实例处理其间可能会有新的commit。
#修改隔离级别 set tx_isolation='read-committed'; select @@tx_isolation; +--------------------------+ | @@tx_isolation | +--------------------------+ | READ-COMMITTED | +---------------------------+ |
#事务A:启动一个事务 start transaction; select * from tx; +---------+---------+ | id | num | +---------+---------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +---------+--------+ | #事务B:也启动一个事务,在这事务中更新数据,不提交 start transaction; update tx set num=10 where id=1; select * from tx; +---------+---------+ | id | num | +---------+---------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +---------+---------+ |

第3级别:Repeatable Read(可重读)
(1) 这是MySQL的默认事务隔离级别;
(2) 它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行;
(3) 此级别可能出现的问题——幻读(Phantom Read):当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影”行;
(4) InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
#更改隔离级别 set tx_isolation='repeatable-read'; select @@tx_isolation; +------------------------+ | @@tx_isolation | +-------------------------+ | REPEATABLE-READ | +-------------------------+ |
#事务A:启动一个事务 start transaction; select * from tx; +--------+---------+ | id | num | +--------+---------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +--------+---------+ | #事务B:开启一个新事务,在事务B中更新数据,并提交 start transaction; update tx set num=10 where id=1; select * from tx; +--------+---------+ | id | num | +--------+---------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +--------+---------+ commit; |
#事务A:这时候即使事务B已经提交了,但A不能看到数据变化 select * from tx; +--------+---------+ | id | num | +--------+---------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +-> (这个级别2不一样,也说明级别3解决了不可重复读问题) | #事务A:只有当事务A也提交了,它才能够看到数据变化 commit; select * from tx; +--------+---------+ | id | num | +--------+---------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +--------+---------+ |
第4级别:Serializable(可串行化)
(1) 这是最高的隔离级别;
(2) 通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。它是在每个读的数据行上加上共享锁;
(3) 在这个级别,可能导致大量的超时现象和锁竞争。并发量大的时候,就会死掉。
#首先修改隔离级别 set tx_isolation='serializable'; select @@tx_isolation; +----------------------+ | @@tx_isolation | +-----------------------+ | SERIALIZABLE | +-----------------------+ |
#事务A:开启一个新事务 start transaction; |
#事务B:在A没有commit之前,这个交叉事务是不能更改数据的 start transaction; insert tx values('4','4'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction update tx set num=10 where id=1; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
小结:在项目中配置事务------注释方式
1、配置文件
<!-- 定义事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!--使用注释事务 --> <tx:annotation-driven transaction-manager="transactionManager" /> |
注意:使用不同的orm框架,事务管理器类class就不同
2、在项目中加@Transactional注释:
(1)放在类名称上,这样配置的@Transactional 对这个类中的所有public方法都起作用;
(2)Transactional 在方法名上,只对这个方法有作用,但必须是public的方法;
3、事物中的属性配置:
(1)事务的传播性:@Transactional(propagation=Propagation.REQUIRED)
(2)事务的超时性:@Transactional(timeout=30) //默认是30秒
(3)事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
(4)事务:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:
@Transactional(rollbackFor={RuntimeException.class, Exception.class})
(5)只读(true表示只读,默认false表示可读写):ransactional(readOnly=true)
注意:
在spring配置文件中引入<tx:>命名空间: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context ttp://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> |