MySQL 本身没有类(class)和对象(object)的概念——它是关系型数据库,只处理表、行、列、约束和索引。所谓“类与对象的设计”,实际是应用层(如 Python、Java、PHP)用 ORM 或手动映射把
SELECT结果转成内存中的对象,而表结构就是这个映射的源头。
表结构如何对应到编程语言中的类
核心原则:一张表 ≈ 一个类,一行记录 ≈ 一个实例,一列字段 ≈ 一个属性(字段名通常直接映射为属性名)。
users表 →
User类;
id、
name、
user.id、
user.name、
user.email主键
id通常映射为对象的唯一标识(如 Python 的
__eq__或 Java 的
equals()依据) 外键(如
order.user_id)不直接作为
Order类的
user_id: int字段存在,而应映射为
user: User关联对象(惰性加载或预加载时才查) 注意字段类型对齐:MySQL 的
TINYINT(1)常被误当布尔用,但 Python 中应映射为
bool,不是
int;
DATETIME映射为
datetime.datetime,不是字符串
常见映射陷阱:NULL、默认值与空字符串
数据库允许
NULL,但多数语言的对象属性不能天然表示“未定义”。若字段定义为
name VARCHAR(50) NULL,则: Python(SQLAlchemy)中,
user.name可能是
None,不是空字符串;硬写
if user.name:会漏掉
''和
None两种情况 Java(JPA)中,
@Column(nullable = true)对应包装类(如
String),但基本类型(如
int)无法接收
NULL,必须用
IntegerMySQL 默认值(
DEFAULT 'N')不会自动同步到对象初始化逻辑里;ORM 通常只在 INSERT 时用,默认不填充对象属性
CHAR(10)存
'a'会被补空格,读出来是
'a '—— Python 的
str.strip()或 MySQL 的
TRIM()必须显式处理
一对多、多对多关系怎么建模
关系不是靠外键字段“存对象”,而是靠查询关联 + 应用层组装。
CREATE TABLE posts ( id INT PRIMARY KEY, title VARCHAR(255), author_id INT, FOREIGN KEY (author_id) REFERENCES users(id) ); CREATE TABLE tags ( id INT PRIMARY KEY, name VARCHAR(50) ); CREATE TABLE post_tags ( post_id INT, tag_id INT, PRIMARY KEY (post_id, tag_id), FOREIGN KEY (post_id) REFERENCES posts(id), FOREIGN KEY (tag_id) REFERENCES tags(id) );
Post类里不要放
author_id: int字段(除非你明确需要 ID 而不查用户),而应放
author: User属性(ORM 自动 JOIN 或懒加载) 多对多(如
posts ↔ tags)必须通过中间表
post_tags,ORM 中常表现为
Post.tags: List[Tag],底层执行额外
SELECT或
JOIN避免“反范式化”地在
posts表里加
tags_json TEXT字段——这会让查询失效(无法索引、无法
WHERE tag = 'mysql')、破坏一致性、增加应用层解析负担
什么时候不该一对一映射表和类
不是所有表都值得变成独立类。以下情况建议合并、忽略或抽象:
纯关联表(如post_tags)一般不建独立类,而是让
Post和
Tag直接管理关系 配置表(
settings)如果只有几行且极少变动,可读进内存当字典用,不必每个 key 都建对象 历史快照表(
orders_history)和当前表(
orders)字段高度重合,可共用一个
Order类,但用不同 DAO/Repository 区分读写路径 宽表(含 50+ 列)拆成多个逻辑类(如
OrderHeader+
OrderPayment+
OrderShipping),按业务上下文加载,而非一股脑全查
真正难的不是“怎么映射”,而是决定哪些字段该进对象、哪些该延迟查、哪些压根不该出现在对象里——这取决于查询频次、一致性要求、序列化场景(API 返回?日志?缓存?)。表结构定下来后,对象模型反而要跟着用法反复调整,而不是一次性对齐就完事。
