zoukankan      html  css  js  c++  java
  • Spring事务详解

    最近发现有很多小伙伴在问Spring事务相关的问题,在解答这些问题的时候,我发现其实很多初学者对于事务很难了解,或者说根本不知道怎么去学习、去理解这方面的知识。为了让广大初学者能够快速了解事务并掌握它,我将Spring事务用直白的语言和非常形象的例子来和大家解答。

    要了解事务,在我看来,其实只需要明白以下两点:

    1、为什么需要事务,它能为我们做什么?

    2、事务的特性、隔离级别、以及传播行为。

    首先,我们来看一个例子:

    去超市购物,应该是每个人都经历过的事情。那么,我们可以将从我们开始进入超市,挑选物品,到为我们购买的物品付款,这整个过程看做是一段业务流程。

    对于超市购物这整个流程,理想化的状态是,我们进入超市,将我们需要的物品一个一个放入到购物车,然后付款,结束我们的购买流程。但是,如果在将物品放入到购物车后,发现这个物品不是我们需要的,或者在付款的时候,发现我们的钱不够,那么怎么办?

    当然,你会说物品不是我们需要的就将不需要的物品拿出购物车,钱不够可以回家拿钱或者拿掉一些物品

    可是,程序知道这样做吗?答案是不知道。程序只知道将这个流程跑下去,如果出错了,就异常结束。这样就造成了在出错之前做的操作其实仍保存在数据库,而出错之后的操作却没有继续进行。这样的部分改变对我们的数据完整性来说,是致命的。

    为了防止上述的情况发生在我们的业务中,就引进了一个概念:事务。

    我们可以将超市购物看做是一个事务,在这个事务中发生的所有操作,都是事务本身在控制的。在我们为购买的物品付款之前,这个事务都在对在购物中发生的每个操作进行管理。如果在购物中途发生异常,那么事务会对整个流程进行回滚,将流程回滚至进入超市之前的状态,并抛出相关异常。这样,就保证了数据的完整性。

    事务所做的事情,决定了事务的特性是:

    A、原子性(Atomicity):事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做,要么全不做。

    B、一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。

    C、隔离性(Isolation):一个事务的执行不能被其他事务干扰。

    D、持续性/永久性(Durability):一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。

    根据上面超市购物的例子,很容易理解事务的四大特性:原子性是指对于超市购物,要么你付款完成,要么你重新回到进入超市之前的状态;一致性是指超市购物的结果是从进入超市之前的状态,变到付款完成的状态;隔离性是指大家都在超市购物,你购物和我购物之间是没有关系的。持续性是指一旦你付款完成,那么这个结果就不能再改变了(当然,你可以退款,那这就是再重新开启了另外的一个事务,和当前的超市购物事务无关了,因为超市购物这个事务在付款结束后就已经完成)。

    事务的隔离级别:

    数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题

      脏读 不可重复读 幻读
    Read uncommitted
    Read committed ×
    Repeatable read × ×
    Serializable × × ×

    说到隔离级别,必须要说明的是,事务能回滚的根本原因是在事务开始到结束事务之前,所有对数据库的改变其实都是保存在内存中未提交的;在事务结束之后,这些改变才会提交至数据库。因此,才会有事务的隔离级别这一概念。不同的隔离级别,用于不同的业务场合。我简单介绍下Read uncommitted,其他的各位可以去百度或者谷歌。对于Read uncommitted,从字面上可以理解,就是可以读取未提交的数据。比如,在事务A中,它操作表test_table的某个记录的字段test_field,test_field原来的值是1,改变后的值是2;但是,在事务A提交前,另外一个事务B刚好读取了表test_table的这条记录,此时,事务B读取的记录中test_field字段的值为2。在事务B读取数据完成后,事务B中由于某个原因发生回滚,回滚后对应记录的字段test_field值变为1,这样,事务B即造成了脏读。

    事务的传播行为:代码执行过程中,开启了一个新事务,这个新事务基于当前事务做出的应答:

    PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
    PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
    PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
    PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
    PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
    PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

    例子:

    在Spring中配置了事务:

        <!-- 事务 -->
        <bean id="txManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
        <tx:annotation-driven transaction-manager="txManager" />
        <!-- 配置事务的拦截方式 -->
        <tx:advice id="transAdvice" transaction-manager="txManager">
            <tx:attributes>
                <tx:method name="del*" propagation="REQUIRED" />
                <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception" timeout="120" />
            </tx:attributes>
        </tx:advice>
    
        <aop:config>
            <!--  事务配置扫描包-->
            <aop:advisor advice-ref="transAdvice" pointcut="execution(* cn.com.service.*.*(..))" />
        </aop:config>

    简单说明下上面的配置:事务配置扫描包是扫描包cn.com.service下的所有接口,配置事务的拦截方式是为接口中方法名以del或者update开头的所有方法开始事务,传播方式指定为REQUIRED,方法名以update开头的方法还指定了回滚异常为Exception,事务超时时间为120秒。

    当一个接口方法deleteTest调用了另外一个接口的方法updateTest,那么,接口updateTest就不会开启新事务,而是加入到deleteTest的事务中。

  • 相关阅读:
    Eval版的ASP木马原理解析
    cmd命令
    Eval版的ASP木马原理解析
    Vbs脚本实现radmin终极后门
    迅雷是如何识别并偷偷上传文件的?
    迅雷是如何识别并偷偷上传文件的?
    广外男生病毒代码剖析
    cmd命令
    Vbs脚本实现radmin终极后门
    广外男生病毒代码剖析
  • 原文地址:https://www.cnblogs.com/wuzhiyuan/p/6889897.html
Copyright © 2011-2022 走看看