注意事项:
-
插入单条数据不需要开启事务;
-
beginTransaction() 获取 主连接后不会释放,直到调用 endTransaction(). 所以在此期间如果有另一个线程进行 CRUD 操作,获取不到主连接,只能等待;
-
执行 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;
}
}
网友评论