事务¶
一个 事务 将一个或多个 SQL 语句分组为一个单一的工作单元。要么所有语句都成功并提交到数据库,要么一个都没有成功——数据库回滚到事务开始之前的状态。
Peewee 以 自动提交模式 运行:在显式事务之外运行的每个语句都在其自己的隐式事务中运行。要分组语句,请使用本文档中描述的工具。
db.atomic¶
Database.atomic() 是推荐的事务 API。atomic() 可以用作上下文管理器或装饰器,并且它会自动处理嵌套。
如果在包裹的代码块中发生未处理的异常,当前代码块将被回滚。否则,语句将在代码块结束时提交。
作为上下文管理器
with db.atomic() as txn:
user = User.create(username='charlie')
tweet = Tweet.create(user=user, content='Hello')
# Both rows are committed when block exits normally.
作为装饰器
@db.atomic()
def create_user_with_tweet(username, content):
user = User.create(username=username)
Tweet.create(user=user, content=content)
return user
如果未处理的异常从代码块中传播出来,事务(或保存点——见下文)将被回滚,并且异常会继续传播。
with db.atomic() as txn:
User.create(username='huey')
# User has been INSERTed into the database but the transaction is not
# yet committed because we haven't left the scope of the "with" block.
raise ValueError('something went wrong')
# This exception is unhandled - the transaction will be rolled-back and
# the ValueError will be raised.
# User('huey') was NOT committed, the transaction rolled-back.
# The ValueError is raised here.
手动提交 / 回滚¶
你可以在 atomic() 块内显式地提交或回滚。在调用 commit() 或 rollback() 后,一个新的事务(或保存点)会自动开始。
with db.atomic() as txn:
try:
save_objects()
except SaveError:
txn.rollback() # Roll back, new transaction starts automatically.
log_error()
finalize() # Runs in a new transaction.
# finalize()'s changes are committed here.
嵌套事务¶
最外层的 atomic() 块会创建一个事务。任何嵌套的 atomic() 块都会创建 保存点。保存点是事务中的一个命名点,你可以在不影响事务其余部分的情况下回滚到该点。
with db.atomic(): # Transaction begins.
User.create(username='charlie')
with db.atomic() as sp: # Savepoint begins.
User.create(username='huey')
sp.rollback() # Rolls back huey only.
User.create(username='alice') # New savepoint begins here.
User.create(username='mickey')
# Committed: charlie, alice, mickey. huey was rolled back.
保存点可以任意深度地嵌套
with db.atomic():
with db.atomic():
with db.atomic() as inner:
do_something_risky()
inner.rollback() # Only the innermost work is lost.
do_something_safe()
atomic() 在内部跟踪嵌套深度。你无需手动管理保存点名称或事务状态。
显式事务¶
Database.transaction() 打开一个不嵌套的显式事务。在外部 transaction() 块内的任何 transaction() 调用都会被忽略——只有最外层的事务是活动的。
仅当你明确需要一个扁平的、非嵌套的事务时才使用它。在大多数情况下,atomic() 是更好的选择。
如果在包裹的代码块中发生异常,事务将被回滚。否则,语句将在包裹的代码块结束时提交。
with db.transaction() as txn:
User.create(username='mickey')
txn.commit() # Commit now; a new transaction begins.
User.create(username='huey')
txn.rollback() # Roll back huey; a new transaction begins.
User.create(username='zaizee')
# zaizee is committed when the block exits.
如果你尝试使用 transaction() 上下文管理器在 Peewee 中嵌套事务,则只会使用最外层的事务。
由于这可能导致不可预测的行为,建议你使用 atomic()。
显式保存点¶
Database.savepoint() 在一个活动的事务中创建一个保存点。保存点必须发生在事务内部,但可以任意深度地嵌套。
with db.transaction() as txn:
with db.savepoint() as sp:
User.create(username='mickey')
with db.savepoint() as sp2:
User.create(username='zaizee')
sp2.rollback() # "zaizee" is not saved.
User.create(username='huey')
# mickey and huey were created.
如果你手动提交或回滚一个保存点,一个新的保存点将自动开始。
自动提交模式¶
Peewee 要求底层驱动程序以自动提交模式运行并自行管理事务边界。这与 DB-API 2.0 的默认行为不同,后者会隐式启动一个事务并要求你手动提交。因此,Peewee 会将所有 DB-API 驱动程序置于 自动提交 模式。
在极少数情况下,如果你需要直接控制 BEGIN/COMMIT/ ROLLBACK——完全绕过 Peewee 的事务管理——请使用 manual_commit()
with db.manual_commit():
db.begin() # Begin transaction explicitly.
try:
user.delete_instance(recursive=True)
except:
db.rollback() # Rollback! An error occurred.
raise
else:
try:
db.commit() # Commit changes.
except:
db.rollback()
raise
manual_commit 在代码块的持续时间内暂停 Peewee 的事务管理。atomic() 和 transaction() 在其内部没有效果。这在应用程序代码中很少需要。
SQLite 事务锁定模式¶
SQLite 支持三种事务锁定模式。当需要精确控制读写锁定时尚可使用这些模式。
with db.atomic('EXCLUSIVE'):
# No other connection can read or write until this commits.
do_something()
@db.atomic('IMMEDIATE')
def load_data():
# No other writer is allowed, but readers can proceed.
insert_records()
这三种模式
DEFERRED (默认) - 在读写发生时获取最少的必要锁。另一个写入者可以在 BEGIN 和你的第一次写入之间介入。
IMMEDIATE - 在 BEGIN 时获取写入保留锁。其他写入者将被阻塞;读取者可以继续。
EXCLUSIVE - 在 BEGIN 时获取排他锁。在事务完成之前,没有其他连接可以读取或写入。
另请参阅
Postgresql 隔离级别注意事项¶
Postgresql 支持每个事务可配置的隔离级别,从最宽松到最严格
READ UNCOMMITTED (未提交读)
READ COMMITTED (已提交读) (大多数部署中的默认值)
REPEATABLE READ (可重复读)
SERIALIZABLE (串行化)
有关讨论,请参阅 Postgresql 事务隔离文档。
默认隔离级别在初始化 PostgresqlDatabase 时指定
db = PostgresqlDatabase(
'my_app',
user='postgres',
host='10.8.0.1',
port=5432,
isolation_level='SERIALIZABLE')
# Or use the constants provided by the driver.
from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE
db = PostgresqlDatabase(
...
isolation_level=ISOLATION_LEVEL_SERIALIZABLE)
from psycopg import IsolationLevel
db = PostgresqlDatabase(
...
isolation_level=IsolationLevel.SERIALIZABLE)
要控制事务的隔离级别,你可以将所需的设置传递给最外层的 atomic() 块
with db.atomic('SERIALIZABLE') as txn:
...
嵌套的 atomic() 块不能指定隔离级别。