事務傳播機制/數據庫異常解析——2016-8-13分享總結

一. 事務的傳播機制/required 跟 required new 的使用與區別

基礎回顧

1.1 事務的隔離級別html

  ISOLATION_READ_UNCOMMITTED(讀未提交)java

  ISOLATION_READ_COMMITTED(讀已提交)mysql

  ISOLATION_REPEATABLE_READ(可重複讀)git

  ISOLATION_SERIALIZABLE(序列化)    github

  ISOLATION_DEFAULT(使用數據庫默認隔離級別)spring

1.2 髒讀,不可重複讀以及幻讀sql

  髒讀:一個事務讀取到另外一事務未提交的更新新據。數據庫

  不可重複讀:在同一事務中,屢次讀取同一數據返回的結果有所不一樣。換句話說就是,後續讀取能夠讀到另外一事務已提交的更新數據。學習

  幻讀:事務1正常查詢一次,以後事務2插入知足事務1查詢條件的新行,再次用事務1查詢,獲得多出來的數據。測試

1.3 事務的傳播屬性

  PROPAGATION_REQUIRED:  支持當前事務,沒有則新建

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

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

  PROPAGATION_MANDATORY:支持當前事務,若是當前沒有事務,就拋出異常

  PROPAGATION_NOT_SUPPORTED:以非事務方式執行,若是當前存在事務,就把當前事務掛起。也就是說業務方法不須要事務

  PROPAGATION_NEVER:以非事務方式執行,若是當前存在事務,則拋出異常。也就是說業務方法絕對不能在事務範圍內執行

  PROPAGATION_NESTED:若是一個活動的事務存在,則運行在一個嵌套的事務中。 若是沒有活動事務, 則按REQUIRED屬性執行  

二. 事務的回滾及傳播機制總結

  1. 首先要注意的是 spring 的默認回滾,roll-back = 「runtimeException」,即 spring 只回滾接收到的運行時異常,對於其餘異常則不回滾;

  源碼解析:

/**
     * Handle a throwable, completing the transaction.
     * We may commit or roll back, depending on the configuration.
     * @param txInfo information about the current transaction
     * @param ex throwable encountered
     */
    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (txInfo != null && txInfo.hasTransaction()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                        "] after exception: " + ex);
            }
            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
...

  注意上述有一段是 txInfo.transactionAttribute.rollbackOn(ex) 方法,跟進此方法:

/**
     * The default behavior is as with EJB: rollback on unchecked exception.
     * Additionally attempt to rollback on Error.
     * <p>This is consistent with TransactionTemplate's default behavior.
     */
    public boolean rollbackOn(Throwable ex) {
        return (ex instanceof RuntimeException || ex instanceof Error);
    }

  因此,默認不修改的話,只回滾 RuntimeException 或者 Error

  2. 對此咱們能夠針對性的添加須要回滾的策略,或者直接顯示設置 roll-back = 「Exception」,即回滾全部異常;

  3. 須要注意的是:普通狀況下,若是異常被 try-catch 處理了,併爲進一步拋出異常,那麼因爲 spring 並未接受到異常, 因此也不會回滾;

   本週討論補充知識:

  4. 異常分爲 應用層拋出 的異常,以及 數據庫 的異常,若是是前者,能夠正常應用 spring 的事務傳播規則,若是是後者,那麼一旦發生,數據庫端就已經放棄事務,也就沒有回滾一說();

  例: int 10/0 爲應用層的異常,能夠正常回滾; insert 提交一個已存在的 惟一字段,那麼數據庫會報錯,因爲事務自己也是由 spring 告之數據庫處理,這種狀況下,因爲 數據庫已經放棄事務,那麼 spring 層面也不能進行事務回滾;

  5. 正常狀況下不會出現一個接口的 bean 進行嵌套調用,若是有,該狀況下,因爲代理類再也不使用 spring 動態代理中的接口實現的方式,而是採用 cglib 的繼承實現方式,一樣也會致使傳播失效;

  6. 事務的傳播機制較爲經常使用的爲:required,required-new,support。討論結果爲 required-new 跟 required 相比,使用應較爲謹慎,緣由:同一事務粒度更細更可控,同時也能避免 嵌套事務 可能致使的複雜狀況,固然,具體狀況須要根據具體業務來定,好比本次討論中的項目,因爲每一次操做的對象都要及時被可能存在的另外一進程知悉,這種狀況下須要 required-new 來實時開啓事務告之其餘進程;

  查詢總結過程當中發現的其它知識點

  1. 注意的小點,MySQL 中 InnoDB 支持事務,MyIsam 並不支持事務;

  2. spring 已經提供了異常處理機制,其基類爲 DataAccessException ,它是 RuntimeExcption 的一個子類,據此能夠知道,全部從 數據庫返回的異常,都會被 spring 處理後 歸屬於 RuntimeException 中,其體系以下:

  

