Spring事務管理

  事務,在平常開發或者面試中都一定會涉及到。開發工做中,結合數據庫開發理解就是:一組dml要麼所有成功執行提交,要麼由於某一個操做異常,撤銷以前所作的成功的操做,總體執行失敗。再簡單點的一句話:生死與共。javascript

  由此,能夠看出,事務的必要性:在開發工做中,保證操做數據的安全性。事務的控制也就是保證數據的訪問安全性。java

1、事務的四大特性

  A:原子性(atomicity),對數據的修改,要麼所有成功執行,要麼所有不執行。mysql

  C:一致性(consistency),一旦事務完成,系統必須保證數據職員是知足業務狀態的一種一致狀態中。很難懂的解釋,跟原子性很像。一個事務在操做過程當中,數據可能會產生不少中間態,一致性保證中間態對其餘事務不可見,由於這些中間態,與事務的開始和結束的狀態是不一致的。也就是從一種正確的狀態到另外一種正確的狀態。web

   I:隔離性(isolation),事務之間的執行應不相互影響,也即事務執行的獨立。面試

  D:持久性(durability),事務一旦提交,則對數據庫的修改是永久性的。spring

2、事務的隔離級別

   併發環境下,事務可能會存在若干問題:髒讀、幻讀、不可重複讀、第一類更新丟失、第二類更新丟失。sql

類型   說明 舉例
髒讀 A事務讀取到了B事務未提交的數據  A開啓事務=>B開啓事務,讀取帳戶1000塊,取走100塊=>A讀取帳戶金額,讀取到900=>B回滾事務。此時A讀取的餘額數據是無效的
幻讀  一個事務裏面的操做發現了未被操做的數據

 A開啓事務,修改某些數據狀態=>B開啓事務,執行新增數據並提交=>A事務提交,會出現一條未被修改的數據。數據庫

幻讀發生的前提是併發事務中發生了新增或者刪除動做。express

不可重複讀  一個事務中,前後兩次讀取數據,讀到的結果不一致

 A開啓事務,讀取帳戶1000塊=>B開啓事務,讀取帳戶1000塊,取出100塊並提交事務=>A再讀取帳戶餘額,餘額900塊。編程

一個事務範圍內的兩次一樣的查詢,卻返回了兩次不一樣的數據,這就是不可重複讀

第一類更新丟失 A事務撤銷,把已經提交的B事務的更新的數據覆蓋 A開啓事務,讀取帳戶1000塊=>B開啓事務,讀取帳戶1000塊,而後增長100塊,提交事務,帳戶變爲1100=>A撤銷回滾事務,帳戶成1000塊
第二類更新丟失 A事務提交,把已經提交的B事務的更新的數據覆蓋 A開啓事務,讀取帳戶1000塊=>B開啓事務,讀取帳戶1000塊,而後增長100塊,提交事務,帳戶變爲1100=>A增長100塊,提交事務,帳戶變爲1100。

    針對併發環境下可能出現的事務問題,因而就出現了隔離級別的解決方案,由低到高依次是:讀未提交(Read uncommitted)、讀已提交(Read committed)、可重複讀(Repeatable read)、串行序列化(serializable)。下表展現出不一樣的隔離級別,對於髒讀、幻讀、不可重複讀是否會出現。

類型 髒讀 不可重複讀 幻讀 說明
Read uncommitted 會   
Read committed 不會  
Repeatable read 不會 不會 mysql的默認隔離級別
serializable 不會 不會 不會 最嚴格的隔離級別,將事務串行化執行,性能低。

  mysql中查詢當前隔離級別:select @@tx_isolation;

3、spring事務支持的隔離級別和傳播特性

  spring中定義了五種隔離界別和七種傳播行爲(能夠在org.springframework.transaction.TransactionDefinition類中看到詳細的解釋)

  一、spring支持的隔離級別

    ISOLATION_DEFAULT:默認級別。通常是使用的是數據庫自己的隔離級別(mysql - Repeatable read 、oracle - Read committed)

    餘下ISOLATION_READ_UNCOMMITTED、ISOLATION_READ_COMMITTED、ISOLATION_REPEATABLE_READ、ISOLATION_SERIALIZABLE分別對應上述數據庫隔離級別配置。

  二、傳播特性

   事務的傳播特性就是在多個事務方法互相調用的時候,事務該如何在方法之間使用傳播:

    PROPAGATION_REQUIRED:若是當前有事務,則加入該事務中。若是沒有,則新建事務。

    PROPAGATION_SUPPORTS:若是當前有事務,則加入該事務中。若是沒有,則以非事務狀態運行。

    PROPAGATION_MANDATORY:若是當前有事務,則加入該事務中。若是沒有,則拋出無事務異常

    PROPAGATION_REQUIRES_NEW:新建事務。若是當前存在事務,則掛起該事務。

    PROPAGATION_NOT_SUPPORTED:非事務狀態運行。若是當前存在事務,則掛起該事務。

    PROPAGATION_NEVER:非事務狀態運行。若是當前存在事務,則拋出異常。

    PROPAGATION_NESTED:若是當前有事務,則嵌套事務(父子事務)執行。若是沒有,相似PROPAGATION_REQUIRED處理。

  spring默認的傳播行爲是PROPAGATION_REQUIRED,通常適用於絕大多數的開發工做。

  三、事務的超時屬性

  事務在超過預約時間內還未完成操做,則自動回滾事務。TransactionDefinition 中以int值來表示超時時間,單位是秒,提供的默認值是TIMEOUT_DEFAULT = -1,即永不超時,一直等待操做完成

 

