1.前瞻:spring
事务管理对于企业应用而言相当重要。它保证了用户的每一次操做都是可靠的,即使出现了异常的访问状况,也不至于破坏后台数据的完整性。就像银行的自助取款机,一般都能正常为客户服务,可是也不免遇到操做过程当中机器忽然出故障的状况,此时,事务就必须确保出故障前对帐户的操做不生效,就像用户刚才彻底没有使用过取款机同样,以保证用户和银行的利益都不受损失。
在 Spring 中,事务是经过 TransactionDefinition 接口来定义的。该接口包含与事务属性有关的方法。数据库
2.事务隔离级别
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
* TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,一般这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。express
* TransactionDefinition.ISOLATION_READ_UNCOMMITTED:并发
该隔离级别表示一个事务能够读取另外一个事务修改但尚未提交的数据。该级别不能防止脏读和不可重复读,所以不多使用该隔离级别。
* TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另外一个事务已经提交的数据。该级别能够防止脏读,这也是大多数状况下的推荐值。app
* TransactionDefinition.ISOLATION_REPEATABLE_READ:ide
该隔离级别表示一个事务在整个过程当中能够屡次重复执行某个查询,而且每次返回的记录都相同。即便在屡次查询之间有新增的数据知足该查询,这些新增的记录也会被忽略。该级别能够防止脏读和不可重复读。性能
* TransactionDefinition.ISOLATION_SERIALIZABLE:优化
全部的事务依次逐个执行,这样事务之间就彻底不可能产生干扰,也就是说,该级别能够防止脏读、不可重复读以及幻读。ui
可是这将严重影响程序的性能。一般状况下也不会用到该级别。spa
事务传播行为
所谓事务的传播行为是指,若是在开始当前事务以前,一个事务上下文已经存在,此时有若干选项能够指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了以下几个表示传播行为的常量:
* TransactionDefinition.PROPAGATION_REQUIRED:若是当前存在事务,则加入该事务;若是当前没有事务,则建立一个新的事务。
* TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一个新的事务,若是当前存在事务,则把当前事务挂起。
* TransactionDefinition.PROPAGATION_SUPPORTS:若是当前存在事务,则加入该事务;若是当前没有事务,则以非事务的方式继续运行。
* TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,若是当前存在事务,则把当前事务挂起。
* TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,若是当前存在事务,则抛出异常。
* TransactionDefinition.PROPAGATION_MANDATORY:若是当前存在事务,则加入该事务;若是当前没有事务,则抛出异常。
* TransactionDefinition.PROPAGATION_NESTED:若是当前存在事务,则建立一个事务做为当前事务的嵌套事务来运行;若是当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
这里须要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。
而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(若是存在外部事务的话)
,此时,内嵌事务并非一个独立的事务,它依赖于外部事务的存在,只有经过外部的事务提交,才能引发内部事务的提交,嵌套的子事务不能单独提交。
若是熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中能够包括多个保存点,
每个嵌套子事务。另外,外部事务的回滚也会致使嵌套子事务的回滚。
事务的只读属性
事务的只读属性是指,对事务性资源进行只读操做或者是读写操做。所谓事务性资源就是指那些被事务管理的资源,好比数据源、 JMS 资源,以及自定义的事务性资源等等。若是肯定只对事务性资源进行只读操做,那么咱们能够将事务标志为只读的,以提升事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。
事务的回滚规则
一般状况下,若是在事务中抛出了未检查异常(继承自 RuntimeException 的异常),则默认将回滚事务。若是没有抛出任何异常,或者抛出了已检查异常,则仍然提交事务。这一般也是大多数开发者但愿的处理方式,也是 EJB 中的默认处理方式。可是,咱们能够根据须要人为控制事务在抛出某些未检查异常时任然提交事务,或者在抛出某些已检查异常时回滚事务。
copy这么多我也是醉了,讲那么的多理论还不如上代码直接明了,
上代码记录下:
PS:这里只上service层事物管理相关代码。
package com.atguigu.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; //添加事务注解 //1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另一个事务方法调用时 //如何使用事务, 默认取值为 REQUIRED, 即便用调用方法的事务 //REQUIRES_NEW: 事务本身的事务, 调用的事务方法的事务被挂起. //2.使用 isolation 指定事务的隔离级别, 最经常使用的取值为 READ_COMMITTED //3.默认状况下 Spring 的声明式事务对全部的运行时异常进行回滚. 也能够经过对应的 //属性进行设置. 一般状况下去默认值便可. //4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据, //这样能够帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true //5.使用 timeout 指定强制回滚以前事务能够占用的时间. // @Transactional(propagation=Propagation.REQUIRES_NEW, // isolation=Isolation.READ_COMMITTED, // noRollbackFor={UserAccountException.class}) @Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, readOnly=false, timeout=3) @Override public void purchase(String username, String isbn) { try { Thread.sleep(5000); } catch (InterruptedException e) {} //1. 获取书的单价 int price = bookShopDao.findBookPriceByIsbn(isbn); //2. 更新数的库存 bookShopDao.updateBookStock(isbn); //3. 更新用户余额 bookShopDao.updateUserAccount(username, price); } }
配置清单:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package="com.atguigu.spring"></context:component-scan> <!-- 导入资源文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置 C3P0 数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> </bean> <!-- 配置 Spirng 的 JdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置 NamedParameterJdbcTemplate, 该对象可使用具名参数, 其没有无参数的构造器, 因此必须为其构造器指定参数 --> <bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg ref="dataSource"></constructor-arg> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 启用事务注解 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
如下是xml配置方式的清单:(xml只需在applicationContext.xml中作配置,代码不添加任何注解)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package="com.atguigu.spring"></context:component-scan> <!-- 导入资源文件 --> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置 C3P0 数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property> <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property> </bean> <!-- 配置 Spirng 的 JdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置 bean --> <bean id="bookShopDao" class="com.atguigu.spring.tx.xml.BookShopDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="bookShopService" class="com.atguigu.spring.tx.xml.service.impl.BookShopServiceImpl"> <property name="bookShopDao" ref="bookShopDao"></property> </bean> <bean id="cashier" class="com.atguigu.spring.tx.xml.service.impl.CashierImpl"> <property name="bookShopService" ref="bookShopService"></property> </bean> <!-- 1. 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 2. 配置事务属性 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 根据方法名指定事务的属性 --> <tx:method name="purchase" propagation="REQUIRES_NEW"/> <tx:method name="get*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <!-- 3. 配置事务切入点, 以及把事务切入点和事务属性关联起来 --> <aop:config> <aop:pointcut expression="execution(* com.atguigu.spring.tx.xml.service.*.*(..))" id="txPointCut"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config> </beans>