一、事务的四个特性(ACID)

原子性(Atomicity):一个事务中所有对数据库的操作是一个不可分割的操作序列,要么全做,要么全部做。

一致性(Consistency): 数据不会因为事务的执行而遭到破坏。

隔离性(Isolation):一个事务的执行,不受其他事务(进程)的干扰。既并发执行的个事务之间互不干扰。

持久性(Durability):一个事务一旦提交,它对数据库的改变将是永久的。

二、事务的实现方式

实现方式共有两种:编码方式和声明式事务管理方式。

基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。

三、创建事务的时机

  是否需要创建事务,是由事务传播行为控制的。读数据不需要或只为其指定只读事务,而数据的插入、修改、删除就需要进行事务管理了,这是由事务的隔离级别控制的。

事务具有7个传播级别和4个隔离级别,传播级别定义的是事务创建的时机,隔离级别定义的是对并发事务数据读取的控制。

四、事务的传播级别

事务的传播级别一共有7种:

1.PROPAGATION_REQUIRED

默认的Spring事务传播级别,使用该级别的特点是:如果上下文中已经存在事务,那么就加入到事务中执行;如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。

2.PROPAGATION_SUPPORTS

从字面意思就知道,supports,支持,该传播级别的特点是:如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。

所以说,并非所有的包含在TransactionTemplate.execute方法中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。

3.PROPAGATION_MANDATORY

该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。

比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。

4.PROPAGATION_REQUIRES_NEW

从字面即可知道,new,每次都要一个新事务,该传播级别的特点是:每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。

这是一个很有用的传播级别,举一个应用场景:现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。

怎么处理整个业务需求呢?就是通过这个PROPAGATION_REQUIRES_NEW级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚。

5.PROPAGATION_NOT_SUPPORTED

这个也可以从字面得知,not supported,不支持,当前级别的特点是:若上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。

这个级别有什么好处?可以帮助你将事务尽可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况,所以事务的这个传播级别就派上用场了。用当前级别的事务模板包含起来就可以了。

6.PROPAGATION_NEVER

该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。

7.PROPAGATION_NESTED

从字面也可知道,nested,嵌套级别事务。该传播级别的特征是:如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

那么什么是嵌套事务呢?很多人都不理解,我看过一些博客,都是有些理解偏差。

嵌套是子事务嵌套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。重点就在于那个save point。看几个问题就明了了:

(1)   如果子事务回滚,会发生什么?

父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

(2)   如果父事务回滚,会发生什么?

父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。

(3)   事务的提交,是什么情况?

是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。

现在你再体会一下这个”嵌套“,是不是有那么点意思了?

以上是事务的7个传播级别,在日常应用中,通常可以满足各种业务需求,但是除了传播级别,在读取数据库的过程中,如果两个事务并发执行,那么彼此之间的数据是如何影响的呢?这就需要了解一下事务的另一个特性:数据隔离级别。

五、事务隔离级别

1.SERIALIZABLE(串行化)

最严格的级别,事务串行执行,资源消耗最大。

2.REPEATABLE_READ(可重复读)

保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。

3.READ_COMMITTED(提交读)

大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。

4.READ_UNCOMMITTED(未提交读)

保证了读取过程中不会读取到非法数据。

上面的解释其实每个定义都有一些拗口,其中涉及到几个术语:脏读、不可重复读、幻读。这里解释一下:

脏读(Dirty Reads)

所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。

不可重复读

不可重复读字面含义已经很明了了,比如事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。

幻读

小的时候数手指,第一次数十10个,第二次数是11个,怎么回事?产生幻觉了?

幻读也是这样子,事务A首先根据条件索引得到10条数据,然后事务B改变了数据库一条数据,导致也符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产生了幻读。

事务隔离级别对照关系表:

所以最安全的,是Serializable,但是伴随而来也是高昂的性能开销。

另外,事务常用的两个属性:① readonly,设置事务为只读以提升性能;② timeout,设置事务的超时时间,一般用于防止大事务的发生。

六、Spring管理声明式事务的配置

需要注意的是:

spring 的默认事务机制,当出现unchecked异常时候回滚,checked异常的时候不会回滚,也就是默认对RuntimeException()异常或是其子类进行事务回滚,

当有try catch后捕获了异常(不管是哪种异常),事务不会回滚,如果不得不在service层写try catch 需要catch后 throw new RuntimeException 让事务回滚。

1.基于XML的配置

