一、事务处理核心流程
1. 标准事务生命周期(增、删、改)
# 顺序:标记操作 → 执行SQL并提交 → 刷新对象
async with AsyncSessionLocal() as db:
# ① 标记操作(内存中记录,未发SQL)
db.add(new_object) # 新增
await db.delete(existing_object) # 删除
# 或者:stmt = update(...).where(...);await db.execute(stmt) # 更新
# ② 提交事务(发送SQL到数据库,永久落盘)
await db.commit()
# ③ 刷新对象(可选,从数据库加载最新值,如自增ID)
await db.refresh(new_object)
2. 查询流程(只读操作)
# 查询通常不需要 commit,只需执行并读取结果
result = await db.execute(select(News).where(...)) # 执行查询,返回 Result 对象
items = result.scalars().all() # 提取 Python 对象列表
二、各方法作用详解
| 方法 | 调用时机 | 作用 | 是否发送SQL |
|---|---|---|---|
db.add(obj) |
新增数据时 | 将对象标记为"待插入",加入会话的工作队列。 | ❌ 否 |
db.add_all([obj1, obj2]) |
批量新增 | 将多个对象标记为"待插入"。 | ❌ 否 |
await db.delete(obj) |
删除数据时 | 将对象标记为"待删除"。 | ❌ 否 |
await db.execute(stmt) |
执行任意SQL(含select/update/delete/insert) |
1. 将SQL语句发送给数据库服务器执行。 2. 返回 Result 对象(包含影响行数、游标等)。 3. 对于 update/delete,数据在数据库内存(Buffer Pool)中已变更,但事务未提交,其他连接不可见。 |
✅ 是 |
await db.commit() |
必须在所有增、删、改操作之后调用 | 1. 将当前事务中的所有变更(add/delete/execute)永久写入磁盘 。 2. 释放数据行上的锁。 3. 使变更对其他数据库连接可见。 4. 若在此前代码抛异常且未捕获,事务会自动回滚,所有变更撤销。 |
✅ 是(提交指令) |
await db.refresh(obj) |
调用 commit() 之后(通常是新增/更新后) |
从数据库重新查询该对象的最新状态,刷新本地内存属性(如获取自增 id、数据库默认值、计算列等)。 |
✅ 是(查询) |
await db.rollback() |
发生异常需要撤销变更时 | 撤销当前事务中所有未提交的变更(利用 Undo Log 回滚数据)。 | ✅ 是(回滚指令) |
await db.flush() |
在 commit 前需要提前获取数据库生成的ID时(极少手动调用) |
提前将 SQL 发送到数据库执行(写入内存),但不提交事务 。数据仍处于当前事务中,其他连接不可见,可通过 rollback 撤销。 |
✅ 是 |
三、事务自动管理(上下文管理器)
使用 async with AsyncSessionLocal() as db: 时,会自动 在代码块结束时执行 close()(归还连接到池)。但它不会自动 commit,仍需显式调用。
若代码块内抛异常,且未显式调用 commit,会话会在关闭时自动执行 rollback。
四、result.rowcount 和 commit 的关系
-
result.rowcount:由execute执行后立即从数据库返回,表示本次 SQL 语句影响了数据库内存中的多少行 。此值不依赖commit,即使未提交,值也已获取。 -
commit:将内存变更持久化到磁盘,并释放锁。若省略commit,内存变更会在会话关闭时被rollback撤销。
五、典型代码范例(增、改、删)
1. 新增 + 获取自增ID
new_news = News(title="标题", content="内容")
db.add(new_news)
await db.commit() # 此时数据库已插入,ID已生成
await db.refresh(new_news) # 将生成的ID加载到本地对象
return new_news.id
2. 更新(方式一:对象属性修改)
result = await db.execute(select(News).where(News.id == id))
news = result.scalar_one()
news.title = "新标题"
await db.commit() # 自动检测到对象变更,生成UPDATE发送
3. 更新(方式二:批量更新,不查询)
stmt = update(News).where(News.category_id == 1).values(views=News.views + 1)
result = await db.execute(stmt)
await db.commit()
return result.rowcount # 返回受影响行数
4. 删除
news = await db.get(News, id)
if news:
await db.delete(news)
await db.commit()