Android SQLite 支持嵌套事务吗?

from:http://blog.csdn.net/efeics/article/details/18739361


Android SQLite相关java源码中多次提到支持 nested transaction。

而SQLite本身不支持嵌套事务,只能使用 savepoint 代替。 https://www.sqlite.org/lang_savepoint.html

嵌套事务即是类似于

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 的开销。事务栈中保存了正在执行的嵌套事务。

事务栈、事务池如下
sqlite事物栈事务池

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值