第 3 章 记录的新增
从一个生活场景说起
延续上一章的比喻:表结构设计好了(Excel 表头画好了),现在要往表格里填一行行数据了。
往数据库写数据有点像在 ATM 机上转账:
- 你在屏幕上输入金额、确认收款人(构建 SQL);
- 点击"确认"(执行 execute)——但此时钱还没有真正转出去;
- 机器最后再问一次"确定提交吗?",你按下"确定"(commit),交易才生效;
- 如果中途断电或你按了"取消",一切恢复原样(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 的标准姿势是:
with engine.connect() as conn:
...with 语法保证:无论中间是否出错,退出代码块时连接都会被归还到连接池(回顾第 1 章的"借-用-还")。
示例代码(逐行讲解)
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()重点看三处:
insert(user_table).values(...)只是"填写指令单",打印出来就是一条 SQL;conn.execute(stmt)才是把指令单发出去执行;conn.commit()是最后的"确认键"。
批量插入的写法很巧妙:把字典列表作为 execute 的第二个参数,SQLAlchemy 会自动拼成一条高效的批量 INSERT:
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 全自动:
with engine.begin() as conn:
conn.execute(insert(user_table).values(name="赵六", age=30))
# 正常退出 with 块 → 自动 commit
# 中途抛出异常 → 自动 rollback2. 拿到新插入行的自增 id。
with engine.begin() as conn:
result = conn.execute(insert(user_table).values(name="张三", age=20))
print(result.inserted_primary_key) # 例如 (1,),即新行的 id3. 查看执行结果。 result.rowcount 可以告诉你这条语句影响了多少行,写断言或日志时很有用。
注意事项
- SQLAlchemy 2.0 默认是"手动提交"模式,忘记
commit()是新手最常见的错误。 - 优先使用
engine.begin()上下文管理器,退出时自动提交、出错自动回滚,不容易遗漏。 - 永远不要拼字符串构造 SQL,交给参数化即可避免 SQL 注入。
- 中文数据正常入库的前提是:数据库、表、连接串三处都是
utf8mb4。 - 批量插入传字典列表给
execute的第二个参数,不要循环里逐条执行——性能差几个数量级。
本章小结
- 插入三步走:
insert()构建指令 →execute()执行 →commit()确认; - 不 commit 一切作废,这是事务安全设计的体现;
- 批量插入用"一条语句 + 多组数据"的模式;
- 想省心就用
with engine.begin()。