SQLAlchemy使用文本SQL执行UPDATE后ORM查询未获取更新值问题
核心结论
文本SQL不会创建单独的事务,你遇到的现象是ORM会话的一级缓存(身份映射机制)导致的,和事务隔离无关。
原因说明
- 你通过
session.execute()执行原生UPDATE语句时,语句会直接提交到数据库执行,但ORM的会话层感知不到你通过原生SQL修改了数据,会话中缓存的对应User对象不会自动同步更新。 - 调用
session.query(User).get(id)时,ORM会优先从会话的一级缓存中查找已经加载过的User对象,直接返回缓存的旧数据,不会发起数据库查询。 - 改用原生SELECT语句执行查询时,请求会直接发送到数据库,绕过ORM的对象缓存,因此拿到的是数据库中最新的更新后的值,全程都在同一个事务中运行,没有创建新事务。
解决方案
你可以选择以下任意一种方式解决ORM读取旧值的问题:
- 执行完原生UPDATE后,手动过期会话缓存,让ORM下次查询时主动拉取数据库最新数据:
# 这里改用参数绑定,避免SQL注入风险 stmt = "UPDATE user SET login_count = login_count + 1 WHERE userid = :userid" session.execute(stmt, {"userid": id}) # 过期指定对象缓存,比expire_all性能更好 session.expire(session.query(User).get(id)) # 也可以直接调用session.expire_all()清空所有缓存 user = session.query(User).get(id) print(user.login_count)
- 改用ORM语法更新字段,ORM会自动同步缓存状态:
user = session.query(User).get(id) user.login_count += 1 # 不需要提交事务,flush即可将更新同步到数据库,同时更新缓存 session.flush() print(user.login_count)
额外注意
你原代码中直接用字符串拼接SQL的写法存在SQL注入风险,建议统一改用参数绑定的方式传递查询参数,示例见第一种解决方案中的SQL写法。
内容的提问来源于stack exchange,提问作者Jude Diñoso
相关产品推荐
相关产品推荐

