spring事务管理

五、spring事务管理(声明式事务IOC和AOP)

事务:一组DML的操作

1.事务有四个特性:ACID

原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

2.事务的五个维度

事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面
在这里插入图片描述

事务传播行为(事务嵌套当中)

事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:

传播行为含义
PROPAGATION_REQUIRED表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
PROPAGATION_SUPPORTS表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
PROPAGATION_MANDATORY表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_REQUIRED_NEW表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NOT_SUPPORTED表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

事务隔离级别

事务的第二个维度就是隔离级别(isolation level)。隔离级别定义了一个事务可能受其他并发事务影响的程度。

(1)并发事务引起的问题

在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致一下的问题。

  • 脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
    解决办法:把数据库的事务隔离级别调整到READ_COMMITTED
  • 不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
    解决 办法:把数据库的事务隔离级别调整到REPEATABLE_READ
  • 幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
    解决办法:把数据库的事务隔离级别调整到SERIALIZABLE_READ
    不可重复读与幻读的区别
    不可重复读的重点是修改
    在同一事务当中, 你读取过的数据, 再次读取出来发现值不一样了
    例如:在事务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
    //con1 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的员工
    //con1 select * from employee where salary =1000;
    共读取到了11条记录,这就产生了幻像读。
    从总的结果来看, 似乎不可重复读和幻读都表现为两次读取的结果不一致。但如果你从控制的角度来看, 两者的区别就比较大。
    对于前者, 只需要锁住满足条件的记录。
    对于后者, 要锁住满足条件及其相近的记录。
(2)隔离级别
隔离级别含义
ISOLATION_DEFAULT使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的

只读

事务的第三个特性是它是否为只读事务。如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。

事务超时

为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。

回滚规则

事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的)
但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。

4.spring事务管理

Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现

编程式事务管理
TransactionTemplate

声明式事务管理

  • XML配置方式实现
<!--配置阿里druid数据源,需要导入druid的jar包-->	
	<bean id="DruidDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"		destroy-method="close">		
	<!-- 基本属性 url、user、password -->		
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>		
		<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />		
		<property name="username" value="root" />		
		<property name="password" value="123" />		
		<!-- 配置初始化大小、最小、最大 -->		
		<property name="initialSize" value="1" />		
		<property name="minIdle" value="1" />		
		<property name="maxActive" value="20" />		
		<!-- 配置获取连接等待超时的时间 -->		
		<property name="maxWait" value="60000" />		
		<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->		
		<property name="timeBetweenEvictionRunsMillis" value="60000" />		
		<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->		
		<property name="minEvictableIdleTimeMillis" value="300000" />		
		<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->		
		<property name="poolPreparedStatements" value="true" />		
		<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />		
		<!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->		
		<property name="filters" value="stat" />	
	</bean>	
	<!-- 添加spring事务管理器 -->	
	<bean id="transactionManager" 	class="org.springframework.jdbc.datasource.DataSourceTransactionManager">		
		<property name="dataSource" ref="DruidDataSource"/>	
	</bean>	
	<!-- 定义spring aop通知 -->	
	<tx:advice id="txAdvice" transaction-manager="transactionManager">		
		<tx:attributes>			
			<tx:method name="insert*" propagation="REQUIRED"/>			
			<tx:method name="add*" propagation="REQUIRED"/>			
			<tx:method name="update*" propagation="REQUIRED"/>			
			<tx:method name="modify*" propagation="REQUIRED"/>			
			<tx:method name="delete*" propagation="REQUIRED" />			
			<tx:method name="find*" read-only="true"/>			
			<tx:method name="get*" read-only="true"/>			
			<tx:method name="select*" read-only="true"/>		
		</tx:attributes>	
	</tx:advice>	
	<!-- 定义切入点,引用通知 -->	
	<aop:config>		
		<aop:pointcut expression="execution(* org.lanqiao.service.*.*(..))" id="mypointcut"/>	
		<aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/>	
	</aop:config>
属性类型默认值说明
propagationPropagation枚举REQUIRED事务传播属性
isolationisolation枚举DEFAULT(所用数据库默认级别)事务隔离级别
readOnlybooleanfalse是否才用优化的只读事务
timeoutint-1超时(秒)
rollbackForClass[]{}需要回滚的异常类
rollbackForClassNameString[]{}需要回滚的异常类名
noRollbackForClass[]{}不需要回滚的异常类
noRollbackForClassNameString[]{}不需要回滚的异常类名
  • 注解方式实现
    <tx:annotation-driven transaction-manager=“transactionManager”/>

          @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    

@Transcational 的工作原理:

默认情况下,数据库处于自动提交模式,每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。这点,Spring会在org/springframework/jdbc/datasource/DataSourceTransactionManager.java中将底层连接的自动提交特性设置为false

注解 @Transcational:

@Transcational
默认情况下,只有方法出现的 是RuntimeException或Error以及它们的子类时(未检查异常),才会导致事务回滚 如果要改变默认情况
@Transactional(rollbackFor=异常类.class)
那么方法如果出现了该异常,或该异常的子类异常时,就会回滚 @Transactional(noRollbackFor=异常类.class)
当遇到这种异常时,不会回滚事务
注意:在业务方法中不要自己try-catch捕获异常,否则spring无法自动回滚事务 @Transactional(readOnly = true|false) :true表示只读(只有查询) false(会有增删改)
设置为true,性能会有所提升,但是底层数据库驱动支持(对mysql支持)
事务超时设置:
@Transactional(timeout=30) //默认是30秒
事务的传播行为:
@Transactional(propagation=Propagation.REQUIRED) 如果有事务, 那么加入事务, 没有的话新建一个(默认情况)

事务隔离级别
@Transactional(isolation = Isolation.READ_COMMITTED)(SQLSERVER默认)

读取已提交数据(会出现不可重复读和幻读)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值