Skip to content

第 3 章 记录的新增 ​

从一个生活场景说起 ​

延续上一章的比喻:表结构设计好了(Excel 表头画好了),现在要往表格里填一行行数据了。

往数据库写数据有点像在 ATM 机上转账:

  1. 你在屏幕上输入金额、确认收款人(构建 SQL);
  2. 点击"确认"(执行 execute)——但此时钱还没有真正转出去;
  3. 机器最后再问一次"确定提交吗?",你按下"确定"(commit),交易才生效;
  4. 如果中途断电或你按了"取消",一切恢复原样(rollback)。

SQLAlchemy 2.0 默认就是这种"先操作、后确认"的模式。忘记最后一步"确认",前面做的修改会全部作废——这是新手最容易踩的坑,本章会反复强调。

核心概念 ​

插入数据的三个角色 ​

  • insert(table):构建一条 INSERT 语句的"指令单",此时还没执行;
  • conn.execute(指令单):把指令单交给连接,让 MySQL 执行;
  • conn.commit():确认提交,让修改真正写入磁盘。

术语解释 —— 事务(Transaction):一组数据库操作的"打包"。要么整包生效,要么整包作废。commit 是"确认整包",rollback 是"作废整包"。本章先用起来,第 16 章会专门深入讲解。

SQLAlchemy 2.0 的执行规则 ​

2.0 版本有一条铁律:任何 SQL 都必须通过 Connection 或 Session 来执行,不能像以前那样直接 engine.execute()。拿到 Connection 的标准姿势是:

python
with engine.connect() as conn:
    ...

with 语法保证:无论中间是否出错,退出代码块时连接都会被归还到连接池(回顾第 1 章的"借-用-还")。

示例代码(逐行讲解) ​

python
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, insert

engine = create_engine(
    "mysql+pymysql://root:123456@localhost:3306/demo_db?charset=utf8mb4",
    echo=True,
)
metadata = MetaData()
user_table = Table(
    "user", metadata,
    Column("id", Integer, primary_key=True, autoincrement=True),
    Column("name", String(50), nullable=False),
    Column("age", Integer),
)
metadata.create_all(engine)

with engine.connect() as conn:
    # ① 插入单条:构建指令单 → 执行
    stmt = insert(user_table).values(name="张三", age=20)
    conn.execute(stmt)

    # ② 批量插入:一条指令单,配多组数据
    conn.execute(
        insert(user_table),
        [
            {"name": "李四", "age": 22},
            {"name": "王五", "age": 25},
        ],
    )
    # ③ 确认提交:不做这一步,上面两条插入都会作废!
    conn.commit()

重点看三处:

  1. insert(user_table).values(...) 只是"填写指令单",打印出来就是一条 SQL;
  2. conn.execute(stmt) 才是把指令单发出去执行;
  3. conn.commit() 是最后的"确认键"。

批量插入的写法很巧妙:把字典列表作为 execute 的第二个参数,SQLAlchemy 会自动拼成一条高效的批量 INSERT:

sql
INSERT INTO user (name, age) VALUES ('李四', 22), ('王五', 25)

比一条条执行快得多——记住这个模式,以后批量写数据都用它。

关键知识点 ​

概念说明
insert(table)构建 INSERT 语句
.values()指定插入的字段值
conn.execute()执行 SQL,参数化防止 SQL 注入
conn.commit()提交事务,让修改真正写入数据库

深入理解:为什么非要 commit? ​

这背后是一个重要的安全设计。试想银行转账:"A 账户减 100"和"B 账户加 100"必须同时成功或同时失败。如果执行完第一条系统就崩溃,第二条没执行,钱就凭空消失了。

所以数据库默认开启一个"事务":你的所有修改先放在"草稿区",只有 commit 才誊写到"正式账本";中途出错可以 rollback 把草稿撕掉,账本纹丝不动。

SQLAlchemy 2.0 把"何时 commit"的决定权完全交给你,这就是"手动提交"模式。

术语解释 —— SQL 注入:如果把用户输入直接拼进 SQL 字符串,恶意用户可以构造特殊输入篡改你的 SQL(比如删掉整张表)。SQLAlchemy 的 .values() 采用参数化方式传值——数据永远当数据、不会被当作 SQL 代码执行,从根上杜绝了注入。

进阶内容 ​

1. 自动提交的写法:engine.begin()。 如果你确定一个代码块里的操作要"同生共死",可以用它让 commit/rollback 全自动:

python
with engine.begin() as conn:
    conn.execute(insert(user_table).values(name="赵六", age=30))
    # 正常退出 with 块 → 自动 commit
    # 中途抛出异常   → 自动 rollback

2. 拿到新插入行的自增 id。

python
with engine.begin() as conn:
    result = conn.execute(insert(user_table).values(name="张三", age=20))
    print(result.inserted_primary_key)   # 例如 (1,),即新行的 id

3. 查看执行结果。 result.rowcount 可以告诉你这条语句影响了多少行,写断言或日志时很有用。

注意事项 ​

  • SQLAlchemy 2.0 默认是"手动提交"模式,忘记 commit() 是新手最常见的错误。
  • 优先使用 engine.begin() 上下文管理器,退出时自动提交、出错自动回滚,不容易遗漏。
  • 永远不要拼字符串构造 SQL,交给参数化即可避免 SQL 注入。
  • 中文数据正常入库的前提是:数据库、表、连接串三处都是 utf8mb4。
  • 批量插入传字典列表给 execute 的第二个参数,不要循环里逐条执行——性能差几个数量级。

本章小结 ​

  • 插入三步走:insert() 构建指令 → execute() 执行 → commit() 确认;
  • 不 commit 一切作废,这是事务安全设计的体现;
  • 批量插入用"一条语句 + 多组数据"的模式;
  • 想省心就用 with engine.begin()。