Spring之美-事务管理

1、概述

事务管理对于企业应用来讲是相当重要的,即便出现异常状况,它也能够保证数据的一致性。
Spring Framework对事务管理提供了一致的抽象,其特色以下:html

  • 为不一样的事务API提供一致的编程模型,好比JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)java

  • 支持声明式事务管理,特别是基于注解的声明式事务管理,简单易用spring

  • 提供比其余事务API如JTA更简单的编程式事务管理API数据库

  • 与spring数据访问抽象的完美集成

2、事务的基本原理

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是没法提供事务功能的。对于纯JDBC操做数据库,想要用到事务,能够按照如下步骤进行:编程

  1. 获取链接 Connection con = DriverManager.getConnection()
  2. 开启事务con.setAutoCommit(true/false);
  3. 执行CRUD
  4. 提交事务/回滚事务 con.commit() / con.rollback();
  5. 关闭链接 conn.close();

使用Spring的事务管理功能后,咱们能够再也不写步骤 2 和 4 的代码,而是由Spirng 自动完成。
那么Spring是如何在咱们书写的 CRUD 以前和以后开启事务和关闭事务的呢?解决这个问题,也就能够从总体上理解Spring的事务管理实现原理了。并发

注意: 性能

默认状况下,数据库处于自动提交模式(即con.setAutoCommit(true))。每一条语句处于一个单独的事务中,在这条语句执行完毕时,若是执行成功则隐式的提交事务,若是执行失败则隐式的回滚事务。this

对于正常的事务管理,是一组相关的操做处于一个事务之中,所以必须关闭数据库的自动提交模式。不过,这个咱们不用担忧,spring会将底层链接的自动提交特性设置为false。spa

下面是Spring底层实现该功能的代码:debug

// switch to manual commit if necessary. this is very expensive in some jdbc drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getautocommit()) {
    txobject.setmustrestoreautocommit(true);
    if (logger.isdebugenabled()) {
        logger.debug("switching jdbc connection [" + con + "] to manual commit");
    }
    con.setautocommit(false);
}

3、事务管理方式

Spring支持编程式事务管理声明式事务管理两种方式。

编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,Spring推荐使用TransactionTemplate

声明式事务管理创建在AOP之上的。其本质是对方法先后进行拦截,而后在目标方法开始以前建立或者加入一个事务,在执行完目标方法以后根据执行状况提交或者回滚事务。声明式事务最大的优势就是不须要经过编程的方式管理事务,这样就不须要在业务逻辑代码中掺琐事务管理的代码,只需在配置文件中作相关的事务规则声明(或经过基于@Transactional注解的方式),即可以将事务规则应用到业务逻辑中。

显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就能够得到彻底的事务支持。和编程式事务相比,声明式事务惟一不足地方是,后者的最细粒度只能做用到方法级别,没法作到像编程式事务那样能够做用到代码块级别。可是即使有这样的需求,也存在不少变通的方法,好比,能够将须要进行事务管理的代码块独立为方法等等。

声明式事务管理也有两种经常使用的方式,一种是基于tx和aop名字空间的xml配置文件,另外一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

 

4、Spring 事务的传播属性

spring事务的传播属性:存在多个事务同时存在的时候,spring应该如何处理这些事务的行为。这些属性在TransactionDefinition中定义,具体常量的解释见下表:

常量名称 常量解释
PROPAGATION_REQUIRED 支持当前事务,若是当前没有事务,就新建一个事务。这是最多见的选择,也是 Spring 默认的事务的传播。
PROPAGATION_REQUIRES_NEW 新建事务,若是当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚以后,不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获,也能够不处理回滚操做
PROPAGATION_SUPPORTS 支持当前事务,若是当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 支持当前事务,若是当前没有事务,就抛出异常。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,若是当前存在事务,则抛出异常。
PROPAGATION_NESTED

若是一个活动的事务存在,则运行在一个嵌套的事务中。若是没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个能够回滚的保存点。内部事务的回滚不会对外部事务形成影响。它只对DataSourceTransactionManager事务管理器起效。

5、数据库隔离级别

隔离级别 隔离级别的值 致使的问题
Read-Uncommitted 0 致使脏读
Read-Committed 1 避免脏读,容许不可重复读和幻读
Repeatable-Read 2 避免脏读,不可重复读,容许幻读
Serializable 3 串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重

脏读:一事务对数据进行了增删改,但未提交,另外一事务能够读取到未提交的数据。若是第一个事务这时候回滚了,那么第二个事务就读到了脏数据。

不可重复读:一个事务中发生了两次读操做,第一次读操做和第二次操做之间,另一个事务对数据进行了修改,这时候两次读取的数据是不一致的。

幻读:第一个事务对必定范围的数据进行批量修改,第二个事务在这个范围增长一条数据,这时候第一个事务就会丢失对新增数据的修改。

总结:

隔离级别越高,越能保证数据的完整性和一致性,可是对并发性能的影响也越大。

大多数的数据库默认隔离级别为 Read Commited,好比 SqlServer、Oracle

少数数据库默认隔离级别为:Repeatable Read 好比: MySQL InnoDB

相关文章
相关标签/搜索