4、spring事務配置方法

   spring並不具體直接的管理事務,而是提供了一個接口org.springframework.transaction.PlatformTransactionManager,該接口中主要定義了三個方法:getTransaction(獲取事務)、commit(提交)和rollback(回滾)。

   根據不一樣的持久化策略,spring提供了不一樣的實現,好比jdbc - org.springframework.jdbc.datasource.DataSourceTransactionManager、hibernate - org.springframework.orm.hibernate5.HibernateTransactionManager等,在其餘的實現能夠經過源碼去查詢。

  spring事務使用,能夠分爲編程式事務和聲明式事務。編程式事務每次業務使用都得書寫獲取事務、設置事務隔離級別和傳播特性、提交或回滾事務,代碼的重複過高,費時費力,且若是代碼的功能性複雜時候,使用編程式變得更加痛苦。而聲明式的事務,屬於無侵入式的,不會影響主業務流程,且編寫上很是簡單。因此目前開發工做中,更多的是使用聲明式事務。

  一、編程式事務

    //to do。後續補充

  二、聲明式事務

    聲明式事務分爲兩種:基於aop的織入和@Transactional的註解。

    2.一、基於aop的事務織入

    

    

    以後在servcie中定義方法,最好的就是tx:method中定義的格式開頭,就會執行特定的事務。

    2.二、註解事務

    註解事務第一部分的數據源和事務管理器配置同上,配置文件中須要修改的是開啓註解配置:

    

    而後在service編程中,加註解@Transactional便可(建議只在service實現類中加),以下是配置樣例,其中的屬性能夠按需設置:

@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ,timeout = 100, readOnly = false, rollbackFor = {},noRollbackFor = {})

    注意點:

      事務的異常回滾只檢查RuntimeException的異常,checked exception(如ClassNotFoundException、FileNotFoundException等)不會滾,捕獲異常不拋出也不會回滾。

5、spring和springmvc父子容器

   如今開發工做中,通常大多數都使用的spring和springmvc構建,這裏,spring容器和springmvc容器就構成了父子容器的關係。父容器spring是發現不了子容器springmvc中的bean的,而子容器能夠發現父容器中註冊的bean。由此,實際開發工做中,不注意的話,每每會產生一些意想不到的問題。

  首先,一般咱們配置spring配置文件applicationContext.xml的時候,會配置以下的掃描:

<context:component-scan base-package="com.cfang" />

  這個配置會掃描指定包下面的全部@Component類型註解,包括@Controller,@Service,@Respository,並將掃描到的bean註冊到spring容器中。

  通常,spring配置文件中,還會出現下面的配置,做用是掃描@Required、@Autowired、 @PostConstruct、@PersistenceContext、@Resource、@PreDestroy等註解。理論上,此配置爲可選配置,由於上面的掃描配置會默認打開。

<context:annotation-config/>

    接下來配置springmvc的配置文件spring-mvc.xml,配置掃描註解@RequestMapping、@RequestBody、@ResponseBody等,同時,該配置默認加載不少參數綁定方法 。

<mvc:annotation-driven />

  上面一句話,至關於:

<!--配置註解控制器映射器,它是SpringMVC中用來將Request請求URL到映射到具體Controller-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--配置註解控制器映射器,它是SpringMVC中用來將具體請求映射到具體方法-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

  上面聊完基本配置,如下梳理下,來了解可能產生的容器衝突,對於事務管理的影響。

首先,有兩個基本的容器:spring和springmvc,配置文件分別爲applicationContext.xml和spring-mvc.xml
一、applicationContext.xml中配置<context:component-scan base-package="com.cfang" />,掃描指定包下的全部bean,並自動註冊到spring容器中
二、spring-mvc.xml配置<mvc:annotation-driven />,掃描相關的springmvc的註解
三、爲了保證springmvc的正常跳轉,一般咱們還得在spring-mvc.xml文件中配置包掃描<context:component-scan base-package="com.cfang" />。

按照以上配置信息,就會產生事務失效。緣由就在於:

Spring容器優先加載由ServletContextListener(對應applicationContext.xml)產生的父容器,
  而SpringMVC(對應spring-mvc.xml)產生的是子容器。子容器Controller進行掃描裝配時裝配的@Service註解的實例是沒有通過事務增強處理,即沒有事務處理能力的Service,
  而父容器進行初始化的Service是保證事務的加強處理能力的。若是不在子容器中將Service排除(exclude)掉,此時獲得的將是無事務處理能力的Service。

 

解決辦法按照官方建議的來配置,各自負責一部分加載:
  spring掃描:

<context:component-scan base-package="com.cfang.WeChat" use-default-filters="false">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
	</context:component-scan>

  springmvc掃描:

<context:component-scan base-package="com.cfang.WeChat" use-default-filters="false">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>

  

6、總結

相關文章
相關標籤/搜索