返回介绍

数据库事务

发布于 2024-06-23 19:15:23 字数 6828 浏览 0 评论 0 收藏 0

It is crucial to correctly manage transactions in an asynchronous program, because you never know how much time an await will actually wait for, it will cause disasters if transactions are on hold for too long. GINO enforces explicit transaction management to help dealing with it.

Basic usage

Transactions belong to GinoConnection. The most common way to use transactions is through an async with statement:

async with connection.transaction() as tx:
    await connection.status('INSERT INTO mytable VALUES(1, 2, 3)')

This guarantees a transaction is opened when entering the async with block, and closed when exiting the block - committed if exits normally, or rolled back by exception. The underlying transaction instance from the database driver is available at raw_transaction, but in most cases you don't need to touch it.

GINO provides two convenient shortcuts to end the transaction early:

They will raise an internal exception to correspondingly commit or rollback the transaction, thus the code within the async with block after raise_commit() or raise_rollback() is skipped. The internal exception is inherited from BaseException so that normal try ... except Exception block can't trap it. This exception stops propagating at the end of async with block, so you don't need to worry about handling it.

Transactions can also be started on a GinoEngine:

async with engine.transaction() as tx:
    await engine.status('INSERT INTO mytable VALUES(1, 2, 3)')

Here a GinoConnection is borrowed implicitly before entering the transaction, and guaranteed to be returned after transaction is done. The GinoConnection instance is accessible at tx.connection. Other than that, everything else is the same.

重要

The implicit connection is by default borrowed with reuse=True. That means using transaction() of GinoEngine within a connection context is the same as calling transaction() of the current connection without having to reference it, no separate connection shall be created.

Similarly, if your Gino instance has a bind, you may also do the same on it:

async with db.transaction() as tx:
    await db.status('INSERT INTO mytable VALUES(1, 2, 3)')

Nested Transactions

Transactions can be nested, nested transaction will create a savepoint as for now on asyncpg. A similar example from asyncpg doc:

async with connection.transaction() as tx1:
    await connection.status('CREATE TABLE mytab (a int)')

    # Create a nested transaction:
    async with connection.transaction() as tx2:
        await connection.status('INSERT INTO mytab (a) VALUES (1), (2)')
        # Rollback the nested transaction:
        tx2.raise_rollback()

    # Because the nested transaction was rolled back, there
    # will be nothing in `mytab`.
    assert await connection.all('SELECT a FROM mytab') == []

As you can see, the raise_rollback() breaks only the async with block of the specified tx2, the outer transaction tx1 just continued. What if we break the outer transaction from within the inner transaction? The inner transaction context won't trap the internal exception because it recognizes the exception is not created upon itself. Instead, the inner transaction context only follows the behavior to either commit or rollback, and lets the exception propagate.

Because of the default reusing behavior, transactions on engine or db follows the same nesting rules. Please see GinoTransaction for more information.

Manual Control

Other than using async with, you can also manually control the transaction:

tx = await connection.transaction()
try:
    await db.status('INSERT INTO mytable VALUES(1, 2, 3)')
    await tx.commit()
except Exception:
    await tx.rollback()
    raise

You can't use raise_commit() or raise_rollback() here, similarly it is prohibited to use commit() and rollback() in an async with block.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文