FastAPI + SQLAlchemy 异步会话(AsyncSession)核心方法

一、事务处理核心流程

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.rowcountcommit 的关系

  • 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()
相关推荐
坐吃山猪1 小时前
【多线程】FutureTask多线程底层实现
java·开发语言·数据库
weixin199701080161 小时前
[特殊字符]《从0到1:闲鱼开放平台授权登录 + AccessToken 刷新 + 聚石塔部署完整链路》(附Python源码)
java·数据库·python
cmes_love1 小时前
国内期货Level2五档行情与逐笔成交数据介绍
数据库·区块链
cmes_love1 小时前
CME和CBOT外盘期货数据下载资源介绍
数据库·区块链
Zenova EdgeOS1 小时前
工业边缘双写一致性:从缓存到多 DB 的工程实战
数据库·缓存·边缘计算·工业边缘
志栋智能2 小时前
运维不再“猜谜”:自动化定界终结无效排查
运维·数据库·自动化
职场的momo2 小时前
16384个槽怎么搬?解析Redis集群迁移与脑裂防御
数据库·redis·缓存
隔窗听雨眠2 小时前
异构数据库迁移至南大通用GBase 8a完全指南:从调研到双轨切换的实战路径
数据库
CNSSIRD数据库2 小时前
中国企业海关信用数据库
大数据·数据库·数据分析·论文笔记