2. 在 org.springframework.jdbc.support包下有sql-error-codes.xml文件,裏面預約義了一些錯誤代碼和信息,其 bean 爲 HSQL,在 第8行,咱們能夠發現重複插入的 code 爲 -104;

  bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes"    
        property name="databaseProductName"     
            valueHSQL Database Engine/value     
        /property     
        property name="badSqlGrammarCodes"     
            value-22,-28/value     
        /property     
        property name="duplicateKeyCodes"     
            value-104/value     
       /property     
       property name="dataIntegrityViolationCodes"     
           value-9/value     
       /property     
       property name="dataAccessResourceFailureCodes"     
           value-80/value     
       /property     
  /bean     

  3. 此外,咱們能夠知道發散性的看,咱們徹底能夠自定義數據庫異常的信息,方法以下:

  3.1 從新新建一個sql-error-codes.xml代碼,並將它放到類路徑的根目錄下,這樣Spring會發現它並使用咱們自定義的文件。同時HSQL的bean的名稱不要改,並將useSqlStateForTranslation置爲false,就可使用咱們本身定義的異常類;

bean id="HSQL" class="org.springframework.jdbc.support.SQLErrorCodes"     
        property name="databaseProductName" value="HSQL Database Engine" /     
        property name="useSqlStateForTranslation" value="false" /     
        property name="customTranslations"     
             list     
                 ref local="vehicleDuplicateKeyTranslation" /     
             /list     
        /property     
     /bean     
bean id="vehicleDuplicateKeyTranslation"     
     class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation"     
     property name="errorCodes" value="-104" /     
     property name="exceptionClass" value="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyException" /     
     /bean 

  3.2 能夠看到結果,控制檯所打印的異常類型爲咱們本身定義的異常類型;

  3.3 除此以外,還能夠實現SQLExceptionTranslator接口,並在JDBC模板中注入其實例來實現異常控制

   1. package org.ourpioneer.vehicle.exception;      
   2. import java.sql.SQLException;      
   3. import org.springframework.dao.DataAccessException;      
   4. import org.springframework.jdbc.UncategorizedSQLException;      
   5. import org.springframework.jdbc.support.SQLExceptionTranslator;      
   6. public class VehicleDuplicateKeyTranslator implements SQLExceptionTranslator {      
   7.     public DataAccessException translate(String task, String sql,      
   8.             SQLException ex) {      
   9.         if (task == null) {      
  10.             task = "";      
  11.         }      
  12.         if (sql == null) {      
  13.         }      
  14.         if (ex.getErrorCode() == -104) {      
  15.             return new VehicleDuplicateKeyException(buildMessage(task, sql, ex));      
  16.         } else {      
  17.             return new UncategorizedSQLException(task, sql, ex);      
  18.         }      
  19.     }      
  20.     private String buildMessage(String task, String sql, SQLException ex) {      
  21.         return "數據庫操做異常:" + task + "; SQL [" + sql + "]; " + ex.getMessage();      
  22.     }      
  23. } 

  3.4 translate方法有三個參數,task表示當前操做要進行的任務是什麼,sql就是執行的sql語句,ex表示SQLException,咱們能夠從中獲取異常信息,其處理代碼僅僅捕捉了錯誤碼爲-104(HSQL數據庫)的錯誤,其他的配置信息能夠根據須要來自行添加。以後要在Spring中從新配置它們:

   1. bean id="vehicleDuplicateKeyTranslator"     
   2. class="org.ourpioneer.vehicle.exception.VehicleDuplicateKeyTranslator"/bean     
   3. bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"     
   4.     property name="exceptionTranslator" ref="vehicleDuplicateKeyTranslator" /     
   5.     property name="dataSource" ref="dataSource" /     
   6. /bean     
   7. bean id="vehicleDAO" class="org.ourpioneer.vehicle.dao.VehicleDAOImpl"     
   8.     property name="jdbcTemplate" ref="jdbcTemplate" /     
   9. /bean 

  3.5 測試結果

 

三. 總結

  其實以前對事務的理解是比較基礎的,通過週六的交流和學習,纔對事務有了略深一層的理解,但同時也愈發以爲事務的博大精深應遠不止於此,本文大部分都是結合各位🐂同事的理解和網上的一些資料整理的文章,因此本文做用更多的督促本身的學習進步,但願之後本身能不斷的吸取,整理和提煉知識😊。

 

參考文獻:

mysql隔離級別及事務傳播:https://gist.github.com/JagoWang/4555317

mysql事務管理及spring聲明式事務中主動異常拋出使數據庫回滾:http://www.cnblogs.com/wanglonghai/p/4866512.html

Spring訪問數據庫異常的處理方法:http://kb.cnblogs.com/page/89216/

相關文章
相關標籤/搜索