Spring事務處理

事務(Transaction是併發控制的單位,是用戶定義的一個操做序列。這些操做要麼都作,要麼都不作,是一個不可分割的工做單位。html

數據庫向用戶提供保存當前程序狀態的方法,叫事務提交(commit;當事務執行過程當中,使數據庫忽略當前的狀態並回到前面保存的狀態的方法叫事務回滾(rollbackjava

 

事務特性(ACID)

原子性(atomicity):將事務中所作的操做捆綁成一個原子單元,即對於事務所進行的數據修改等操做,要麼所有執行,要麼所有不執行。spring

一致性(Consistency):事務在完成時,必須使全部的數據都保持一致狀態,並且在相關數據中,全部規則都必須應用於事務的修改,以保持全部數據的完整性。事務結束時,全部的內部數據結構都應該是正確的。數據庫

隔離性(Isolation):由併發事務所作的修改必須與任何其餘事務所作的修改相隔離。事務查看數據時數據所處的狀態,要麼是被另外一併發事務修改以前的狀態,要麼是被另外一併發事務修改以後的狀態,即事務不會查看由另外一個併發事務正在修改的數據。這種隔離方式也叫可串行性。express

持久性(Durability):事務完成以後,它對系統的影響是永久的,即便出現系統故障也是如此。segmentfault

 

事務隔離級別(Isolation Level)服務器

讀操做未提交(Read Uncommitted)讀取未提交的數據是容許的。說明一個事務在提交前,其變化對於其餘事務來講是可見的。這樣髒讀、不可重讀和幻讀都是容許的。當一個事務已經寫入一行數據但未提交,其餘事務都不能再寫入此行數據;可是,任何事務均可以讀任何數據。這個隔離級別使用排寫鎖實現。(RU模式下,一個事務能夠讀取到另外一個未提交(commit)的數據,致使了髒讀。若是B事務回滾了,就會形成數據的不一致。RU是事務隔離級別最低的。)session

讀操做已提交(Read Committed)讀取未提交的數據是不容許的,它使用臨時的共讀鎖和排寫鎖實現。這種隔離級別不容許髒讀,但不可重讀和幻讀是容許的。(RC模式下,若是數據被修改,可是沒有commit的時候,這種狀況下取到的是修改以前的記錄,這就避免了在RU模式中的髒讀,若是commit以前查詢了一次,在commit以後又查詢了一次,就會出現兩次select的數據不同,一個是更新前的數據,一個是更新後的數據,這就存在了不可重複讀的問題。RC事務隔離級別是Oracle數據庫的默認隔離級別.)數據結構

可重讀(Repeatable Read):說明事務保證可以再次讀取相同的數據而不會失敗。此隔離級別不容許髒讀和不可重讀,但幻讀會出現。(在這種隔離級別下,在一個事務中咱們可以保證可以獲取到同樣的數據(即便已經有其餘事務修改了咱們的數據)。可是沒法避免幻讀,幻讀簡單的解釋就是在數據有新增的時候,沒法保證兩次獲得的數據不一致,可是不一樣數據庫對不一樣的RR級別有不一樣的實現,有時候或加上間隙鎖來避免幻讀,這是在傳統的RR級別定義中會出現的。可是在innoDB中利用MVCC多版本併發控制解決了這個問題)併發

可串行化(Serializable):提供最嚴格的事務隔離。這個隔離級別不容許事務並行執行,只容許串行執行。這樣,髒讀、不可重讀或幻讀均可發生。

大部分狀況下,不多使用徹底隔離的事務。但不徹底隔離的事務會帶來以下一些問題。(全部事物串行,最高隔離級別,性能最差)

 

未徹底隔離事務可能帶來的問題:

事務隔離意味着對於某一個正在運行的事務來講,好像系統中只有這一個事務,其餘併發的事務都不存在同樣。

大部分狀況下,不多使用徹底隔離的事務。但不徹底隔離的事務會帶來以下一些問題。

更新丟失(Lost Update):兩個事務都企圖去更新一行數據,致使事務拋出異常退出,兩個事務的更新都白費了。

髒數據(Dirty Read):若是第二個應用程序使用了第一個應用程序修改過的數據,而這個數據處於未提交狀態,這時就會發生髒讀。第一個應用程序隨後可能會請求回滾被修改的數據,從而致使第二個事務使用的數據被損壞,即所謂的「變髒」。

不可重讀(Unrepeatable Read):一個事務兩次讀同一行數據,但是這兩次讀到的數據不同,就叫不可重讀。若是一個事務在提交數據以前,另外一個事務能夠修改和刪除這些數據,就會發生不可重讀。

幻讀(Phantom Read):一個事務執行了兩次查詢,發現第二次查詢結果比第一次查詢多出了一行,這多是由於另外一個事務在這兩次查詢之間插入了新行。針對由事務的不徹底隔離所引發的上述問題,提出了一些隔離級別,用來防範這些問題。

 

                                                              1. 1事務隔離與隔離級別的關係

隔離級別

髒讀(Dirty Read)

不可重讀(Unrepeatable read)

幻讀(Phantom Read)

讀操做未提交(Read Uncommitted)

可能

可能

可能

讀操做已提交(Read Committed)

不可能

可能

可能

可重讀(Repeatable Read)

不可能

不可能

可能

可串行化(Serializable)

不可能

不可能

不可能

 

事務的傳播行爲(Propagation)

1. 什麼是事務傳播行爲?

事務傳播行爲用來描述由某一個事務傳播行爲修飾的方法被嵌套進另外一個方法的時事務如何傳播。

用僞代碼說明:

public void methodA(){
    methodB();
    //doSomething
 }
 
 @Transaction(Propagation=XXX)
 public void methodB(){
    //doSomething
 }

 

 2.Spring中七種事務傳播行爲

事務傳播行爲類型

說明

PROPAGATION_REQUIRED

若是當前沒有事務,就新建一個事務,若是已經存在一個事務中,加入到這個事務中。這是 最多見的選擇。

PROPAGATION_SUPPORTS

支持當前事務,若是當前沒有事務,就以非事務方式執行。

PROPAGATION_MANDATORY

使用當前的事務,若是當前沒有事務,就拋出異常。

PROPAGATION_REQUIRES_NEW

新建事務,若是當前存在事務,把當前事務掛起。

PROPAGATION_NOT_SUPPORTED

以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。

PROPAGATION_NEVER

以非事務方式執行,若是當前存在事務,則拋出異常。

PROPAGATION_NESTED

若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則執行與 PROPAGATION_REQUIRED 相似的操做。

 當使用 PROPAGATION_NESTED 時, 底層的數據源必須基於 JDBC 3.0 ,而且實現者須要支持保存點事務機制。

 

事務屬性
spring在聲明事務時,用到了@Transactional(readOnly = false, propagation = Propagation.REQUIRED)。 
中間的參數readOnly,propagation咱們稱爲事務屬性。它就是對事務的基本配置。

事務屬性有五個方面:傳播行爲,隔離級別,事務超時時間,回滾規則,是否只讀。 


由屬性接口TransactionDefinition能夠看到,可返回四個基本事務屬性:

public interface TransactionDefinition {
    int getPropagationBehavior(); // 傳播行爲。
    int getIsolationLevel(); // 隔離級別。事務管理器根據它來控制另一個事務能夠看到本事務內的哪些數據。
    int getTimeout();  // 事務必須在多少秒內完成。
    boolean isReadOnly(); // 事務是否只讀。事務管理器可以根據這個返回值進行優化,確保事務是隻讀的
} 


readOnly 
    事務屬性中的readOnly標誌表示對應的事務應該被最優化爲只讀事務。這是一個最優化提示 。在一些狀況下,一些事務策略可以起到顯著的最優化效果,例如在使用Object/Relational映射工具 (如:Hibernate或TopLink)時避免dirty checking(試圖「刷新」)。

Timeout

     在事務屬性中還有定義「timeout」值的選項,指定事務超時爲幾秒。在JTA中,這將被簡單地傳遞到J2EE服務器的事務協調程序,並據此獲得相應的解釋。

例子:

ServiceA {

    void methodA() {
        try {
        //savepoint
        ServiceB.methodB();
        } 
        catch (SomeException) {
        // 執行其餘業務, 如 ServiceC.methodC();
        }
    }

}

1: PROPAGATION_REQUIRED

加入當前正要執行的事務不在另一個事務裏,那麼就起一個新的事務

例如

        ServiceB.methodB的事務級別定義爲PROPAGATION_REQUIRED

        ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB就加入ServiceA.methodA的事務內部,就再也不起新的事務。ServiceA.methodA沒有在事務中,這時調用ServiceB.methodB,

        ServiceB.methodB就會爲本身分配一個事務。

        在ServiceA.methodA或者在ServiceB.methodB內的任何地方出現異常,事務都會被回滾。即便ServiceB.methodB的事務已經被提交,可是ServiceA.methodA在接下來fail要回滾,ServiceB.methodB也要回滾

 

2: PROPAGATION_SUPPORTS

若是當前在事務中,即以事務的形式運行,若是當前再也不一個事務中,那麼就以非事務的形式運行

3: PROPAGATION_MANDATORY

必須在一個事務中運行。也就是說,他只能被一個父事務調用。不然,他就要拋出異常

4: PROPAGATION_REQUIRES_NEW

例如

        ServiceA.methodA的事務級別爲PROPAGATION_REQUIRED,ServiceB.methodB的事務級別爲PROPAGATION_REQUIRES_NEW,

        當調用ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成之後,他才繼續執行。

        PROPAGATION_REQUIRES_NEW與PROPAGATION_REQUIRED 的事務區別在於事務的回滾程度:

                由於ServiceB.methodB和ServiceA.methodA兩個不一樣的事務。若是ServiceB.methodB已經提交,那麼ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。若是ServiceB.methodB失敗回滾,

                若是他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務仍然可能提交。

 

5: PROPAGATION_NOT_SUPPORTED

當前不支持事務

例如:

  ServiceA.methodA的事務級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級別是PROPAGATION_NOT_SUPPORTED ,

  調用ServiceB.methodB時,ServiceA.methodA的事務掛起,而以非事務的狀態運行完,再繼續ServiceA.methodA的事務。

6: PROPAGATION_NEVER

不能在事務中運行

假設ServiceA.methodA的事務級別是PROPAGATION_REQUIRED,  而ServiceB.methodB的事務級別是PROPAGATION_NEVER ,

那麼ServiceB.methodB就要拋出異常了。

7: PROPAGATION_NESTED

理解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區別是,PROPAGATION_REQUIRES_NEW另起一個事務,將會與他的父事務相互獨立,

而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,若是父事務最後回滾,他也要回滾的。

而Nested事務的好處是他有一個savepoint。

Spring事務處理

  Spring配置文件中關於事務配置老是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,不管哪一種配置方式,通常變化的只是代理機制這部分。

    DataSource、TransactionManager這兩部分只是會根據數據訪問方式有所變化,好比使用Hibernate進行數據訪問 時,DataSource實際爲SessionFactory,TransactionManager的實現爲 HibernateTransactionManager。

具體以下圖:

     

 

第一種方式:每一個Bean都有一個代理

 

1.    <?xml version="1.0" encoding="UTF-8"?>  
2.    <beans xmlns="http://www.springframework.org/schema/beans"  
3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4.        xmlns:context="http://www.springframework.org/schema/context"  
5.        xmlns:aop="http://www.springframework.org/schema/aop"  
6.        xsi:schemaLocation="http://www.springframework.org/schema/beans   
7.               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
8.               http://www.springframework.org/schema/context  
9.               http://www.springframework.org/schema/context/spring-context-2.5.xsd  
10.               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  
11.      
12.        <bean id="sessionFactory"    
13.                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    
14.            <property name="configLocation" value="classpath:hibernate.cfg.xml" />    
15.            <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />  
16.        </bean>    
17.      
18.        <!-- 定義事務管理器(聲明式的事務) -->    
19.        <bean id="transactionManager"  
20.            class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
21.            <property name="sessionFactory" ref="sessionFactory" />  
22.        </bean>  
23.          
24.        <!-- 配置DAO -->  
25.        <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">  
26.            <property name="sessionFactory" ref="sessionFactory" />  
27.        </bean>  
28.          
29.        <bean id="userDao"    
30.            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">    
31.               <!-- 配置事務管理器 -->    
32.               <property name="transactionManager" ref="transactionManager" />       
33.            <property name="target" ref="userDaoTarget" />    
34.             <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />  
35.            <!-- 配置事務屬性 -->    
36.            <property name="transactionAttributes">    
37.                <props>    
38.                    <prop key="*">PROPAGATION_REQUIRED</prop>  
39.                </props>    
40.            </property>    
41.        </bean>    
42.    </beans>

 第二種方式:全部Bean共享一個代理基類

<?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <bean id="sessionFactory" 
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean> 

    <!-- 定義事務管理器(聲明式的事務) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
   
    <bean id="transactionBase" 
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" 
            lazy-init="true" abstract="true"> 
        <!-- 配置事務管理器 --> 
        <property name="transactionManager" ref="transactionManager" /> 
        <!-- 配置事務屬性 --> 
        <property name="transactionAttributes"> 
            <props> 
                <prop key="*">PROPAGATION_REQUIRED</prop> 
            </props> 
        </property> 
    </bean>   
  
    <!-- 配置DAO -->
    <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
   
    <bean id="userDao" parent="transactionBase" > 
        <property name="target" ref="userDaoTarget" />  
    </bean>
</beans>

第三種方式:使用攔截器

<?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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <bean id="sessionFactory" 
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean> 

    <!-- 定義事務管理器(聲明式的事務) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean> 
  
    <bean id="transactionInterceptor" 
        class="org.springframework.transaction.interceptor.TransactionInterceptor"> 
        <property name="transactionManager" ref="transactionManager" /> 
        <!-- 配置事務屬性 --> 
        <property name="transactionAttributes"> 
            <props> 
                <prop key="*">PROPAGATION_REQUIRED</prop> 
            </props> 
        </property> 
    </bean>
     
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
        <property name="beanNames"> 
            <list> 
                <value>*Dao</value>
            </list> 
        </property> 
        <property name="interceptorNames"> 
            <list> 
                <value>transactionInterceptor</value> 
            </list> 
        </property> 
    </bean> 
 
    <!-- 配置DAO -->
    <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
</beans>

第四種方式:使用tx標籤配置的攔截器

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <context:annotation-config />
    <context:component-scan base-package="com.bluesky" />

    <bean id="sessionFactory" 
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean> 

    <!-- 定義事務管理器(聲明式的事務) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>
   
    <aop:config>
        <aop:pointcut id="interceptorPointCuts"
            expression="execution(* com.bluesky.spring.dao.*.*(..))" />
        <aop:advisor advice-ref="txAdvice"
            pointcut-ref="interceptorPointCuts" />       
    </aop:config>     
</beans>

第五種方式:全註解

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <context:annotation-config />
    <context:component-scan base-package="com.bluesky" />

    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="sessionFactory" 
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
        <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
        <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    </bean> 

    <!-- 定義事務管理器(聲明式的事務) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
   
</beans>

 

此時在DAO上需加上@Transactional註解,以下:

package com.bluesky.spring.dao;

import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Component;

import com.bluesky.spring.domain.User;

@Transactional
@Component("userDao")
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

    public List<User> listUsers() {
        return this.getSession().createQuery("from User").list();
    }
    
    
}

Spring事務處理原理

問題

       一、當JPA框架對數據庫進行操做的時候,是從那裏獲取Connection?

       二、jdbc對事務的配置,好比事務的開啓,提交以及回滾是在哪裏設置的?

       三、Spring是經過aop攔截切面的全部須要進行事務管理的業務處理方法,那如何獲取業務處理方法裏面對數據庫操做的事務呢?

解答

      一、既然在JPA的框架裏面配置了datasource,那天然會從這個datasource裏面去得到鏈接。

      二、jdbc的事務配置是在Connection對消裏面有對應的方法,好比setAutoCommit,commit,rollback這些方法就是對事務的操做。

      三、Spring須要操做事務,那必需要對Connection來進行設置。Spring的AOP能夠攔截業務處理方法,而且也知道業務處理方法裏面的 DAO操做的JAP框架是從datasource裏面獲取Connection對象,那麼Spring須要對當前攔截的業務處理方法進行事務控制,那     必然 須要獲得他內部的Connection對象。總體的結構圖以下:

            

 

附上文章:

一、MySQL事務隔離級別和Spring事務關係介紹

https://blog.csdn.net/hellozhxy/article/details/81081187

二、Spring service本類中方法調用另外一個方法事務不生效問題

https://blog.csdn.net/qq_16675313/article/details/79955136

三、Spring爲內部方法新起一個事務,此處應有坑。

https://www.cnblogs.com/yougewe/p/7466677.html

4.Spring事務傳播行爲和具體示例詳解

https://segmentfault.com/a/1190000013341344

相關文章
相關標籤/搜索