您好,登錄后才能下訂單哦!
這篇文章主要介紹“java事務的詳細講解”,在日常操作中,相信很多人在java事務的詳細講解問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”java事務的詳細講解”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
我們要理解下事務概念: 什么是事務呢?事務是并發控制的單位,是用戶定義的一個操作序列。有四個特性(ACID):
原子性(Atomicity): 事務是數據庫的邏輯工作單位,事務中包括的諸操作要么全做,要么全不做。
一致性(Consistency): 事務執行的結果必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(Isolation): 一個事務的執行不能被其他事務干擾。
持續性/永久性(Durability): 一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。
以上是書面解釋,簡單來說就是把你的操作統一化,要么所有操作都成功,要么就都不成功,如果執行中有某一項操作失敗,其之前所有的操作都回滾到未執行這一系列操作之前的狀態。
先理解這三種由于并發訪問導致的數據讀取問題,再理解事務隔離級別就簡單多了。
A事務讀取B事務尚未提交的數據,此時如果B事務發生錯誤并執行回滾操作,那么A事務讀取到的數據就是臟數據。就好像原本的數據比較干凈、純粹,此時由于B事務更改了它,這個數據變得不再純粹。這個時候A事務立即讀取了這個臟數據,但事務B良心發現,又用回滾把數據恢復成原來干凈、純粹的樣子,而事務A卻什么都不知道,最終結果就是事務A讀取了此次的臟數據,稱為臟讀。
這種情況常發生于轉賬與取款操作中
事務A在執行讀取操作,由整個事務A比較大,前后讀取同一條數據需要經歷很長的時間 。而在事務A第一次讀取數據,比如此時讀取了小明的年齡為20歲,事務B執行更改操作,將小明的年齡更改為30歲,此時事務A第二次讀取到小明的年齡時,發現其年齡是30歲,和之前的數據不一樣了,也就是數據不重復了,系統不可以讀取到重復的數據,成為不可重復讀。
事務A在執行讀取操作,需要兩次統計數據的總量,前一次查詢數據總量后,此時事務B執行了新增數據的操作并提交后,這個時候事務A讀取的數據總量和之前統計的不一樣,就像產生了幻覺一樣,平白無故的多了幾條數據,成為幻讀。
小總結:不可重復讀和幻讀到底有什么區別?
(1) 不可重復讀是讀取了其他事務更改的數據,針對update操作
解決:使用行級鎖,鎖定該行,事務A多次讀取操作完成后才釋放該鎖,這個時候才允許其他事務更改剛才的數據。
(2) 幻讀是讀取了其他事務新增的數據,針對insert和delete操作
解決:使用表級鎖,鎖定整張表,事務A多次讀取數據總量之后才釋放該鎖,這個時候才允許其他事務新增數據。
這時候再理解事務隔離級別就簡單多了呢。
SQL 標準定義的四種隔離級別被 ANSI(美國國家標準學會)和 ISO/IEC(國際標準)采用,每種級別對事務的處理能力會有不同程度的影響。事務是一系列的動作,它們綜合在一起才是一個完整的工作單元,這些動作必須全部完成,如果有一個失敗的話,那么事務就會回滾到最開始的狀態,仿佛什么都沒發生過一樣。
數據庫事務的隔離級別有4個,由低到高依次為Read uncommitted 、Read committed 、Repeatable read 、Serializable ,這四個級別可以逐個解決臟讀 、不可重復讀 、幻讀 這幾類問題。
默認值,表示使用底層數據庫的默認隔離級別。大部分數據庫為READ_COMMITTED(MySql默認REPEATABLE_READ)
該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀和不可重復讀,因此很少使用該隔離級別。
該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止臟讀,這也是大多數情況下的推薦值。
該隔離級別表示一個事務在整個過程中可以多次重復執行某個查詢,并且每次返回的記錄都相同。即使在多次查詢之間有新增的數據滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止臟讀和不可重復讀。
所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。 在該隔離級別下事務都是串行順序執行的,MySQL 數據庫的 InnoDB 引擎會給讀操作隱式加一把讀共享鎖,從而避免了臟讀、不可重讀復讀和幻讀問題。
mysql中,默認的事務隔離級別是可重復讀(repeatable-read),為了解決不可重復讀,innodb采用了MVCC(多版本并發控制)來解決這一問題。 MVCC是利用在每條數據后面加了隱藏的兩列(創建版本號和刪除版本號),每個事務在開始的時候都會有一個遞增的版本號,用來和查詢到的每行記錄的版本號進行比較。 MYSQL MVCC
先來介紹下Spring事務傳播行為的使用方法:
@Transactional(propagation=Propagation.REQUIRED) public void test() { //todo something }
注解@Transactional 通過使用 propagation 屬性設置,例如:@Transactional(propagation = Propagation.REQUIRED)
它的propagation屬性取值有以下幾種:
public enum Propagation { REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED), SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS), MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY), REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW), NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED), NEVER(TransactionDefinition.PROPAGATION_NEVER), NESTED(TransactionDefinition.PROPAGATION_NESTED); }
事務傳播行為:
REQUIRED
:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
SUPPORTS
:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
MANDATORY
:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
REQUIRES_NEW
:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
NOT_SUPPORTED
:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
NEVER
:以非事務方式運行,如果當前存在事務,則拋出異常。
NESTED
:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于 REQUIRED
Spring 支持“編程式事務
”管理和“聲明式事務
”管理兩種方式:
1編程式事務
: 編程式事務使用 TransactionTemplate 或者直接使用底層的 PlatformTransactionManager
實現事務。 對于編程式事務 Spring 比較推薦使用 TransactionTemplate 來對事務進行管理。
2聲明式事務
: 聲明式事務是建立在 AOP 之上的。其本質是對方法前后進行攔截,然后在目標方法開始之前創建或者加入一個事務,在執行完目標方法之后根據執行情況“提交”或者“回滾”事務。
編程式事務允許用戶在代碼中精確定義事務的邊界。
聲明式事務有助于用戶將操作與事務規則進行解耦,它是基于 AOP 交由 Spring 容器實現,是開發人員只重點關注業務邏輯實現。
編程式事務侵入到了業務代碼里面,但是提供了更加纖細的事務管理。而聲明式事務基于 AOP,所以既能起到事務作用,又可以不影響業務代碼的具體實現。一般而言比較推薦使用聲明式事務,尤其是使用 @Transactional 注解,它能很好地幫助開發者實現事務的同時,也減少代碼開發量,且使代碼看起來更加清爽整潔。
一般來說編程式事務有兩種方法可以實現: 模板事務的方式(TransactionTemplate)
和 平臺事務管理器方式(PlatformTransactionManager)
模板事務的方式(TransactionTemplate): 主要是使用 TransactionTemplate 類實現事務,這也是 Spring 官方比較推薦的一種編程式使用方式;
例:
① 獲取模板對象 TransactionTemplate;
② 選擇事務結果類型;
③ 業務數據操作處理;
④ 業務執行完成事務提交或者發生異常進行回滾;
其中 TransactionTemplate 的 execute 能接受兩種類型參數執行事務,分別為:
TransactionCallback<Object>(): 執行事務且可以返回一個值。 TransactionCallbackWithoutResult(): 執行事務沒有返回值。
下面是使用 TransactionTemplate 的實例:
@Service public class TransactionExample { /** 1、獲取 TransactionTemplate 對象 **/ @Autowired private TransactionTemplate transactionTemplate; public void addUser() { // 2、使用 TransactionCallback 或者 TransactionCallbackWithoutResult 執行事務 transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override public void doInTransactionWithoutResult(TransactionStatus transactionStatus) { try { // 3、執行業務代碼(這里進行模擬,執行多個數據庫操作方法) userMapper.delete(1); userMapper.delete(2); } catch (Exception e) { // 4、發生異常,進行回滾 transactionStatus.setRollbackOnly(); } } }); } }
平臺事務管理器方式(PlatformTransactionManager): 這里使用最基本的事務管理局對事務進行管理,借助 Spring 事務的 PlatformTransactionManager 及 TransactionDefinition 和 TransactionStatus 三個核心類對事務進行操作。
使用事務管理器方式實現事務步驟:
① 獲取事務管理器 PlatformTransactionManager;
② 獲取事務屬性定義對象 TransactionDefinition;
③ 獲取事務狀態對象 TransactionStatus;
④ 業務數據操作處理;
⑤ 進行事務提交 commit 操作或者發生異常進行事務回滾 rollback 操作;
@Service public class TransactionExample { /** 1、獲取 PlatformTransactionManager 對象 **/ @Autowired private PlatformTransactionManager platformTransactionManager; public void addUser() { // 2、獲取默認事務定義 DefaultTransactionDefinition def = new DefaultTransactionDefinition(); // 設置事務傳播行為 def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 3、根據事務定義對象設置的屬性,獲取事務狀態 TransactionStatus status = platformTransactionManager.getTransaction(def); try { // 4、執行業務代碼(這里進行模擬,執行多個數據庫操作方法) userMapper.delete(1); userMapper.delete(2); // 5、事務進行提交 platformTransactionManager.commit(status); } catch(Exception e){ // 5、事務進行回滾 platformTransactionManager.rollback(status); } } }
聲明式事務(declarative transaction management)顧名思義就是使用聲明的方式來處理事務。該方式是基于 Spring AOP 實現的,將具體業務邏輯和事務處理解耦,其本質是在執行方法前后進行攔截,在方法開始之前創建或者加入一個事務,在執行完目標方法之后根據執行情況提交或者回滾事務。
常用的聲明式事務使用方法有
1 XML
2 @Transactional 注解
兩種方法,由于近幾年 SpringBoot 的流行,提供很方便的自動化配置,致使 XML 方式已經逐漸淘汰,比較推薦使用注解的方式
注解 @Transactional 不僅僅可以添加在方法上面,還可以添加到類級別上,當注解放在類級別時,表示所有該類的公共方法都配置相同的事務屬性信息。如果類級別配置了 @transactional,方法級別也配置了 @transactional,應用程序會以方法級別的事務屬性信息來管理事務,換言之,方法級別的事務屬性信息會覆蓋類級別的相關配置。
value
: 事務管理器,此配置項是設置 Spring 容器中的 Bean 名稱,這個 Bean 需要實現接口 PlatformTransactionManager。
transactionManager
: 事務管理器,該參數和 value 配置保持一致,是同一個東西。
isolation
: 事務隔離級別,默認為 Isolation.DEFAULT 級別
propagation
: 事務傳播行為,默認為 Propagation.REQUIRED
timeout
: 事務超時時間,單位為秒,默認值為-1,當事務超時時會拋出異常,進行回滾操作。
readOnly
: 是否開啟只讀事務,是否開啟只讀事務,默認 false
rollbackForClassName
: 回滾事務的異常類名定義,同 rollbackFor,只是用類名定義。
noRollbackForClassName
: 指定發生哪些異常名不回滾事務,參數為類數組,同 noRollbackFor,只是使用類的名稱定義。
rollbackFor
: 回滾事務異常類定義,當方法中出異常,且異常類和該參數指定的類相同時,進行回滾操作,否則提交事務。
noRollbackFor
: 指定發生哪些異常不回滾事務,當方法中出異常,且異常類和該參數指定的類相同時,不回滾而是將繼續提交事務。
@Transactional(propagation=Propagation.REQUIRED) public void test() { //todo something }
注意: 一般而言,不推薦將 @Transaction 配置到類上,因為這樣很可能使后來的維護人員必須強制使用事務。
1、遇到異常檢測不回滾,原因:默認RuntimeException級別才回滾,如果是Eexception級別的異常需要手動添加
@Transactional(rollbackFor=Exception.class)
2、捕捉異常后事物不生效,原因:捕捉處理了異常導致框架無法感知異常,自然就無法回滾了。建議:若非實際業務要求,則在業務層統一拋出異常,然后在控制層統一處理
@Transactional(rollbackFor=Exception.class) public void test() { try { //業務代碼 } catch (Exception e) { // TODO: handle exception } //主動捕捉異常導致框架無法捕獲,從而導致事物失效 }
到此,關于“java事務的詳細講解”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。