applicationContext.xml配置如下

 <?xml version="1.0" encoding="gbk"?>

 <beans xmlns="http://www.springframework.org/schema/beans"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xmlns:aop="http://www.springframework.org/schema/aop"

     xmlns:tx="http://www.springframework.org/schema/tx"

     xsi:schemaLocation="http://www.springframework.org/schema/beans

     http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

     http://www.springframework.org/schema/tx

     http://www.springframework.org/schema/tx/spring-tx-2.0.xsd

     http://www.springframework.org/schema/aop

     http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

     <bean id="dataSource"

         class="org.apache.commons.dbcp.BasicDataSource">

         <property name="driverClassName"

             value="com.microsoft.sqlserver.jdbc.SQLServerDriver">

         </property>

         <property name="url"

             value="jdbc:sqlserver://localhost:1500;databaseName=ssh">

         </property>

         <property name="username" value="sa"></property>

         <property name="password" value="sa"></property>

     </bean>

     <bean id="sessionFactory"

         class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

         <property name="dataSource">

             <ref bean="dataSource" />

         </property>

         <property name="hibernateProperties">

             <props>

                 <prop key="hibernate.dialect">

                     org.hibernate.dialect.SQLServerDialect

                 </prop>

             </props>

         </property>

         <property name="mappingResources">

             <list>

                 <value>bank/entity/Account.hbm.xml</value>

             </list>

         </property>

     </bean>

     <bean id="AccountDAO" class="bank.dao.AccountDAO">

         <property name="sessionFactory">

             <ref bean="sessionFactory" />

         </property>

     </bean>

     <bean id="AccountManager" class="bank.biz.AccountManager">

         <property name="dao">

             <ref bean="AccountDAO" />

         </property>

     </bean>

     <bean name="/account" class="bank.action.AccountAction">

         <property name="accountManager">

             <ref bean="AccountManager" />

         </property>

     </bean>  

     <!--通用事务管理器-->

     <bean id="TransactionManager"

         class="org.springframework.orm.hibernate3.HibernateTransactionManager">

         <property name="sessionFactory" ref="sessionFactory" />

     </bean>

     <!--指定事务策略,声明一个通知,用以指出要管理哪些事务方法及如何管理-->

     <tx:advice id="txAdvice" transaction-manager="TransactionManager">

         <tx:attributes>

             <!-- 对get/load/search开头的方法要求只读事务 -->

             <tx:method name="find*" propagation="SUPPORTS"

                 read-only="true" />

             <!-- 对其它方法要求事务 -->

             <tx:method name="*" propagation="REQUIRED" />

         </tx:attributes>

     </tx:advice>

     <!--声明一个config,用以将事务策略和业务类关联起来-->

     <aop:config>

         <!-- 添加事务支持,因为前面配置的transactionManager是专对Hibernate的事务管理器-->

         <aop:pointcut id="bizMethods" expression="execution(* bank.biz..*.*(..))" />

         <!-- 织入 -->

         <aop:advisor advice-ref="txAdvice" pointcut-ref="bizMethods" />

     </aop:config>          

 </beans>

2.基于注解@Transactional的配置

一般来说我们在Service层的方法上打上@Transactional注解就可以启用事务了,但是有时候需要特别设置其中的一些属性。

 @Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional { @AliasFor("transactionManager")
String value() default ""; @AliasFor("value")
String transactionManager() default "";
//设置事务传播级别
Propagation propagation() default Propagation.REQUIRED;
//设置事务隔离级别
Isolation isolation() default Isolation.DEFAULT;
//设置事务超时时间
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
//是否为只读
boolean readOnly() default false;
//如果抛出指定类型的异常则回滚
Class<? extends Throwable>[] rollbackFor() default {};
//如果抛出数组里任意一个异常则回滚
String[] rollbackForClassName() default {};
//针对特定类型的异常不会滚
Class<? extends Throwable>[] noRollbackFor() default {};
//针对一组类型的异常不会滚
String[] noRollbackForClassName() default {}; }

如果配置了rollbackFor 和 noRollbackFor 且两个都是用同样的异常,那么遇到该异常,还是回滚。

参考地址:https://blog.csdn.net/yang1982_0907/article/details/44408809

https://blog.csdn.net/weixin_38379125/article/details/78727303

