Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android SQLite 支持嵌套事務嗎?

Android SQLite 支持嵌套事務嗎?

編輯:關於Android編程

Android SQLite相關java源碼中多次提到支持 nested transaction。

而SQLite本身不支持嵌套事務,只能使用 savepoint 代替。 

嵌套事務即是類似於

 

BEGIN
     BEGIN
     ......

也許沒有太多人會直接這樣寫,更多情況是,一個程序執行事務中調用了其他程序,而其他程序有自己的事務。

 

通過查看Android源碼發現:

Android所謂的支持嵌套事務,只是屏蔽掉子事務的 begin end,只有最外層事務的begin end 有效。同時,子事務出錯會導致整個嵌套事務出錯,整個回滾。

當然,SQL SERVER好像也沒有支持真正的嵌套事務,了解不太詳細。

--------------------------------------------------------------------------------------

Android SQLite 關於嵌套事務的處理,均位於SqliteSession,多數數據庫相關操作的調用流程均為 SQLiteDataBase --> SqliteSeesion --> SqliteConnectionPool --> SqliteConnection。稍後詳細分析。只需知道,SQLiteDatabase.beginTransacion() 調用的是SQLiteSeesion.beginTransacion(),其他類似。如下

 

    public void beginTransaction() {
        beginTransaction(null, true);
    } 
    private void beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive) {
        acquireReference();
        try {
            getThreadSession().beginTransaction( //--- 獲取線程session,開始事務
                    exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :
                            SQLiteSession.TRANSACTION_MODE_IMMEDIATE,
                    transactionListener,
                    getThreadDefaultConnectionFlags(false /*readOnly*/), null);
        } finally {
            releaseReference();
        }
    }
--------------------------------------------------------------------

 

SQLiteSeesion中相關成員有以下需要注意:

 

    private Transaction mTransactionPool;   // 事務池
    private Transaction mTransactionStack;  // 事務棧

    private static final class Transaction {
        public Transaction mParent;
        public int mMode;
        public SQLiteTransactionListener mListener;
        public boolean mMarkedSuccessful;
        public boolean mChildFailed;
    }
可以猜想,事務棧、事務池其實是又 mParent聯系起來的兩條事務鏈。

 

先看 beginTransaction。

 

    public void beginTransaction( /*省略參數*/ ) {
        throwIfTransactionMarkedSuccessful();
        beginTransactionUnchecked( /*省略參數*/ );
    }

    private void beginTransactionUnchecked( /*省略參數*/ ) {
        //... 
        if (mTransactionStack == null) {  // 事務棧為空 獲取新連接
            acquireConnection(null, connectionFlags, cancellationSignal);
        }
        try {
            if (mTransactionStack == null) {  // 事務棧為空 執行“begin” 
                switch (transactionMode) {    // 事務棧不為空 直接略過 
                    case TRANSACTION_MODE_IMMEDIATE:
                        mConnection.execute(BEGIN IMMEDIATE;, null, cancellationSignal);
                        break;
                    case TRANSACTION_MODE_EXCLUSIVE:
                        mConnection.execute(BEGIN EXCLUSIVE;, null, cancellationSignal);
                        break;
                    default:
                        mConnection.execute(BEGIN;, null, cancellationSignal);
                        break;
                }
            }

            //... 省略監聽 transactionListener
                                                                                           
            Transaction transaction = obtainTransaction(transactionMode, transactionListener);	// 獲取新的事務
            transaction.mParent = mTransactionStack;   // 將事務加入事務棧中
            mTransactionStack = transaction;
        } finally {
            if (mTransactionStack == null) {
                releaseConnection(); // might throw
            }
        }
    }
獲取新事務obtainTransaction()如下

 

 

    private Transaction obtainTransaction(int mode, SQLiteTransactionListener listener) {
        Transaction transaction = mTransactionPool;  // 事務池中有事務,直接提取事務
        if (transaction != null) {
            mTransactionPool = transaction.mParent;                                            
            transaction.mParent = null;
            transaction.mMarkedSuccessful = false;
            transaction.mChildFailed = false;
        } else {
            transaction = new Transaction();        // 否則新建
        }
        transaction.mMode = mode;
        transaction.mListener = listener;
        return transaction;
    }

由上可以看出,事務棧開始為空, 不斷beginTransaction時會向事務棧中添加事務。那麼事務池中的事務怎麼來的呢?

 

在endTransaction中:

 

    public void endTransaction(CancellationSignal cancellationSignal) {
        throwIfNoTransaction();
        assert mConnection != null;

        endTransactionUnchecked(cancellationSignal, false);
    }

    private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
        final Transaction top = mTransactionStack;  // 獲取事務棧的top事務
        boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
                        	                                       
        //... 省略監聽
        mTransactionStack = top.mParent; // 將top從事務棧中剔除
        recycleTransaction(top);         // 將top放入事務池中

        if (mTransactionStack != null) { // 事務棧不為空
            if (!successful) {           // 如果未成功 將該事務的子事務置為失敗
                mTransactionStack.mChildFailed = true;
            }
        } else {
            try {                        // 事務棧為空
                if (successful) {        // 若成功 則提交 此時意味著所有事務都已經成功
                    mConnection.execute(COMMIT;, null, cancellationSignal); 
                } else {                 // 不成功 就回滾 此時一個子事務未成功 則整個事務體系未成功
                    mConnection.execute(ROLLBACK;, null, cancellationSignal);
                }
            } finally {
                releaseConnection(); 
            }
        }
    }
    private void recycleTransaction(Transaction transaction) { 
        transaction.mParent = mTransactionPool; // 其實就是將事務放入事務池
        transaction.mListener = null;
        mTransactionPool = transaction;
    }

 

可以看到,事務池中的事務由事務棧中使用完畢的事務組成。不用的放入事務池,使用時直接提取,並將狀態初始化。

也可以看出,事務池是為了避免Transaction多次new delete 的開銷。事務棧中保存了正在執行的嵌套事務。

事務棧、事務池如下

\

setTransactionSuccessful()就比較簡單了:

 

    public void setTransactionSuccessful() {        // 標記當前事務成功
        throwIfNoTransaction();
        throwIfTransactionMarkedSuccessful();

        mTransactionStack.mMarkedSuccessful = true; // mTransactionStack指向的就是最近的一個事務 如圖

    }

 

 

----------------------------------------------------------------------


執行beginTransaction():

當事務棧為空,說明沒有外層事務,獲取transaction,執行“BEGIN”,然後將transaction放入事務棧。

事務棧不為空,說明含有外層事務,獲取transaction,不執行“BEGIN”,將transaction放入事務棧。

執行endTransaction():

查看successful,當本事務成功且內層事務成功,才算成功;將該事務從事務棧移入事務池。

當事務棧不為空,說明仍有外層事務,如果successful為價,將該事物置為子事務失敗

當事務棧為空,說明已是最外層事務,successful則提交,否則回滾。

可以看到,嵌套事務中,無論哪一層事務出錯,都會自其開始直至最外層事務標記為false,回滾。只有每一層都標記成功,最後才成功。

需要注意的是,yield會對嵌套事務successful的標記產生影響,使得當前事務無論如何都標記為成功。android不推薦在嵌套事務中使用yield。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved