細說spring事務配置屬性


一、spring事務配置

1、spring配置
在配置數據源的下方配置
<!-- 事務配置 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<!--使用注解事務管理器(配置主服務器)-->
<tx:annotation-driven transaction-manager="transactionManager"/>

2、增加@Transactional注解
在需要的service實現層方法上加入這個注解

加注解時需要注意的問題:

1、在需要事務管理的地方加@Transactional注解。
2、@Transactional注解只能應用到 public 可見度的方法上。
如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯,但是這個被注解的方法將不會展示已配置的事務設置。
3、@Transactional注解使用在service實現層的方法上,別的地方出現都不正確。
在接口上使用 @Transactional 注解,只能當你設置了基於接口的代理時它才生效。因為注解是 不能繼承 的,這就意味着如果正在使用基於類的代理時,那么事務的設置將不能被基於類的代理所識別,而且對象也將不會被事務代理所包裝。
4、@Transactional的事務開啟,是基於接口的或者是基於類的代理被創建。所以在同一個類中一個方法調用另一個方法是有事務的方法,事務是不會起作用的。
5、Spring使用聲明式事務處理,默認情況下,如果被注解的數據庫操作方法中發生了unchecked異常,所有的數據庫操作將rollback;如果發生的異常是checked異常,默認情況下數據庫操作還是會提交的。

 

二、spring事務屬性

我們先分類把表格列出,然后再詳細說明

屬性名 類型 說明
propagation 枚舉org.springframework.transaction.annotation.Propagation的值 事務傳播行為
isolation 枚舉org.springframework.transaction.annotation.Isolation的值 事務隔離級別
readOnly boolean 事務讀寫性

 

屬性名 類型 說明
noRollbackFor Class<? extends Throwable>[]

一組異常類,遇到時不回滾。默認為{}

noRollbackForClassName Stirng[]

一組異常類名,遇到時不回滾,默認為{}

 

屬性名 類型 說明
rollbackFor Class<? extends Throwable>[] 一組異常類,遇到時回滾
rollbackForClassName Stirng[] 一組異常類名,遇到時回滾

 

屬性名 類型 說明
value String 可選的限定描述符,指定使用的事務管理器
timeout int 超時時間,以秒為單位

 

 

三、數據庫事務的四大特性和出現的問題

⑴ 原子性(Atomicity)

原子性是指事務包含的所有操作要么全部成功,要么全部失敗回滾,這和前面兩篇博客介紹事務的功能是一樣的概念,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。

⑵ 一致性(Consistency)

一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之后都必須處於一致性狀態。

拿轉賬來說,假設用戶A和用戶B兩者的錢加起來一共是5000,那么不管A和B之間如何轉賬,轉幾次賬,事務結束后兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。

⑶ 隔離性(Isolation)

隔離性是當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所干擾,多個並發事務之間要相互隔離。

即要達到這么一種效果:對於任意兩個並發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,這樣每個事務都感覺不到有其他事務在並發地執行。

關於事務的隔離性數據庫提供了多種隔離級別,稍后會介紹到。

⑷ 持久性(Durability)

持久性是指一個事務一旦被提交了,那么對數據庫中的數據的改變就是永久性的,即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。

例如我們在使用JDBC操作數據庫時,在提交事務方法后,提示用戶事務操作完成,當我們程序執行完成直到看到提示后,就可以認定事務以及正確提交,即使這時候數據庫出現了問題,也必須要將我們的事務完全執行完成,否則就會造成我們看到提示事務處理完畢,但是數據庫因為故障而沒有執行事務的重大錯誤。

以上介紹完事務的四大特性(簡稱ACID),現在重點來說明下事務的隔離性,當多個線程都開啟事務操作數據庫中的數據時,數據庫系統要能進行隔離操作,以保證各個線程獲取數據的准確性,在介紹數據庫提供的各種隔離級別之前,我們先看看如果不考慮事務的隔離性,會發生的幾種問題:

1,臟讀

臟讀是指在一個事務處理過程里讀取了另一個未提交的事務中的數據。

當一個事務正在多次修改某個數據,而在這個事務中這多次的修改都還未提交,這時一個並發的事務來訪問該數據,就會造成兩個事務得到的數據不一致。例如:用戶A向用戶B轉賬100元,對應SQL命令如下

    update account set money=money+100 where name=’B’;  (此時A通知B)

    update account set money=money - 100 where name=’A’;

當只執行第一條SQL時,A通知B查看賬戶,B發現確實錢已到賬(此時即發生了臟讀),而之后無論第二條SQL是否執行,只要該事務不提交,則所有操作都將回滾,那么當B以后再次查看賬戶時就會發現錢其實並沒有轉。

2,不可重復讀

不可重復讀是指在對於數據庫中的某個數據,一個事務范圍內多次查詢卻返回了不同的數據值,這是由於在查詢間隔,被另一個事務修改並提交了。

例如事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,發送了不可重復讀。

不可重復讀和臟讀的區別是,臟讀是某一事務讀取了另一個事務未提交的臟數據,而不可重復讀則是讀取了前一事務提交的數據。

在某些情況下,不可重復讀並不是問題,比如我們多次查詢某個數據當然以最后查詢得到的結果為主。但在另一些情況下就有可能發生問題,例如對於同一個數據A和B依次查詢就可能不同,A和B就可能打起來了……

