SQLAlchemy教程:expire_on_commit不要设置为True 作者:马育民 • 2026-08-17 14:20 • 阅读:10002 # 说明 创建 `session` 工厂对象时,代码如下: ``` async_session = async_sessionmaker( async_engine, # 绑定数据库引擎 class_=AsyncSession, # 指定会话类,调用 async_session() 返回的类型 expire_on_commit=False ) ``` ### 关键 `expire_on_commit` 的默认值是 `True`,需要将 `expire_on_commit` 设置为 `False` **作用:**执行 `commit` 后,会话中的 **表模型对象** **不过期**,再次访问对象中的属性(即:表中的字段),不需要重新查询数据库 # expire_on_commit 设置为 True 代码修改为: ``` async_session = async_sessionmaker( async_engine, # 绑定数据库引擎 class_=AsyncSession, # 指定会话类,调用 async_session() 返回的类型 expire_on_commit=True ) ``` 或者 去掉 `expire_on_commit` 参数,默认值为 `True` # 错误写法1 ### save() 函数 修改 `save()` 函数,执行 `commit` 后,再访问对象的属性,如:`id`,内容如下: ``` async def save(user: User): # AsyncSession 使用上下文管理器,自动关闭会话 async with async_session() as session: try: session.add(user) # 成功执行才提交事务 await session.commit() # commit 后,访问对象属性 print("id:", user.id) except Exception as e: # 如果报错就回滚 await session.rollback() raise e ``` ### main() 函数 ``` async def main(): # await init_table() # 添加数据 user = User(username="lilei", password="123456") await save(user) # ✅关键:主动dispose释放全部连接,避免GC阶段触发报错 await async_engine.dispose() ``` ### 报错 [](https://www.malaoshi.top/upload/0/0/1GW3sSq87dKr.png) ### 原因 1. `await save(db, user)` 内部执行 `await db.commit()` 2. 默认 `expire_on_commit=True`,执行 `commit()` 后, `user` 对象标记为 `expired` 过期 3. 读取 `user.id`,对象过期,SQLAlchemy 尝试自动 `SELECT` 刷新对象 4. `AsyncSession` 不能同步执行数据库 IO,直接抛 `MissingGreenlet` 错误 # 解决 解决方法有若干种,这里用:增加 `await db.refresh(user)` ### 代码 ``` async def save(user: User): # AsyncSession 使用上下文管理器,自动关闭会话 async with async_session() as session: try: session.add(user) # 成功执行才提交事务 await session.commit() # commit后再刷新模型对象,会自动查询 await session.refresh(user) print("id:", user.id) except Exception as e: # 如果报错就回滚 await session.rollback() raise e ``` ### 执行测试 程序能够正确执行,但看控制台,发现 `commit()` 后,又再次执行查询了: [](https://www.malaoshi.top/upload/0/0/1GW3sSuqp2uA.png) # 结论 如果将 `expire_on_commit` 设置为 `True` ,`commit` 后模型对象已经过期,不能再用。 再次访问对象属性(即:表中的字段值),为了避免报错,需要加额外代码,**导致再次查询数据** 所以:`expire_on_commit` 设置为 `False` # 完整代码 ``` import asyncio from sqlalchemy import String, Integer from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column # 数据库链接 DB_URL = "mysql+aiomysql://root:root@127.0.0.1:3306/fastapi_std?charset=utf8mb4" async_engine = create_async_engine( DB_URL, echo=True, # True打印执行SQL,调试打开;生产关闭 pool_size=5, # 连接池大小 max_overflow=10, # 最大连接数量 ) """ 创建异步会话工厂,通过该会话工厂,创建session 好处:参数统一在这里配置,如:expire_on_commit """ async_session = async_sessionmaker( async_engine, # 绑定数据库引擎 class_=AsyncSession, # 指定会话类,调用 async_session() 返回的类型 expire_on_commit=False # commit后,会话中的模型对象不过期,访问对象的属性,不需重新查询数据库 ) class Base(DeclarativeBase): pass class User(Base): __tablename__ = "user" id: Mapped[int] = mapped_column(Integer(),primary_key=True,autoincrement=True,comment="主键") username: Mapped[str] = mapped_column(String(50),comment="") password: Mapped[int] = mapped_column(String(50),comment="密码") async def init_table(): async with async_engine.begin() as conn: # 删除旧表(测试用) # await conn.run_sync(Base.metadata.drop_all) # 创建表 await conn.run_sync(Base.metadata.create_all) async def save(user: User): # AsyncSession 使用上下文管理器,自动关闭会话 async with async_session() as session: try: session.add(user) # 成功执行才提交事务 await session.commit() except Exception as e: # 如果报错就回滚 await session.rollback() raise e async def main(): # await init_table() # 添加数据 user = User(username="lilei", password="123456") await save(user) print("id:", user.id) # ✅关键:主动dispose释放全部连接,避免GC阶段触发报错 await async_engine.dispose() if __name__ == "__main__": asyncio.run(main()) ``` # 结论 如果将 `expire_on_commit` 设置为 `True` ,`commit` 后模型对象已经过期,不能再用。 再次访问对象属性(即:表中的字段值),为了避免报错,需要加额外代码,**导致再次查询数据** 所以:`expire_on_commit` 设置为 `False` 原文出处:/show_1GW3sWwNvAuY.html