spring 事務 以及 數據庫事務

事務的認識

我們在實際業務場景中,經常會遇到數據頻繁修改讀取的問題。在同一時刻,不同的業務邏輯對同一個表數據進行修改,這種衝突很可能造成數據不可挽回的錯亂,所以我們需要用事務來對數據進行管理。

事務的概念

事務必須服從ACID原則。ACID指的是原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持久性(durability)。
通俗理解,事務其實就是一系列指令的集合。
  1. 原子性:操作這些指令時,要麼全部執行成功,要麼全部不執行。只要其中一個指令執行失敗,所有的指令都執行失敗,數據進行回滾,回到執行指令前的數據狀態。
  2. 原子性:操作這些指令時,要麼全部執行成功,要麼全部不執行。只要其中一個指令執行失敗,所有的指令都執行失敗,數據進行回滾,回到執行指令前的數據狀態。
  3. 隔離性:在該事務執行的過程中,無論發生的任何數據的改變都應該只存在於該事務之中,對外界不存在任何影響。只有在事務確定正確提交之後,纔會顯示該事務對數據的改變。其他事務才能獲取到這些改變後的數據。
  4. 持久性:當事務正確完成後,它對於數據的改變是永久性的。

事務的基本原理

Spring事務的本質其實就是數據庫對事務的支持使用JDBC的事務管理機制,就是利用java.sql.Connection對象完成對事務的提交,那在沒有Spring幫我們管理事務之前,我們要怎麼做。

Connection conn = DriverManager.getConnection();
try {  
    conn.setAutoCommit(false);  //將自動提交設置爲false                         
    //執行CRUD操作 
    conn.commit();      //當兩個操作成功後手動提交  
} catch (Exception e) {  
    conn.rollback();    //一旦其中一個操作出錯都將回滾,所有操作都不成功
    e.printStackTrace();  
} finally {
    conn.colse();
}

事務的併發

多用戶訪問數據庫是常見的場景,這就是所謂的事務的併發。事務併發所可能存在的問題:
1.髒讀:一個事務讀到另一個事務未提交的更新數據。
2.不可重複讀:一個事務兩次讀同一行數據,可是這兩次讀到的數據不一樣。
3.幻讀:一個事務執行兩次查詢,但第二次查詢比第一次查詢多出了一些數據行。
4.丟失更新:撤消一個事務時,把其它事務已提交的更新的數據覆蓋了。

可以在java.sql.Connection中看到JDBC定義了五種事務隔離級別來解決這些併發導致的問題:

/**
 * A constant indicating that transactions are not supported. 
 */
int TRANSACTION_NONE         = 0;  //JDBC   驅動不支持事務 
/**
 * A constant indicating that
 * dirty reads, non-repeatable reads and phantom reads can occur.
 * This level allows a row changed by one transaction to be read
 * by another transaction before any changes in that row have been
 * committed (a "dirty read").  If any of the changes are rolled back, 
 * the second transaction will have retrieved an invalid row.
 */
int TRANSACTION_READ_UNCOMMITTED = 1; //允許髒讀、不可重複讀和幻讀。
/**
 * A constant indicating that
 * dirty reads are prevented; non-repeatable reads and phantom
 * reads can occur.  This level only prohibits a transaction
 * from reading a row with uncommitted changes in it.
 */
int TRANSACTION_READ_COMMITTED   = 2; //禁止髒讀,但允許不可重複讀和幻讀。 
/**
 * A constant indicating that
 * dirty reads and non-repeatable reads are prevented; phantom
 * reads can occur.  This level prohibits a transaction from
 * reading a row with uncommitted changes in it, and it also
 * prohibits the situation where one transaction reads a row,
 * a second transaction alters the row, and the first transaction
 * rereads the row, getting different values the second time
 * (a "non-repeatable read").
 */
int TRANSACTION_REPEATABLE_READ  = 4; //禁止髒讀和不可重複讀,單運行幻讀。 
/**
 * A constant indicating that
 * dirty reads, non-repeatable reads and phantom reads are prevented.
 * This level includes the prohibitions in
 * <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the 
 * situation where one transaction reads all rows that satisfy
 * a <code>WHERE</code> condition, a second transaction inserts a row that
 * satisfies that <code>WHERE</code> condition, and the first transaction
 * rereads for the same condition, retrieving the additional
 * "phantom" row in the second read.
 */
int TRANSACTION_SERIALIZABLE     = 8; //禁止髒讀、不可重複讀和幻讀。

這幾個常量就是
TRANSACTION_NONE JDBC 驅動不支持事務
TRANSACTION_READ_UNCOMMITTED 允許髒讀、不可重複讀和幻讀。
TRANSACTION_READ_COMMITTED 禁止髒讀,但允許不可重複讀和幻讀。
TRANSACTION_REPEATABLE_READ 禁止髒讀和不可重複讀,單運行幻讀。
TRANSACTION_SERIALIZABLE 禁止髒讀、不可重複讀和幻讀。

隔離級別越高,意味着數據庫事務併發執行性能越差,能處理的操作就越少。
通過conn.setTransactionLevel去設置你需要的隔離級別。

JDBC規範雖然定義了事務的以上支持行爲,但是各個JDBC驅動,數據庫廠商對事務的支持程度可能各不相同。

出於性能的考慮我們一般設置TRANSACTION_READ_COMMITTED就差不多了,剩下的通過使用數據庫的鎖來幫我們處理別的。

事務大致瞭解之後 那麼spring事務又是怎麼處理的呢?

