zoukankan      html  css  js  c++  java
  • 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(传播)

    在当前事务中运行:

    1. Propagation.REQUIRED (默认级别)method1事务存在,使用method1事务。method1事务不存在,新建事务)
    2. PROPAGATION_SUPPORTS(method1事务存在,使用method1事务。method1事务不存在,以非事务方式执行)

    3. PROPAGATION_MANDATORY(强制性)method1事务存在,使用method1事务。method1事务不存在,抛出异常)

    不再当前事务中运行:

    1. PROPAGATION_REQUIRES_NEW(method1事务不存在,新建事务。method1事务存在,挂起当前事务,重建一个事务
    2. PROPAGATION_NOT_SUPPORTED(method1事务不存在,新建事务。method1事务存在,挂起当前事务
    3. PROPAGATION_NEVER(method1事务不存在,新建事务。method1事务存在,抛出异常
    1. 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的事务吧。

  • 相关阅读:
    【HDOJ】4370 0 or 1
    【HDOJ】4122 Alice's mooncake shop
    【HDOJ】4393 Throw nails
    【HDOJ】2385 Stock
    WinCE 输入法编程
    WinCE 自由拼音输入法的测试
    SQL Server CE开发环境建立过程
    【SQL Server CE2.0】创建加密的数据库(源代码)
    【SQL Server CE2.0】打开加密的数据库(源代码)
    EVC在双核PC上调试速度慢的原因
  • 原文地址:https://www.cnblogs.com/Gang-Bryant/p/10887216.html
Copyright © 2011-2022 走看看