事务管理对于企业应用来讲是相当重要的,即便出现异常状况,它也能够保证数据的一致性。
Spring Framework对事务管理提供了一致的抽象,其特色以下:html
为不一样的事务API提供一致的编程模型,好比JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)java
支持声明式事务管理,特别是基于注解的声明式事务管理,简单易用spring
提供比其余事务API如JTA更简单的编程式事务管理API数据库
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是没法提供事务功能的。对于纯JDBC操做数据库,想要用到事务,能够按照如下步骤进行:编程
使用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); }
Spring支持编程式事务管理和声明式事务管理两种方式。
编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,Spring推荐使用TransactionTemplate。
声明式事务管理创建在AOP之上的。其本质是对方法先后进行拦截,而后在目标方法开始以前建立或者加入一个事务,在执行完目标方法以后根据执行状况提交或者回滚事务。声明式事务最大的优势就是不须要经过编程的方式管理事务,这样就不须要在业务逻辑代码中掺琐事务管理的代码,只需在配置文件中作相关的事务规则声明(或经过基于@Transactional注解的方式),即可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就能够得到彻底的事务支持。和编程式事务相比,声明式事务惟一不足地方是,后者的最细粒度只能做用到方法级别,没法作到像编程式事务那样能够做用到代码块级别。可是即使有这样的需求,也存在不少变通的方法,好比,能够将须要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种经常使用的方式,一种是基于tx和aop名字空间的xml配置文件,另外一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。
spring事务的传播属性:存在多个事务同时存在的时候,spring应该如何处理这些事务的行为。这些属性在TransactionDefinition中定义,具体常量的解释见下表:
常量名称 | 常量解释 |
PROPAGATION_REQUIRED | 支持当前事务,若是当前没有事务,就新建一个事务。这是最多见的选择,也是 Spring 默认的事务的传播。 |
PROPAGATION_REQUIRES_NEW | 新建事务,若是当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚以后,不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获,也能够不处理回滚操做 |
PROPAGATION_SUPPORTS | 支持当前事务,若是当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,若是当前没有事务,就抛出异常。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,若是当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 若是一个活动的事务存在,则运行在一个嵌套的事务中。若是没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个能够回滚的保存点。内部事务的回滚不会对外部事务形成影响。它只对DataSourceTransactionManager事务管理器起效。 |
隔离级别 | 隔离级别的值 | 致使的问题 |
Read-Uncommitted | 0 | 致使脏读 |
Read-Committed | 1 | 避免脏读,容许不可重复读和幻读 |
Repeatable-Read | 2 | 避免脏读,不可重复读,容许幻读 |
Serializable | 3 | 串行化读,事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重 |
脏读:一事务对数据进行了增删改,但未提交,另外一事务能够读取到未提交的数据。若是第一个事务这时候回滚了,那么第二个事务就读到了脏数据。
不可重复读:一个事务中发生了两次读操做,第一次读操做和第二次操做之间,另一个事务对数据进行了修改,这时候两次读取的数据是不一致的。
幻读:第一个事务对必定范围的数据进行批量修改,第二个事务在这个范围增长一条数据,这时候第一个事务就会丢失对新增数据的修改。
总结:
隔离级别越高,越能保证数据的完整性和一致性,可是对并发性能的影响也越大。
大多数的数据库默认隔离级别为 Read Commited,好比 SqlServer、Oracle
少数数据库默认隔离级别为:Repeatable Read 好比: MySQL InnoDB