Engine、Core、Session关系和区别
下面我把这几个容易混淆的点彻底拆开讲:
- SQLAlchemy 里的 Core 查询 和 ORM 查询,到底分别是什么
- Session 到底是什么,它和“数据库连接 connection”是什么关系
- 为什么 Web 项目推荐“每个请求一个 Session”
- 什么时候用 Core,什么时候用 ORM
- 常见误区与最佳实践
我会尽量用概念 + 类比 + 代码 + Web 场景来解释。
一、先说结论:你现在模糊,主要是混了 3 层东西
很多人刚学 SQLAlchemy 时会把下面三件事混在一起:
-
SQL 表达方式
- 原生 SQL
- SQLAlchemy Core
- SQLAlchemy ORM
-
数据库连接方式
EngineConnectionSession
-
事务管理方式
- 手动
BEGIN / COMMIT / ROLLBACK Connection级事务Session级事务
- 手动
其实这三层不是一回事。
你可以先记住:
一句话理解
- Core:偏底层,主要是“怎么写 SQL”
- ORM:偏对象化,主要是“怎么把表记录当 Python 对象来操作”
- Connection:一条具体数据库连接,偏底层
- Session:ORM 世界里的工作单元,负责对象状态管理 + 事务边界 + 持有/使用连接
- Web 请求一个 Session:是为了让一次请求中的数据库操作处在统一的事务和状态管理范围内
二、Core 和 ORM 到底是什么关系
1. Core 是什么
SQLAlchemy Core 可以理解为:
Python 方式来拼装 SQL 语句的工具层
它不是把数据库记录先变成 Python 对象来给你改,而是更接近:
- 表
- 列
- SQL 表达式
select / insert / update / deletejoin / group by / order by / returning
Core 的特点
- 更贴近 SQL
- 更适合复杂 SQL、批量操作、高性能场景
- 查询结果通常是 Row,不是 ORM 实体对象
- 不负责对象生命周期管理
Core 例子
from sqlalchemy import create_engine, MetaData, Table, select
engine = create_engine("postgresql+psycopg://user:pass@localhost/db")
metadata = MetaData()
users = Table("users", metadata, autoload_with=engine)
with engine.connect() as conn:
stmt = select(users).where(users.c.id == 1)
result = conn.execute(stmt)
row = result.first()
print(row)
这里的 row 一般像一行结果 ,不是 User() 对象。
2. ORM 是什么
ORM 是:
Object Relational Mapping,对象关系映射
它的核心思想是:
- 数据库表 ↔ Python 类
- 表中的一行 ↔ Python 对象实例
- 列 ↔ 类属性
- 外键关系 ↔ 对象关系属性
你写的是 User(username="alice"),但底层会帮你变成 INSERT INTO users ...
ORM 例子
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
from sqlalchemy import create_engine, select
engine = create_engine("postgresql+psycopg://user:pass@localhost/db")
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
username: Mapped[str]
with Session(engine) as session:
stmt = select(User).where(User.id == 1)
user = session.execute(stmt).scalar_one_or_none()
print(user.username)
这里拿到的是 User 对象,不是简单一行结果。
3. Core 和 ORM 的根本区别
最重要的区别不是“语法不同”,而是:
Core 关注的是:
- SQL 语句本身
- 查询出什么列
- 怎样拼条件、join、group by
- 执行后得到结果行
ORM 关注的是:
- 对象
User、Order这些实体- 实体之间的关系
- 对象状态是否修改过
- 提交时自动生成相应 SQL
4. 你可以把它们这样类比
Core 像什么
Core 像你自己在写:
- SQL 模板
- 参数绑定
- 执行查询
- 获取结果
它更像一个SQL 构造器/查询生成器
ORM 像什么
ORM 像你在操作:
user.name = "Tom"user.orders.append(order)session.add(user)
然后 ORM 替你决定什么时候发 INSERT/UPDATE/DELETE
三、为什么很多 ORM 查询看起来也在用 select(),却还是 ORM
这是 SQLAlchemy 2 最容易让人困惑的地方。
在 SQLAlchemy 2 里,Core 和 ORM 都用 select() 语法,所以看起来很像。
但关键区别在于:
1. 你 select() 的对象是什么
Core
select(users)
select(users.c.id, users.c.username)
这里 users 是 Table 对象,结果更偏向行数据
ORM
select(User)
select(User.id, User.username)
这里 User 是 ORM 映射类,结果会带有 ORM 语义。
2. 谁在执行它
Core 通常是 Connection.execute()
with engine.connect() as conn:
result = conn.execute(select(users))
ORM 通常是 Session.execute()
with Session(engine) as session:
result = session.execute(select(User))
3. 结果长什么样
Core 查询结果
with engine.connect() as conn:
result = conn.execute(select(users))
rows = result.all()
你拿到的是:
- Row
- tuple 风格
- mapping 风格
ORM 查询结果
with Session(engine) as session:
result = session.execute(select(User))
users = result.scalars().all()
你拿到的是:
User对象实例- 这些对象被
Session管理 - 后续修改对象属性,可能自动转成
UPDATE
四、最核心:Session 到底是什么
这个概念是最关键的。
1. Session 不是数据库连接池
不是。
连接池主要是 Engine 管。
2. Session 也不是“数据库本身的 session”这个词的简单等价物
数据库里也有 session/会话这个概念,但 SQLAlchemy 的 Session 是一个应用层抽象。
它更准确的理解是:
ORM 的工作单元(Unit of Work) + 身份映射(Identity Map) + 事务边界管理器
你先别被名词吓住,我拆开讲。
3. Session 的三个核心职责
职责 1:管理 ORM 对象
比如:
user = session.get(User, 1)
这个 user 对象不是普通对象,而是被 session 跟踪的对象。
如果你改它:
user.username = "newname"
Session 知道:
- 这个对象原来是什么
- 现在变成了什么
- 在提交时需要发一条
UPDATE
这叫对象状态追踪
职责 2:管理事务
比如:
with Session(engine) as session:
user = User(username="alice")
session.add(user)
session.commit()
这个 commit() 本质上是:
- flush 所有待提交改动
- 提交当前事务
如果报错:
session.rollback()
当前事务回滚。