← [ BACK_TO_NOTES ]

Python 数据访问与 SQLAlchemy

数据访问层隔离 SQL/ORM;SQLAlchemy 为核心生态之一,需理解会话、事务与 N+1。

#synthesis #growing #dev #python #python

[!info] related notes

Python 数据访问与 SQLAlchemy

这个概念为什么出现

业务若直接拼 SQL 字符串,会散落注入风险与事务边界混乱。数据访问层(Repository/DAO)+ ORM/查询构建器提供结构,但 ORM 不是免费午餐(N+1、隐式 IO)。

[!abstract] 一句话理解 把持久化细节关在数据层;SQLAlchemy 提供 Core/ORM,会话与事务界定工作单元,查询行为必须可观察。

最小可运行示例

场景:用 SQLAlchemy 2.x 写入并查询用户

在隔离 Python 环境中安装 python -m pip install 'SQLAlchemy>=2,<3'。示例用内存 SQLite,只为验证 ORM 映射、提交和查询,不连接生产数据库。

# sqlalchemy_demo.py
from sqlalchemy import String, create_engine, select
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"
    id: Mapped[str] = mapped_column(String, primary_key=True)
    email: Mapped[str] = mapped_column(String, nullable=False)


def main() -> None:
    engine = create_engine("sqlite+pysqlite:///:memory:")
    try:
        Base.metadata.create_all(engine)  # 演示建表,不代替生产迁移
        with Session(engine) as session:
            with session.begin():
                session.add(User(id="u1", email="a@example.com"))
            # begin 正常结束才提交;仅 close Session 不会自动提交。

        with Session(engine) as session:
            email = session.scalar(select(User.email).where(User.id == "u1"))
            print(email)  # 取标量,不把依赖会话的 ORM 对象传到外层。
    finally:
        engine.dispose()


if __name__ == "__main__":
    main()

执行 python sqlalchemy_demo.py,应输出 a@example.com。内存库仅在这个 Engine 的连接生命周期内存在;多连接、多线程测试要明确 SQLite 内存库与连接池的配置,不能假设所有连接看到同一份数据。

原来的字典仓储例子只演示内存读写:add 会覆盖同 ID 值,也没有独立接口或事务。它不能用来证明 SQLAlchemy 的唯一约束、持久性、并发或回滚行为。

结合场景再看三个关注点

  1. 示例直接使用 ORM;Repository/DAO 是按业务需要选择的边界,不是 SQLAlchemy 强制要求。
  2. Engine 通常按数据库配置复用,Session 按工作单元创建。
  3. 谨防延迟加载触发隐式查询;按使用路径选择预加载,并观测 SQL 数量。

核心概念与准确模型

  • Engine 管理方言、DBAPI 与连接池策略;不是业务事务本身。
  • Session 维护工作单元与身份映射,flush 把变更发往数据库但不等于 commit;查询也可能触发 autoflush。
  • Core 提供 SQL 表达式与连接层,ORM 在其上增加映射、身份跟踪与对象工作单元。
  • create_all 适合示例或初始化,不负责已有表的增量版本迁移;迁移可用 Alembic。

边界情况与反直觉行为

  1. N+1:先查列表,再逐条懒加载关联;一次查询是否足够取决于访问路径,不由使用 ORM 自动保证。
  2. 并发与生命周期:Session 不供多线程共享,AsyncSession 不供并发任务共享;按线程/任务工作单元创建,事务范围不必等于整个 HTTP 请求。
  3. 身份映射只属于会话:不是跨进程对象缓存。提交后对象默认可能过期,关闭会话后访问尚未加载的属性可能失败。
  4. 失败处理:begin 上下文遇异常会回滚;自行 flush 失败后若继续复用同一 Session,需要显式 rollback 或结束该工作单元。
  5. 参数化:表达式会绑定值;自行拼接 text SQL、动态表名或排序字段仍需明确约束,ORM 不自动消除所有注入风险。

常见误区

[!warning] 常见误区:ORM 对象当跨层 DTO 到处传 错误理解:省转换。
正确模型:边界用明确 schema,避免会话剥离问题。

工程实践

  • 事务边界清晰
  • 慢查询日志
  • 迁移版本化

本节总结

数据访问是正确性与性能交汇点。先边界,后 ORM 技巧。

自测题

  1. 仓储模式收益?
  2. N+1 是什么?
参考答案
  1. 业务与存储解耦、可测试。
  2. 循环中每次额外查询,放大延迟。

延伸阅读与资料来源

资料类型支撑内容
SQLAlchemy文档ORM/Core
创建于 2026/7/15 更新于 2026/10/17
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本