transaction<T> method

Future<T> transaction<T>(
  1. Future<T> action()
)

Executes action in a transaction, which means that all its queries and updates will be called atomically.

Returns the value of action. When action throws an exception, the transaction will be reset and no changes will be applied to the databases. The exception will be rethrown by transaction.

The behavior of stream queries in transactions depends on where the stream was created:

  • streams created outside of a transaction block: The stream will update with the tables modified in the transaction after it completes successfully. If the transaction fails, the stream will not update.
  • streams created inside a transaction block: The stream will update for each write in the transaction. When the transaction completes, successful or not, streams created in it will close. Writes happening outside of this transaction will not affect the stream.

Please note that nested transactions are not supported. Creating another transaction inside a transaction returns the parent transaction.

See also:

Implementation

Future<T> transaction<T>(Future<T> Function() action) async {
  final resolved = resolvedEngine;
  if (resolved is Transaction) {
    return action();
  }

  return await resolved.doWhenOpened((executor) {
    final transactionExecutor = executor.beginTransaction();
    final transaction = Transaction(this, transactionExecutor);

    return _runConnectionZoned(transaction, () async {
      var success = false;
      try {
        final result = await action();
        success = true;
        return result;
      } catch (e, s) {
        await transactionExecutor.rollbackAfterException(e, s);

        // pass the exception on to the one who called transaction()
        rethrow;
      } finally {
        if (success) {
          try {
            await transaction.complete();
          } catch (e, s) {
            // Couldn't commit -> roll back then.
            await transactionExecutor.rollbackAfterException(e, s);
            rethrow;
          }
        }
        await transaction.disposeChildStreams();
      }
    });
  });
}