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>