有了Spring,我們再也無需要去處理獲得連接、關閉連接、事務提交和回滾等這些操作,使得我們把更多的精力放在處理業務上。事實上Spring並不直接管理事務,而是提供了多種事務管理器。他們將事務管理的職責委託給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。

spring 事務

首先 spring支持 編程式事務管理 和 聲明式事務管理 兩種方式。

編程式事務管理

編程式事務管理編程式事務使用TransactionTemplate或者直接使用底層的
PlatformTransactionManager。對於編程式事務管理,
spring推薦使用TransactionTemplate。

聲明式事務管理

聲明式事務聲明式事務是建立在AOP之上的。其本質是對方法前後進行攔截,
然後在目標方法開始之前創建或者加入一個事務,
在執行完目標方法之後根據執行情況提交或者回滾事務。
聲明式事務最大的優點就是不需要通過編程的方式管理事務,
這樣就不需要在業務邏輯代碼中摻雜事務管理的代碼,
只需在配置文件中做相關的事務規則聲明(或通過基於@Transactional註解的方式),
便可以將事務規則應用到業務邏輯中。

顯然聲明式事務管理要優於編程式事務管理,這正是spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上註解就可以獲得完全的事務支持。和編程式事務相比,聲明式事務唯一不足地方是,它的最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的代碼塊獨立爲方法等等。聲明式事務管理也有兩種常用的方式,一種是基於tx和aop名字空間的xml配置文件,另一種就是基於@Transactional註解。顯然基於註解的方式更簡單易用,更清爽。

spring方式
開啓事務註解標記@Transactional

Spring在jdbc中提供了一個事務管理組件DataSourceTransactionManager

<!-- 配置事務管理組件 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource”> 
</bean>
<!-- 開啓事務註解標記@Transactional -->
<tx:annotation-driven transaction-manager=“txManager" />  

配置上面的信息後,Spring在初始化包含Transactional註解的類時,會自動生成這些類的代理,並放置再容器中,以便備用。

spring boot
spring boot中打開事務的幾種方式:

1、自動裝載
spring-boot-autoconfigure jar中 spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
@TransactionAutoConfiguration->EnableTransactionManagement
2、手功啓動事務管理 @EnableTransactionManagemen
@EnableTransactionManagemen ->TransactionManagementConfigurationSelector->ProxyTransactionManagementConfiguration->TransactionInterceptor

具體實現:
::Transactional 實現事務管理是通過TransactionInterceptor攔截器工作的。::
主要關注兩點:
::Spring 會調用TransactionInterceptor在目標方法執行前後進行攔截::
::獲取數據庫連接是通過當前線程獲取的,同一線程獲取的連接是同一個::

數據庫事務

數據庫事務的概念:
用一句話簡單的說明:數據庫事務(Database Transaction) ,是指作爲單個邏輯工作單元執行的一系列操作(對數據庫的相關增刪改查的操作),要麼完全地執行,要麼完全地不執行。

事務的隔離級別

隔離級別一共有四種:讀未提交、讀已提交、可重複讀、串行化。這四種隔離級別分別可以解決的不同的問題:

  1. 丟失修改
    A和B兩個事物同事修改同一個數據,A修改的提交在B提交之後,導致B好像沒有修改,丟失修改。

  2. 髒讀
    B事務修改了一個數據並未提交,A事物讀取了這個數據,然後B事務回滾了,最後A又讀取了一次,兩次讀取的數據不一致,稱爲髒讀。

  3. 不可重複讀
    A事務讀取了一個數據後,B事務修改了這個數據,A事務又讀取了這個數據,兩次讀取的數據也不一致,稱爲不可重複讀。

  4. 幻讀
    A事務更新了某個字段(範圍是整個數據表的)(以id=1爲條件的),B事務又插入了一條新的記錄,導致A事務認爲自己沒有完全更新過來,就像出現幻覺一樣。
    針對這幾種錯誤分別設置不同的隔離級別來解決:

    第一種丟失修改一般使用加鎖鎖來解決,因此串行化可以解決,並且串行化可以解決上面出現的所有問題。
    第二種問題髒讀是因爲讀取其他事物未提交的數據,因爲設置讀已提交隔離級別可以解決這個問題。但不可解決不可重複讀和幻讀的問題。
    第三種問題不可能重複讀,是因爲B事物的修改影響了A事務的讀取數據,設置可重複讀隔離級別,使得B事務修改數據和A事務讀取數據互不影響,隔離開來,從而解決這個問題,同時解決 髒讀問題
    第四種幻讀問題,是因爲A事務更新完數據後,B事務又插入了新的數據,設置串行化隔離級別可解決,並且這種隔離級別解決上面所有的問題。除了串行化,多版本併發控制(MVCC,Multiversion Concurrency Control)機制也可以解決該問題。

數據庫事務與spring事務的區別

數據庫事務和spring事務 本質上其實是同一個概念,spring的事務是對數據庫的事務的封裝,
最後本質的實現還是在數據庫,假如數據庫不支持事務的話,spring的事務是沒有作用的.
數據庫的事務說簡單就只有開啓,回滾和關閉,spring對數據庫事務的包裝,
原理就是拿一個數據連接,根據spring的事務配置,操作這個數據連接對數據庫進行事務開啓,
回滾或關閉操作.但是spring除了實現這些,還配合spring的傳播行爲對事務進行了更廣泛的管理.
其實這裏還有個重要的點,那就是事務中涉及的隔離級別,
以及spring如何對數據庫的隔離級別進行封裝.事務與隔離級別放在一起理解會更好些.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章