在業務層進行回滾操作時如何避免回滾指令冗余


  眾所周知,數據庫有事務處理(Database Transaction),當一個事務中的操作沒有能全部進行時,之前的操作將回滾。

  如果操作都在同一個數據庫上,那可以直接使用數據庫事務進行處理,但是如果跨數據庫操作呢?可以使用JTA。來看看百度百科中JTA的解釋:“JTA,即Java Transaction API,譯為Java事務API。JTA允許應用程序執行分布式事務處理——在兩個或多個網絡計算機資源上訪問並且更新數據。”。有興趣的朋友可以搜一下JTA的用法。

  把回滾放在業務層有利有弊

  利在於可以不用增加DAO層的代碼,DAO層只單純扮演數據讀寫的角色,操作的粒度很細。細粒度意味着DAO層接口可以擁有更好的復用性。並且,如果不使用JTA,那么業務層中將不會混入與SQL相關的語句。所有與DB有關的部分都被封裝在DAO層不會泄露到上層。當DB被更換時,只需要更改DAO層的數據接口代碼,而不需要改動業務層代碼。

  弊在於在業務層用代碼實現回滾是一件復雜的事情,需要做一步一步的判斷,並且回滾指令是累加的。

  為什么說回滾指令是累加的呢?

  假設現在有4個操作(operation1~4),只有當這4個操作都順利進行時才接受,否則就回滾之前進行的操作。

  那么大致的邏輯就是(偽代碼,假設有四個替換操作,每個替換都成功時才算正確,否則將回滾之前的操作):

public boolean rollBackExample(String oldID, Object newOne){
        
        boolean res = true;
        Object oldOne = getObject(oldID);
        
        res = Replace1(oldID, newOne);
        if(res){
            //operation1 success
            //now doing operation 2
            res = Replace2(oldID, newOne);
            if(res){
                //operation2 success
                //now doing operation 3
                res = Replace3(oldID, newOne);
                if(res){
                    //operation3 success
                    //now doing operation 4
                    res = Replace4(oldID, newOne);
                    if(res){
                        return true;
                    }else{
                        //rollback Replace3 \Replace2 \ Replace1
                        Replace3(newOne.getID(), oldOne);
                        Replace2(newOne.getID(), oldOne);
                        Replace1(newOne.getID(), oldOne);
                        return false;
                    }
                }else{
                    //rollback Replace2 \ Replace1
                    Replace2(newOne.getID(), oldOne);
                    Replace1(newOne.getID(), oldOne);
                    return false;
                }
            }else{
                //rollback Replace1
                Replace1(newOne.getID(), oldOne);
                return false;
            }
        }else{
            return false;
        }
        
    }

  可以看到,代碼中進行逐級進行了判斷,並且依據操作進行程度的加深,回滾的列表逐漸增多。把回滾的操作單獨提出來可以看得更明顯些:

  當第二個操作出錯時,只需回滾

//rollback Replace1
 Replace1(newOne.getID(), oldOne);

  當第三個操作出錯時,需要回滾:

//rollback Replace2 \ Replace1
 Replace2(newOne.getID(), oldOne);
 Replace1(newOne.getID(), oldOne);

  當第四個操作出錯時,需要回滾:

//rollback Replace3 \Replace2 \ Replace1
 Replace3(newOne.getID(), oldOne);
 Replace2(newOne.getID(), oldOne);
 Replace1(newOne.getID(), oldOne);

  假設這個事務有N個操作組成,那么當進行到第N個操作時出錯,需要進行N-1項回滾。而累積的代碼為1 + 2 + …… + N - 1 = N(N-1)/2行代碼,直觀點看就是如果有10項操作,那么理論上將有9項可能的回滾操作,並且在函數中將累計出現45行用於回滾的代碼。用於描述回滾的代碼的平均重復出現次數達5次。非常拖沓。

  要如何解決這個代碼不優雅的問題呢?

  首先,判斷條件是不可少的,也就是if-else語句無法省略。因為operationj可能是在operationi(j later then i)的基礎上運行的,因此需要一步步判斷以避免出錯。

  其次,不管是哪一步出錯,它進行回滾的操作都是與自己所處的執行深度成正相關的。當第k步出錯時,k-1及之前的步驟就需要回滾,每一個操作都是如此。這個性質可以在沒有寫break的switch語句中找到影子。當case1執行后,會接着執行case2……以此類推。

  因此我們可以將需要進行的回滾操作設計到一個switch-case語句中,偽代碼如下:

public boolean rollBackExample2(String oldID, Object newOne) {

        boolean res = true;
        Object oldOne = getObject(oldID);
        int phase = 0;

        res = Replace1(oldID, newOne);
        if (res) {
            res = Replace2(oldID, newOne);
            if (res) {
                res = Replace3(oldID, newOne);
                if (res) {
                    res = Replace4(oldID, newOne);
                    if (res) {
                        phase = 4;
                    }
                } else {
                    phase = 3;
                }
            } else {
                phase = 2;
            }
        } else {
            phase = 1;
        }

        switch (phase) {
        case 4:
            return true;
        case 3:
            Replace3(newOne.getID(), oldOne);
        case 2:
            Replace2(newOne.getID(), oldOne);
        case 1:
            Replace1(newOne.getID(), oldOne);
        default:
            return false;
        }
    }

可以看到,當使用switch-case結構+phase階段判斷時,就不會出現回滾指令的代碼冗余了。 


免責聲明!

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



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