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.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()
相关推荐
niuyueyang6 分钟前
MCP `/mcp/status` Lifecycle 技术方案
java·服务器·数据库
Umi·16 分钟前
MySQL之DQL使用实例
数据库·mysql
sun_weitao17 分钟前
用 FastAPI + MQTT 做一个 ESP32最小化跑通示例
中间件·vue·fastapi
牧竹子1 小时前
当前支持向量的主流数据库情况对比
数据库
Small-Young2 小时前
数据库乐观锁深度解析:MySQL、PostgreSQL 实战 + Spring Boot 集成指南
数据库·spring boot·mysql·postgresql·乐观锁·并发控制
浮生若城3 小时前
MySQL 数据库基础
数据库·mysql
香蕉大香蕉(06敬畏市场)3 小时前
MySQL 单点详解(09)
java·数据库·mysql·面试·八股·大厂实习
瀚高PG实验室3 小时前
瀚高数据库运行日志(错误日志)查看与分析方法
数据库·postgresql·瀚高数据库
snow@li3 小时前
PostgreSQL:PostgreSQL(PG)全景深度梳理
数据库·postgresql
java_nnnn3 小时前
Spring IoC & DI 从入门到理解:控制反转、依赖注入与 Bean 管理详解
数据库·sql·spring