Spring的事务传播性与隔离级别以及实现事物回滚的更多相关文章

  1. spring的事务传播行为与隔离级别

    具体请参考blog:https://bbs.csdn.net/topics/391875990 要明白2个概念: 1.“spring的事务传播属性” 2.“spring的事务隔离级别” 例如正常的sp ...

  2. spring事务传播性与隔离级别

    事务的7种传播级别: 1)PROPAGATION_REQUIRED:支持当前事务,没有事务就新建一个. 2)PROPAGATION_SUPPORTS:支持当前事务,如果没有事务,以非事务方式处理 3) ...

  3. spring 中常用的两种事务配置方式以及事务的传播性、隔离级别

    一.注解式事务 1.注解式事务在平时的开发中使用的挺多,工作的两个公司中看到很多项目使用了这种方式,下面看看具体的配置demo. 2.事务配置实例 (1).spring+mybatis 事务配置 &l ...

  4. spring 事务配置方式以及事务的传播性、隔离级别

    在前面的文章中总结了spring事务的5中配置方式,但是很多方式都不用而且当时的配置使用的所有参数都是默认的参数,这篇文章就看常用的两种事务配置方式并信息配置事务的传播性.隔离级别.以及超时等问题,废 ...

  5. Spring事务传播机制与隔离级别(转)

    Spring事务传播机制与隔离级别 博客分类: Spring   转自:http://blog.csdn.net/edward0830ly/article/details/7569954 (写的不错) ...

  6. spring事务传播属性和隔离级别

    猫咪咪的Java世界 spring事务传播属性和隔离级别 博客分类: Spring java编程   1 事务的传播属性(Propagation) 1) REQUIRED ,这个是默认的属性 Supp ...

  7. 事务传播性、隔离性与MVCC

    一.事务传播性 1.1 什么是事务的传播性 事务的传播性一般在事务嵌套时候使用,比如在事务A里面调用了另外一个使用事务的方法,那么这俩个事务是各自作为独立的事务执行提交,还是内层的事务合并到外层的事务 ...

  8. 浅析Spring事务传播行为和隔离级别

    7个传播行为.4个隔离级别. Spring事务的传播行为和隔离级别[transaction behaviorand isolatedlevel] Spring中事务的定义: Propagation(k ...

  9. Spring支持的常用数据库事务传播属性和隔离级别

    事务的四大特征:原子性,隔离性,持久性,一致性 spring提供了7种事务传播属性: 一个事务与其他事务的隔离程度称为隔离级别.不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性 ...

随机推荐

  1. Mysql 数据库允许远程连接 服务器连接错误 Host 'XXX' is not allowed to connect to this MySQL server

    如果连接数据库的时候出现这个问题 Host 'XXX' is not allowed to connect to this MySQL server 说明 Mysql数据库 不允许远程连接, 需要修改 ...

  2. INFO org.apache.hadoop.ipc.RPC: Server at master/192.168.200.128:9000 not available yet, Zzzzz...

    hadoop 启动时namenode和datanode可以启动,使用jps命令也可以看到进程,但是在浏览器中输入master:50070却没有显示datanode 查看datanode的log日志: ...

  3. Windows下C/C++连接mysql数据库的方法

    步骤 安装MySQL数据库 项目属性页->C/C++->常规->附加包含目录:xxx\mysql Server 5.6\include 项目属性页->链接器->常规-&g ...

  4. Win8系统如何关闭用户账户控制UAC

    按WIN+S,屏幕右侧出现搜索框,在搜索框中输入UAC,然后单击"更改用户账户控制设置"   然后把弹出的窗口改成"从不通知"就可以了  

  5. Centos 6.4 搭建LANMP一键安装版

    lanmp一键安装包是wdlinux官网2010年开始推出的lamp,lnmp,lnamp(apache,nginx,php,mysql,zend,eAccelerator,pureftpd)应用环境 ...

  6. Linux——系统调用笔记1

    底层文件访问:    进程:运行中的程序,它有一些与值关联的文件描述符,有多少个文件描述符取决于系统配置情况.    当一个程序开始运行时,一般会打开三个文件描述符:        0:标准输入    ...

  7. POJ 题目1204 Word Puzzles(AC自己主动机,多个方向查询)

    Word Puzzles Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 10244   Accepted: 3864   S ...

  8. IOS报错:Unexpected ‘@’ in program

    IOS开发中出现此错误的原因: 1.宏定义重复. 我在OC与C++混编的时候,由于C++中使用到了interface,在工程中年将interface从定义为struct,当引用此接口时候出现Unexp ...

  9. 实践001:char 类型字段在表中的长度

    Rainy on 20170215 1.同事在 写RFC的时候遇到报错:"YTST_001" 必须为扁平结构.不能将内部表.字符# 原因是自建结构中字段定义为了string 类型. ...

  10. mongodb01--安装

    配置Mongo服务端 安装MongoDB 1.按照操作系统下载http://www.mongodb.org/downloads. 2.在D盘新建MongoDB文件夹(此文件夹为自定义的数据库安装目录D ...