SQLAlchemy会话问题:query_device回滚致外层ta事务无法提交
解决SQLAlchemy会话回滚导致外层事务提交失败的问题
问题本质
你使用了scoped_session,它在同一个线程内会复用同一个会话实例。query_device函数里的with Session()获取的会话,和ta函数里的session_是同一个对象。当query_device的with代码块结束时,会话会被自动关闭并触发回滚(因为该块内没有执行commit),直接导致外层ta中的会话失效,后续的session_.commit()自然无法正常执行。
解决方案
方法一:复用外层会话(推荐)
将外层的会话实例作为参数传入query_device,让查询操作和事务操作共享同一个会话上下文:
engine = create_engine(db_path + "?check_same_thread=False", pool_recycle=True, echo=True) session_factory = sessionmaker(bind=engine) Session = scoped_session(session_factory) def query_device(session, id_) : # 直接使用传入的会话执行查询 device = session.query(Device).filter(Device.deviceNumber == id_).first() return device def ta(): with Session() as session_: nurse_ = session_.query(Nurse).filter(Nurse.id == 1).first() nurse_.age = 800 # 把外层会话传给query_device query_device(session_, '11f0a51b01060a4a') session_.commit()
方法二:使用独立会话(适合需要隔离查询的场景)
如果query_device需要完全独立的会话,不要使用scoped_session,直接用原始的session_factory创建会话,这样两个会话互不干扰:
engine = create_engine(db_path + "?check_same_thread=False", pool_recycle=True, echo=True) # 移除scoped_session,直接使用sessionmaker session_factory = sessionmaker(bind=engine) def query_device(id_) : with session_factory() as session: device = session.query(Device).filter(Device.deviceNumber == id_).first() return device def ta(): with session_factory() as session_: nurse_ = session_.query(Nurse).filter(Nurse.id == 1).first() nurse_.age = 800 query_device('11f0a51b01060a4a') session_.commit()
注意:如果后续需要将query_device返回的对象放到外层会话中修改,需要调用session_.merge(device)来合并对象状态。
方法三:显式提交查询会话(不推荐)
若坚持使用scoped_session且不想传参,可以在query_device的with块内显式提交空事务,避免会话关闭时触发回滚。但这种做法无意义,会增加数据库开销,仅作参考:
def query_device(id_) : with Session() as session: device = session.query(Device).filter(Device.deviceNumber == id_).first() session.commit() # 提交空事务,防止自动回滚 return device
内容的提问来源于stack exchange,提问作者edcjian
相关产品推荐
相关产品推荐

