为何SQLAlchemy执行SELECT时会开启事务?相关报错解析
问题:SQLAlchemy执行SELECT时自动开启事务导致报错的原因
当执行以下代码到with session.begin():行时,会抛出报错:sqlalchemy.exc.InvalidRequestError: A transaction is already begun on this Session
import datetime from sqlalchemy import create_engine, select, Integer, Column, String, DateTime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import Session Base = declarative_base() class Person(Base): __tablename__ = "people" id = Column(Integer, primary_key = True) name = Column(String(128), nullable = False) created_at = Column(DateTime, nullable = False, default=datetime.datetime.now(datetime.timezone.utc)) # # # # # # # # # # # # # # # # # # # # dsn = 'sqlite:////tmp/people.sqlite' engine = create_engine(dsn, echo=True) Base.metadata.drop_all(engine) Base.metadata.create_all(engine) session = Session(engine, future=True) p = Person(name="Able") session.add(p) session.commit() print("About to do a select...") sel = select(Person).filter_by(name="Able") q = session.execute(sel).scalars().first() print(q.created_at) with session.begin(): print("hello")
查看详细输出可知,SQLAlchemy在执行SELECT语句时会开启事务。理解INSERT或UPDATE操作开启事务的原因,但为何SELECT操作也会开启事务?
原因解析
这和数据库的事务机制以及SQLAlchemy的Session设计直接相关:
- 数据库默认行为:像SQLite这类数据库默认关闭自动提交模式,任何SQL语句(包括SELECT)执行时都会自动启动一个事务,直到显式执行
COMMIT或ROLLBACK才会结束,这是数据库本身的特性。 - SQLAlchemy Session的一致性保障:Session的核心作用是维护持久化上下文,为了保证查询结果的稳定性和数据一致性,它会在首次执行任何数据库操作(无论读写)时自动开启事务。比如执行SELECT时,事务能确保你在查询过程中看到的数据是一致的,不会被其他并发事务修改,这就是事务隔离性的体现。
- 报错的直接触发点:执行完SELECT后,自动开启的事务仍处于活跃状态,此时调用
session.begin()会尝试启动新事务,但Session默认不支持嵌套事务(除非使用保存点),因此抛出该错误。
解决办法
如果需要在SELECT后开启新事务,可先结束当前活跃事务:
# 执行查询后提交或回滚当前事务 session.commit() # 也可以用 session.rollback() with session.begin(): print("hello")
另外,若数据库支持保存点,也可以使用session.begin(nested=True)创建嵌套事务。
内容的提问来源于stack exchange,提问作者rlandster
相关产品推荐
相关产品推荐

