異常是程序運行過程當中出現的錯誤。本文拿實際項目中的應用來簡單說明一下事務處理中的異常機制。Java語言的異常處理框架,是Java語言健壯性的一個重要體現。java
開始以前,先給你們上一張java中異常架構圖:spring
說明以下:
編程
Thorwable
類全部異常和錯誤的超類,有兩個子類Error
和Exception
,分別表示錯誤和異常。
其中異常類Exception
又分爲運行時異常(RuntimeException)和非運行時異常,這兩種異常有很大的區別,也稱之爲不檢查異常(Unchecked Exception)
和檢查異常(Checked Exception)。架構
Error
是程序沒法處理的錯誤,好比OutOfMemoryError
、ThreadDeath
等。這些異常發生時,Java虛擬機(JVM)通常會選擇線程終止。框架
Exception
是程序自己能夠處理的異常,這種異常分兩大類,運行時異常和非運行時異常。程序中應當儘量去處理這些異常。性能
運行時異常都是RuntimeException類及其子類異常,如編碼
這些異常是不檢查異常(UncheckedException),程序中能夠選擇捕獲處理,也能夠不處理。這些異常通常是由程序邏輯錯誤引發的,程序應該從邏輯角度儘量避免這類異常的發生。編譯是能夠經過的。spa
非運行時異常是除RuntimeException之外的異常,類型上都屬於Exception類及其子類。從程序語法角度講是必須進行處理的異常,若是不處理,程序就不能編譯經過。如IOException、SQLException等以及用戶自定義的Exception異常,通常狀況下不自定義檢查異常。線程
Java異常的捕獲和處理是一個不容易把握的事情,若是處理不當,不但會讓程序代碼的可讀性大大下降,並且致使系統性能低下,甚至引起一些難以發現的錯誤。設計
Java異常處理涉及到五個關鍵字,分別是:try
、catch
、finally
、throw
、throws
。
try
、catch
、finally
三個語句塊均不能單獨使用,三者能夠組成 try...catch...finally
、try...catch
、try...finally
三種結構,catch語句能夠有一個或多個,finally語句最多一個。
try、catch、finally三個代碼塊中變量的做用域爲代碼塊內部,分別獨立而不能相互訪問。若是要在三個塊中均可以訪問,則須要將變量定義到這些塊的外面。
多個catch塊時候,只會匹配其中一個異常類並執行catch塊代碼,而不會再執行別的catch塊,而且匹配catch語句的順序是由上到下。
throw
關鍵字是用於方法體內部,用來拋出一個Throwable
類型的異常。
若是拋出了檢查異常,則還應該在方法頭部聲明方法可能拋出的異常類型。該方法的調用者也必須檢查處理拋出的異常。若是全部方法都層層上拋獲取的異常,最終JVM會進行處理,處理也很簡單,就是打印異常消息和堆棧信息。
若是拋出的是Error或RuntimeException,則該方法的調用者可選擇處理該異常。有關異常的轉譯會在下面說明。
當拋出的是運行時異常,程序能夠選擇處理和不處理
public void insertCustomTable(List list,TransactionStatus transactionStatus ){ try { //FIELD_READ_RULE若是爲null,則設置默認值0; for(int i=0;i<list.size();i++){ Map custom=(Map)list.get(i); if(custom.get("FIELD_READ_RULE")==null || custom.get("FIELD_READ_RULE")==""){ custom.put("FIELD_READ_RULE", 0); } //是否主鍵 ,轉爲0:否,1:是 String isFieldKey = custom.get("IS_FIELD_KEY").toString(); custom.put("IS_FIELD_KEY", isFieldKey); } fillManagerDomain.insertCustom(list); } catch (Exception e) { log.info("任務建立時,插入表DSP_OFL_CUSTOMER失敗,insertCustomTable()!"+e); // transactionStatus.isRollbackOnly(); throw new RuntimeException(); } }
拋出的是非運行時異常時,程序必需要處理,能夠繼續往外拋,或者捕獲
public void insertCustomTable(List list,TransactionStatus transactionStatus ){ try { //FIELD_READ_RULE若是爲null,則設置默認值0; for(int i=0;i<list.size();i++){ Map custom=(Map)list.get(i); if(custom.get("FIELD_READ_RULE")==null || custom.get("FIELD_READ_RULE")==""){ custom.put("FIELD_READ_RULE", 0); } //是否主鍵 ,轉爲0:否,1:是 String isFieldKey = custom.get("IS_FIELD_KEY").toString(); custom.put("IS_FIELD_KEY", isFieldKey); } fillManagerDomain.insertCustom(list); } catch (SQLException e) { e.printStackTrace(); // transactionStatus.isRollbackOnly(); throw new SQLException(); } }
throws關鍵字用於方法體外部的方法聲明部分,用來聲明方法可能會拋出某些異常。僅當拋出了檢查異常,該方法的調用者才必須處理或者從新拋出該異常。當方法的調用者無力處理該異常的時候,應該繼續拋出,而不是囫圇吞棗通常在catch塊中打印一下堆棧信息作個勉強處理。下面給出一個簡單例子,
getCause()
:返回拋出異常的緣由。若是 cause 不存在或未知,則返回 null。getMessage()
:返回異常的消息信息。printStackTrace()
:對象的堆棧跟蹤輸出至錯誤輸出流,做爲字段 System.err 的值。能處理就早處理,拋出不去還不能處理的就想法消化掉或者轉換爲RuntimeException處理。由於對於一個應用系統來講,拋出大量異常是有問題的,應該從程序開發角度儘量的控制異常發生的可能。
對於檢查異常,若是不能行之有效的處理,還不如轉換爲RuntimeException拋出。這樣也讓上層的代碼有選擇的餘地――可處理也可不處理。
對於一個應用系統來講,應該有本身的一套異常處理框架,這樣當異常發生時,也能獲得統一的處理風格,將優雅的異常信息反饋給用戶。
Java將異常區分爲Error與Exception,Error是程序無力處理的錯誤,Exception是程序能夠處理的錯誤。異常處理是爲了程序的健壯性。
異常能處理就處理,不能處理就拋出,最終沒有處理的異常JVM會進行處理。
異常能夠傳播,也能夠相互轉譯,但應該根據須要選擇合理的異常轉譯的方向。
對於一個應用系統,設計一套良好的異常處理體系很重要。這一點在系統設計的時候就應該考慮到。
Exception
通常分爲Checked異常
和Runtime異常
,全部RuntimeException
類及其子類的實例被稱爲Runtime異常
,不屬於該範疇的異常則被稱爲CheckedException
。
開始以前,先給你們兩個概念
編程式事務:所謂編程式事務指的是經過編碼方式實現事務,即相似於JDBC編程實現事務管理。管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,spring推薦使用TransactionTemplate。
聲明式事務:管理創建在AOP之上的。其本質是對方法先後進行攔截,而後在目標方法開始以前建立或者加入一個事務,在執行完目標方法以後根據執行狀況提交或者回滾事務。聲明式事務最大的優勢就是不須要經過編程的方式管理事務,這樣就不須要在業務邏輯代碼中摻瑣事務管理的代碼,只需在配置文件中作相關的事務規則聲明(或經過基於@Transactional註解的方式),即可以將事務規則應用到業務邏輯中。
顯然聲明式事務管理要優於編程式事務管理,這正是spring倡導的非侵入式的開發方式。
聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上註解就能夠得到徹底的事務支持。和編程式事務相比,聲明式事務惟一不足地方是,後者的最細粒度只能做用到方法級別,沒法作到像編程式事務那樣能夠做用到代碼塊級別。可是即使有這樣的需求,也存在不少變通的方法,好比,能夠將須要進行事務管理的代碼塊獨立爲方法等等。
迴歸spring2.5版本,咱們採用配置方式來實現事務管理,其實這種方式是一種類編程式事務的管理:
爲了避嫌,已作模糊處理,以下圖所示:
事務模板jdbcTrasactionTemplate 必須進行聲明,見以下圖:
事務模板聲明以後,須要service層進行引用,方可開啓事務管理功能,見以下圖:
對非檢查型類異常能夠不用捕獲,而檢查型異常則必須用try語句塊進行處理或者把異常交給上級方法處理總之就是必須寫代碼處理它。因此必須在service捕獲異常,而後再次拋出,這樣事務方纔起效。
在spring的事務管理環境下,使用unckeckedException能夠極大地簡化異常的處理,只須要在事務層聲明可能拋出的異常(這裏的異常能夠是自定義的unckecked exception體系),在全部的中間層都只是須要簡單throws便可,不須要捕捉和處理,直接到最高層,好比UI層再進行異常的捕捉和處理
聲明這個service全部方法須要事務管理。每個業務方法開始時都會打開一個事務。
Spring默認狀況下會對運行期例外(RunTimeException)進行事務回滾。這個例外是unchecked若是遇到checked意外就不回滾。
如何改變默認規則:
1 讓checked例外也回滾:在整個方法前加上 @Transactional(rollbackFor=Exception.class)
2 讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)
3 不須要事務管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
(2) 若是不想拋出非檢查類型異常,即運行期異常,咱們能夠在catch中,直接顯示的回滾事務,例如:
public void insertCustomTable(List list,TransactionStatus transactionStatus ){ try { //FIELD_READ_RULE若是爲null,則設置默認值0; for(int i=0;i<list.size();i++){ Map custom=(Map)list.get(i); if(custom.get("FIELD_READ_RULE")==null || custom.get("FIELD_READ_RULE")==""){ custom.put("FIELD_READ_RULE", 0); } //是否主鍵 ,轉爲0:否,1:是 String isFieldKey = custom.get("IS_FIELD_KEY").toString(); custom.put("IS_FIELD_KEY", isFieldKey); } fillManagerDomain.insertCustom(list); } catch (Exception e) { log.info("任務建立時,插入表DSP_OFL_CUSTOMER失敗,insertCustomTable()!"+e); transactionStatus.isRollbackOnly(); // throw new RuntimeException(); } }
這種方式,也能夠回滾事務.
若是既沒有向上拋出異常,也沒有顯示回滾,那麼結果就一種,數據入庫,事務失效.
總結,
一個統一的異常層次結構對於提供服務抽象是必需的。 最重要的就是org.springframework.dao.DataAccessException以及其子類了。 須要強調的是Spring的異常機制重點在於應用編程模型。與SqlException和其餘數據存取API不一樣的是: Spring的異常機制是爲了讓開發者使用最少, 最清晰的代碼。DataAccessException和其餘底層異常都是非檢查性異常(unchecked exception)。 spring的原則之一就是基層異常就應該是非檢查性異常. 緣由以下:
很是感謝各位審閱本博文,文章說明有不合適的地方,還請各位能及時提出建議與意見!
完