美文网首页Android 源码分析
Sqlite 源码分析 -- SQLiteDatabase 使用

Sqlite 源码分析 -- SQLiteDatabase 使用

作者: _夜 | 来源:发表于2017-12-12 16:10 被阅读0次

    注意事项:

    1. 插入单条数据不需要开启事务;

    2. beginTransaction() 获取 主连接后不会释放,直到调用 endTransaction(). 所以在此期间如果有另一个线程进行 CRUD 操作,获取不到主连接,只能等待;

    3. 执行 sql 语句的正确方式:

    db.beginTransaction();
    try {
      ...
      // 注意该语句放在 try 语句块的最后,表明最终的操作成功
      db.setTransactionSuccessful();
    } finally {
      // 注意该语句放在 finally 语句块中,确定进行 roll back 或 commit
      db.endTransaction();
    }
    
    

    一、beginTransaction() 从连接池获取主连接,且不会释放

    1. 开启事务

    public void beginTransaction() {
        beginTransaction(null /* transactionStatusCallback */, true);
    }
    
    

    2. 获取当前线程的 SQLiteSession,调用其 beginTransaction()

    /**
     * 1. 采用 TRANSACTION_MODE_EXCLUSIVE,只允许当前 session 对数据库进行读写,别的 session 不允许访问数据库
     * 2. 获取当前线程的 SQLiteSession,如果不存在则创建
     * 3. 要求获取主连接
     * @param transactionListener
     * @param exclusive
     */
    private void beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive) {
        // 增加引用次数
        acquireReference();
        try {
            getThreadSession().beginTransaction(exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE : SQLiteSession.TRANSACTION_MODE_IMMEDIATE,
                    transactionListener,
                    getThreadDefaultConnectionFlags(false /*readOnly*/), null);
        } finally {
            // 减少引用次数
            releaseReference();
        }
    }
    
    

    3. 从连接池获取主连接,且持有连接不释放

    /**
     * 1. 从连接池获取主连接
     * 2. 获取主连接后不会释放, 在 endTransaction() 中释放主连接
     * @param transactionMode
     * @param transactionListener
     * @param connectionFlags
     * @param cancellationSignal
     */
    private void beginTransactionUnchecked(int transactionMode, SQLiteTransactionListener transactionListener, int connectionFlags, CancellationSignal cancellationSignal) {
        if (cancellationSignal != null) {
            // cancellationSignal 默认为 null
            cancellationSignal.throwIfCanceled();
        }
    
        if (mTransactionStack == null) {
            // 第一次调用 beginTransaction 的情况,不存在嵌套
            /**
             * 从连接池获取一条连接(此处要求获取主连接),赋值给 mConnection:
             * 1. 多个线程共用一个连接池,获取连接的过程是同步的
             * 2. 若主连接已被占用,则进入循环等待状态,等待主连接被释放
             */
            acquireConnection(null, connectionFlags, cancellationSignal); // might throw
        }
        try {
            // Set up the transaction such that we can back out safely
            // in case we fail part way.
            if (mTransactionStack == null) {
                // Execute SQL might throw a runtime exception.
                switch (transactionMode) {
                    case TRANSACTION_MODE_IMMEDIATE:
                        mConnection.execute("BEGIN IMMEDIATE;", null, cancellationSignal); // might throw
                        break;
                    case TRANSACTION_MODE_EXCLUSIVE:
                        // 通过主连接调用 native 方法执行 sql
                        mConnection.execute("BEGIN EXCLUSIVE;", null, cancellationSignal); // might throw
                        break;
                    default:
                        mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
                        break;
                }
            }
    
            // Listener might throw a runtime exception.
            if (transactionListener != null) {
                try {
                    transactionListener.onBegin(); // might throw
                } catch (RuntimeException ex) {
                    if (mTransactionStack == null) {
                        mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
                    }
                    throw ex;
                }
            }
    
            // Bookkeeping can't throw, except an OOM, which is just too bad...
            SQLiteSession.Transaction transaction = obtainTransaction(transactionMode, transactionListener);
            // mTransactionStack 若为 null,则 mParent 为 null
            // mTransactionStack 若不为 null,则设为新开的 transaction 的 parent
            transaction.mParent = mTransactionStack;
            // 替换 mTransactionStack 为新开的 transaction,意味着进入子 transaction 环境
            // 后续操作都是针对新的子 transaction
            mTransactionStack = transaction;
        } finally {
            if (mTransactionStack == null) {
                // 正常情况下 mTransactionStack 不会为 null
                // beginTransaction 获取主连接后不会释放
                releaseConnection(); // might throw
            }
        }
    }
    

    二、setTransactionSuccessful() 设置操作成功标识

    /**
     * setTransactionSuccessful() 与 endTransaction() 之间尽量不要做别的事情
     * Marks the current transaction as successful. Do not do any more database work between
     * calling this and calling endTransaction. Do as little non-database work as possible in that
     * situation too. If any errors are encountered between this and endTransaction the transaction
     * will still be committed.
     *
     * @throws IllegalStateException if the current thread is not in a transaction or the
     * transaction is already marked as successful.
     */
    public void setTransactionSuccessful() {
        acquireReference();
        try {
            // 获取 SQLiteSession,调用其 setTransactionSuccessful()
            getThreadSession().setTransactionSuccessful();
        } finally {
            releaseReference();
        }
    }
    
    
    /**
     * Marks the current transaction as having completed successfully.
     * <p>
     * This method can be called at most once between {@link #beginTransaction} and
     * {@link #endTransaction} to indicate that the changes made by the transaction should be
     * committed.  If this method is not called, the changes will be rolled back
     * when the transaction is ended.
     * </p>
     *
     * @throws IllegalStateException if there is no current transaction, or if
     * {@link #setTransactionSuccessful} has already been called for the current transaction.
     *
     * @see #beginTransaction
     * @see #endTransaction
     */
    public void setTransactionSuccessful() {
        throwIfNoTransaction();
        throwIfTransactionMarkedSuccessful();
        // 标记当前的 mTransactionStack
        mTransactionStack.mMarkedSuccessful = true;
    }
    
    

    三、endTransaction()

    1. 调用 SQLiteDatabase 的 endTransaction()

    public void endTransaction() {
        acquireReference();
        try {
            getThreadSession().endTransaction(null);
        } finally {
            releaseReference();
        }
    }
    
    

    2. 调用 SQLiteSession 的 endTransaction()

    /**
     * Ends the current transaction and commits or rolls back changes.
     * <p>
     * If this is the outermost transaction (not nested within any other
     * transaction), then the changes are committed if {@link #setTransactionSuccessful}
     * was called or rolled back otherwise.
     * </p><p>
     * This method must be called exactly once for each call to {@link #beginTransaction}.
     * </p>
     *
     * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
     *
     * @throws IllegalStateException if there is no current transaction.
     * @throws SQLiteException if an error occurs.
     * @throws OperationCanceledException if the operation was canceled.
     *
     * @see #beginTransaction
     * @see #setTransactionSuccessful
     * @see #yieldTransaction
     */
    public void endTransaction(CancellationSignal cancellationSignal) {
        throwIfNoTransaction();
        assert mConnection != null;
    
        endTransactionUnchecked(cancellationSignal, false);
    }
    
    
    private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
        if (cancellationSignal != null) {
            cancellationSignal.throwIfCanceled();
        }
    
        final SQLiteSession.Transaction top = mTransactionStack;
        // 当前 transaction 最终是否被认为成功要考虑两个方面:
        // 1. 当前 transaction 被标记为成功
        // 2. 当前 transaction 的子 transaction 同样被标记为成功
        boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
    
        RuntimeException listenerException = null;
        final SQLiteTransactionListener listener = top.mListener;
        if (listener != null) {
            try {
                if (successful) {
                    listener.onCommit(); // might throw
                } else {
                    listener.onRollback(); // might throw
                }
            } catch (RuntimeException ex) {
                listenerException = ex;
                successful = false;
            }
        }
        // 注意此处,替换 mTransactionStack 为外层的 transaction
        mTransactionStack = top.mParent;
        /**
         * 1. 把 mTransactionPool 赋值给调用 endTransaction 时所在的 transaction 的 parent
         * 2. 把 mTransactionPool 赋值为调用 endTransaction 时所在的 transaction
         */
        recycleTransaction(top);
    
        if (mTransactionStack != null) {
            if (!successful) {
                mTransactionStack.mChildFailed = true;
            }
        } else {
            // mTransactionStack == null, 意味着当前是最外层的 transaction
            try {
                if (successful) {
                    // 当前 transaction 成功及其子 transaction 也成功,则 COMMIT
                    mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
                } else {
                    // 否则回滚
                    mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
                }
            } finally {
                // 已经是最外层 transaction,释放主连接
                releaseConnection(); // might throw
            }
        }
    
        if (listenerException != null) {
            throw listenerException;
        }
    }
    
    

    相关文章

      网友评论

        本文标题:Sqlite 源码分析 -- SQLiteDatabase 使用

        本文链接:https://www.haomeiwen.com/subject/vyjnixtx.html