3,虛讀(幻讀)

幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個數據項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值還是為“1”並且提交給數據庫。而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。

幻讀和不可重復讀都是讀取了另一條已經提交的事務(這點就臟讀不同),所不同的是不可重復讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)。

 

四、具體spring屬性的設置

Isolation 屬性一共支持五種事務設置,具體介紹如下:

DEFAULT 使用數據庫設置的隔離級別 ( 默認 ) ,由 DBA 默認的設置來決定隔離級別 .

READ_UNCOMMITTED 會出現臟讀、不可重復讀、幻讀 ( 隔離級別最低,並發性能高 )

READ_COMMITTED  會出現不可重復讀、幻讀問題(鎖定正在讀取的行)

REPEATABLE_READ 會出幻讀(鎖定所讀取的所有行)

SERIALIZABLE 保證所有的情況不會發生(鎖表)

 

不可重復讀的重點是修改 :
同樣的條件 ,   你讀取過的數據 ,   再次讀取出來發現值不一樣了
幻讀的重點在於新增或者刪除
同樣的條件 ,   第 1 次和第 2 次讀出來的記錄數不一樣

Spring在TransactionDefinition接口中定義這些屬性

在TransactionDefinition接口中定義了五個不同的事務隔離級別

ISOLATION_DEFAULT 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.另外四個與JDBC的隔離級別相對應
ISOLATION_READ_UNCOMMITTED 這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的數據。這種隔離級別會產生臟讀,不可重復讀和幻像讀

ISOLATION_READ_COMMITTED 保證一個事務修改的數據提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。這種事務隔離級別可以避免臟讀出現,但是可能會出現不可重復讀和幻像讀。

ISOLATION_REPEATABLE_READ 這種事務隔離級別可以防止臟讀,不可重復讀。但是可能出現幻像讀。它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了避免下面的情況產生(不可重復讀)。

ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。除了防止臟讀,不可重復讀外,還避免了幻像讀。

在TransactionDefinition接口中定義了七個事務傳播行為

PROPAGATION_REQUIRED 如果存在一個事務,則支持當前事務。如果沒有事務則開啟一個新的事務。

PROPAGATION_SUPPORTS 如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行。但是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。

PROPAGATION_MANDATORY 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。

PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。

PROPAGATION_NOT_SUPPORTED 總是非事務地執行,並掛起任何存在的事務。

PROPAGATION_NEVER 總是非事務地執行,如果存在一個活動事務,則拋出異常

PROPAGATION_NESTED如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行

 

五、總結(不想看上面的直接看總結)

我就知道有人會直接看這個地方,因為現在的年輕人太浮躁了。

好吧,其實我也是這樣的人。下面才是重點。

 

在看這些配置信息之前,我都是直接寫的@Transactional然后就沒有然后了。

而且項目也算是中型的項目,也沒有出現極高的並發,所以沒有出現什么問題。

所以,如果項目比較小,而且並非並不高的情況下,如果要使用事務,就直接寫個注解,然后簡單的手動回滾或者異常回滾就可以了。

主要是考慮到懶癌和開發效率。

 

但是,但是,但是。在有高的並發的情況下,需要按照以下的步驟依次考慮。

第一步:這個服務需要做的操作有那些,你可以先不加入事務,然后把整個業務邏輯全部都寫出來,看看做了哪些操作。

第二步:排列操作的順序,這步很重要,很多人都會小看在服務層里面的操作順序這個步驟,先減少庫存還是先增加訂單,步驟上面的順序很容易導致最后出現數據的問題。這個也要根據具體的業務邏輯來判斷,我這里也不能給出確定的說法,需要你仔細斟酌。

第三步:列出所有涉及的表,找到所有涉及的表之后,就看看這幾張表,除了這個事務之外還是否有別的服務會對表的數據進行修改,是否有多個同時修改的情況,是否有別的服務正在讀取你修改或者修改到一半的數據,總之要找到對於這張表會同時有可能產生的所有操作進行分析。

第四步:確定當前是否會出現臟讀,不可重復讀和幻讀這三種情況,這步如果你第一次做,分析起來會比較復雜,做多了會好一些,只能仔細考慮。

第五步:根據第四第五步所給出的結論正式確定上面事物的屬性,找上面的“具體spring屬性的設置”然后進行設置。

第六步:考慮時間,盡可能的讓事務的執行時間縮短,因為事務是非常消耗性能的,特別是SERIALIZABLE,最安全也就是最慢的。

第七步:測試,不測試就沒辦法確定任何事務的可行性。那么肯定有人會問,我怎么測試啊,並發這么高我怎么模擬啊。有幾個巧妙的方法,比如斷點,讓斷點一直卡住,不執行然后同時去修改數據庫以達到同時操作的目的;睡眠延時,導致事務執行時間變長讓其容易導致並發;定時或者循環調用;測試接口的軟件模擬並發。

 

總之做完以上的步驟,對於事務的考慮你肯定是已經明確了,如果再出現問題你也能解決了。

最后還有一句,事務確實是好用,但是並發的解決並不都只是通過事務來解決的,還有很多的中間件或者nosql來解決相應的並發。

 

參考博客:

http://www.cnblogs.com/fjdingsd/p/5273008.html

http://blog.csdn.net/bejustice/article/details/48245741?ref=myread


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM