Spring事务隔离级别,事务传播行为
作者:互联网
什么是事务:
事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败.
Spring事务的隔离性(5种)
在讲隔离界别前,我们先来讲讲下基础知识
事务的特性(ACID)
原子性 (atomicity):强调事务的不可分割.
一致性 (consistency):事务的执行的前后数据的完整性保持一致.
隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰
持久性(durability) :事务一旦结束,数据就持久到数据库
可能发生的数据安全问题
脏读:另一个事务还未提交,就已经读到数据未提交的数据。
不可重复读:在同一个事务中,读取一次数据,当另一个数据修改数据提交事务后,再次读取数据与上一次读取数据不同。
幻读:在同一个事务中,读取一次数据的数量。当另一个事务增加/删除一些数据时,数据数量与第一次不同。
MySQL默认隔离级别,可重复读
1.读未提交(最低事务隔离界别):可能发生问题 脏读,不可重复读,幻读
2.读已提交 解决问题:脏读 可能发生问题 不可重复读,幻读
3.可重复读(默认隔离级别) 解决问题:脏读 , 不可重复读 可能发生问题幻读
4.串行化 (最高事务隔离级别) 解决问题:脏读,不可重复读,幻读
不难想象隔离级别越高,发生的问题就越少。当然也就意味着同步数据消耗的资源更多。
相信MySQL能把可重复读指定为默认隔离级别,说明其性能已经非常棒了
Spring隔离级别
1.ISOLATION_DEFAULT 使用数据库默认隔离级别
2.ISOLATION_READ_UNCOMMITTED 读未提交
3.ISOLATION_READ_COMMITTED 读已提交
4.ISOLATION_REPEATABLE_READ 可重复读
5.ISOLATION_SERIALIZABLE 串行化
Spring事务的传播行为(7种)
事务传播:当一个方法中调用另外一个方法时,处理这两个方法间的事务行为(是基于Spring的AOP实现的)
举例:
1 @Transactional(isolation = Isolation.SERIALIZABLE) 2 public void method1() { 3 method2(); 4 } 5 6 @Transactional(isolation = Isolation.READ_COMMITTED) 7 public void method2() { 8 }
现在两个方法事务的隔离级别显然不同,那我们对于这种情况就要使用Spring事务传播行为来解决。
propagation(传播)
在当前事务中运行:
- Propagation.REQUIRED (默认级别)(method1事务存在,使用method1事务。method1事务不存在,新建事务)
-
PROPAGATION_SUPPORTS(method1事务存在,使用method1事务。method1事务不存在,以非事务方式执行)
- PROPAGATION_MANDATORY(强制性)(method1事务存在,使用method1事务。method1事务不存在,抛出异常)
不再当前事务中运行:
- PROPAGATION_REQUIRES_NEW(method1事务不存在,新建事务。method1事务存在,挂起当前事务,重建一个事务)
- PROPAGATION_NOT_SUPPORTED(method1事务不存在,新建事务。method1事务存在,挂起当前事务)
- PROPAGATION_NEVER(method1事务不存在,新建事务。method1事务存在,抛出异常)
- PROPAGATION_NESTED(嵌套的事务执行)
事务的实现
接下来我们看看都可以怎么实现Spring的事务
1.编程式事务管理
手动编写事务代码(很少使用)
2.声明式事务管理
基于TransactionProxyFactoryBean方式(很少使用)
基于Aspect的XML方式(常用)
基于注解方式(常用)
实验代码
dao层方法
1 @Repository("MyDao") 2 public interface MyDao { 3 int inCount(@Param("name") String name, @Param("count") double count);//增加钱 4 5 int outCount(@Param("name") String name, @Param("count") double count);//减少钱 6 7 double getCount(@Param("name") String name);//查询钱 8 }
查询余额
1 public interface GetCountService { 2 double getCount(String name); 3 }
1 //获取当前余额 2 @Service 3 public class GetCountServiceImpl implements GetCountService { 4 5 @Autowired 6 private MyDao myDao; 7 8 public double getCount(String name) { 9 return myDao.getCount(name); 10 } 11 }
转出钱
1 public interface OutService { 2 boolean out(String out, double count); 3 }
1 @Service 2 public class OutServiceImpl implements OutService { 3 @Autowired 4 private MyDao myDao; 5 6 public boolean out(String out, double count) { 7 return myDao.outCount(out,count)>0; 8 } 9 }
转入钱
1 public interface InService { 2 boolean in(String in, double count); 3 }
1 @Service 2 public class InServiceImpl implements InService { 3 @Autowired 4 private MyDao myDao; 5 6 public boolean in(String in, double count) { 7 return myDao.inCount(in, count) > 0; 8 } 9 }
转账
1 public interface MyService { 2 boolean transferCount(String out, String in, double count); 3 }
1 //实现转账 2 @Service 3 @Transactional 4 //(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT) 5 public class MyServiceImpl implements MyService { 6 7 @Resource 8 private GetCountService getCountService; 9 10 @Resource 11 private OutService outService; 12 13 @Resource 14 private InService inService; 15 16 17 // 解耦是为了更多的复用 18 // 对于代码模块的修改,不影响接口的使用 19 public boolean transferCount(String out, String in, double count) { 20 //判断传出金额与当前金额 21 if (getCountService.getCount(out) < count) return false; 22 //转出钱 23 if (!outService.out(out, count)) return false; 24 25 //制造异常 26 //int i = 10 / 0; 27 28 //转入钱 29 return inService.in(in, count); 30 } 31 }
当关上注释的时候,能实现正常的转账功能。
但是手动制造异常除数等于0后发现,传出人的钱少了,但是转入人的钱缺没有发生变化,没有保证数据的原子性。
基于Aspect的XML方式
1 <!-- 配置事务的通知(事务增强) --> 2 <tx:advice id="txAdvice" transaction-manager="transactionManager"> 3 <tx:attributes> 4 <!--以transfer开头的方法 --> 5 <tx:method name="transfer*" isolation="DEFAULT" propagation="REQUIRED"/> 6 </tx:attributes> 7 </tx:advice> 8 <!-- 配置切面 --> 9 <aop:config> 10 <!-- 配置切入点 --> 11 <!--*任意返回值 *任意方法 (..)任意参数 --> 12 <aop:pointcut id="pointcut1" expression="execution(* com.hg.service.*+.*(..))"/> 13 <!-- 配置切面 --> 14 <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> 15 </aop:config>
基于注解方式(常用)
首先在XML中配置事务管理器
1 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 2 <!--注入数据库连接池--> 3 <property name="dataSource" ref="dataSource"/> 4 </bean> 5 6 <!--开启注解--> 7 <tx:annotation-driven transaction-manager="transactionManager"/>
开启注解
@Transactional //这些都是spring事务的默认属性,timeout是直接指定的时间 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT,readOnly = false,timeout = 1000,rollbackFor = Exception.class)
//这两种实现效果相同
对比
第一种明显是一种AOP编程思想(面向切面)
当对所有的切面指定好规则后,所有的方法都不需要在进行配置。
优点:
代码量较少
通过制定规则(insert开头的方法,delete开头的方法,select开头的方法,update开头的方法),不易出错
第二种注解的方式
优点
较第一种方法更为灵活。但是在某些情况下冗余太多,导致修改维护的困难
结论,没有最好的技术,只有最符合业务的技术,小伙根据自己的情况选择合适的方式实现spring的事务吧。
标签:count,事务,隔离,method1,Spring,double,public,String 来源: https://www.cnblogs.com/Gang-Bryant/p/10887216.html