spring 事务管理
事务
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。 事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。就像从小父母教育我们要有始有终,不能半途而废。
事务的ACID
事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。
1 、原子性
事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
2 、一致性
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。
比如转账:A转给B5000块,A的账号扣了钱,但是B的账号没有增加。
3 、隔离性
一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。比如我们在updata的那一刹那,有可能别人提交了一个delete对于同一个数据进行操作。可以想象这样会出麻烦,必须加以控制,让他们顺序的执行,锁进行控制。保证数据库操作之间是隔离的,没有干扰的。隔离的级别越高,那么并发性,吞吐量肯定会收到影响。数据库专家们制定了数据库隔离的规范,防止脏读,不可重复读..
4 、持续性
也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
事务隔离面临的影响
脏读:事务A读取了事务B未提交的数据
不可重复读:事务A读取了事务B已提交更改的数据。
幻读:事务A读取了事务B已提交新增的数据。
事务隔离性的定义:Isolation: Concurrently executing transactions see the stored information as if they were running serially (one after another)
事务的隔离级别从低到高有:
Read Uncommitted:最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生。
Read Committed:只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
Repeated Read:在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。
Serialization:事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。 通常,在工程实践中,为了性能的考虑会对隔离性进行折中。
考虑到实践,为了性能,数据库厂商做出了这方面的妥协,让使用者可以选择隔离的级别。不同的隔离级别可以解决不同阶段的问题,是层层递进,逐渐增强的关系。
隔离性为了解决的问题主要有三个(将事务的隔离级别和问题联系在一起理解):
脏读(Drity Read):事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。Read Committed可以解决脏读问题,但仍存在以下两种问题。
不可重复读(Non-repeatable read): 在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这导致锁竞争加剧,影响性能。Repeated Read可以解决不可重复读问题和脏读问题,但仍无法解决下面的问题。
幻读(Phantom Read): 在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读仅指由于并发事务增加记录导致的问题,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。Serialization解决了以上所有问题,但是性能效率较低。通常来说,事务隔离级别越低,所需持有锁的时间也就越短,并发性能也就越好。
不可重复读的例子:
不可重复读的重点是修改:同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
con1 = getConnection();
select salary from employee empId = "Mary" ;
在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
con2 = getConnection();
update employee set salary = 2000 ;
con2.commit();
在事务1中,Mary 再次读取自己的工资时,工资变为了2000
select salary from employee empId = "Mary" ;
在一个事务中前后两次读取的结果并不致,导致了不可重复读。
幻读例子:
幻读:幻读的重点在于新增或者删除 同样的条件, 第1次和第2次读出来的记录数不一样
目前工资为1000的员工有10人。
事务1,读取所有工资为1000的员工。
con1 = getConnection();
Select * from employee where salary = 1000 ;
共读取10条记录
这时另一个事务向employee表插入了一条员工记录,工资也为1000
con2 = getConnection();
Insert into employee(empId,salary) values( "Lili" , 1000 );
con2.commit();
事务1再次读取所有工资为1000的员工
select * from employee where salary = 1000 ;
共读取到了11条记录,这就产生了幻像读。
spring 事务的隔离级别(方法间调用)
ISOLATION_DEFAULT:数据库默认
ISOLATION_COMMITTED:允许读取其他并发事务已经提交的更新(防此脏读)
ISOLATION—READ_UNCOMMITTED允许读取其他并发事务还未提交的更新,会导致事务之间的3个缺陷发生,这是速度最快的一个隔离级别,但同时它的隔离级别也是最低
ISOLATION_SERIALIZABLE 这是最高的隔离级别,它可以防此脏读,不可重复读和 幻读等问题,但因其侵占式的数据记录完全锁定,导致它影响事务的性能,成为隔离级别中最展慢的一个。 注意:并不是所有的资源管理器都支持所有的隔离级别,可针对不同的资源管理使用以上的隔离级别.
事务的传播行为
PROPAGATION_MANDATORY: (强制性,传播)
规定了方法必须在事务中运行,否则会抛出异常
PROPAGATION_NEVER(调用的方法不能有事务)
使当前方法永远不在事务中运行,否则抛出异常
PROPAGATION-NOT_SUPPORTED
定义为当前事务不支持的方法,在该方法运行期间正在运行的事务会被暂停
PROPAGATION_REQUIRED(够用)
规定当前的方法必须在事务中,如果没有事务就创建一个新事务,一个新事务和方法一同开始,随着方法的返回或抛出异常而终止
PROPAGATION-REQUIRED_NEW
当前方法必须创建新的事务来运行,如果现存的事务正在运行就暂停它
PROPAGATION_SUPPORTS
规定当前方法支持当前事务处理,但如果没有事务在运行就使用非事务方法执行
事务的只读属性
在对数据库的操作中,查询是使用最频繁的操作,每次执行查询时都要从数据库中重新读取数据,有时多次读取的数据都是相同的,这样的数据操作不仅浪费了系统资源,还影响了系统速度。对访问量大的程序来说,节省这部分资源可以大大提升系统速度。如果将事务声明为只读的,那么数据库可以根据事务的特性优化事务的读取操作。事务的只读属性需要配合事务的传播行为共同设置.
事务超时属性
这个属性和事务的只读属性一样需要搭配事务的传播行为共同设置,它设置了事务的超时时间,事务本身可能会因某种原因很长没有回应,在这期间事务可能锁定了数据库的表格,这样会出现严重的性能问题。通过设置事务的超时时间,从开始执行事务起,在规定的超时时间内如果没有事务就将它回滚。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
String value() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
spring 事务管理器
都实现了PlatformTransactionManager接口
DataSourceTransactionManager JDBC事务管理器
HibernateTransactionManager Hibernate事务管理器
<!-- 配置JDBC事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!-- 配置Hibernate事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
启动注解,@Transactional
<tx:annotation-driven transaction-manager="transactionManager"/>
使用AOP的方式实现事务的配置
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<!--配置事务传播性,隔离级别以及超时回滚等问题 -->
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="*" rollback-for="java.lang.Exception" timeout="-1" isolation="READ_COMMITTED" read-only="true" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<!--配置事务切点 -->
<aop:pointcut id="services"
expression="execution(* com.website.service.*.*(..))" />
<aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
</aop:config>
本文摘自 :https://blog.51cto.com/u