☰
对象模型与关系模型:数据库设计中的两种世界观及ORM映射实践
2026/10/19 1:53:14 网站建设 项目流程

很多人在初学数据库时会有一种困惑:明明业务代码是面向对象写的,比如订单、用户、商品都是一个一个的对象,怎么一落到数据库里就全变成了一张一张的表?这个小标题里的“对象模型”和“关系模型”,其实就是这两套世界观各自的名字。

我把这个话题拆透,聊清楚这两个模型各自的来历、设计思路、优缺点,以及今天我们在实际项目中是怎么让它们共存的。本文适合刚入门的同学建立完整认知,也适合已经写过不少增删改查、想回头补一补底层概念的人。

1. 先搞清楚:数据模型到底是在解决什么问题

1.1 用“填表格”和“摆积木”来理解两种模型

数据模型这个词听起来很学术,本质上它回答的只有一个问题:程序里的一堆数据,用什么规则组织起来,才能让存取方便、计算高效、不出错。

关系模型的做法,可以类比成填表格。一张学生表,一行是一个学生,每一列是学生的某个属性。你只需要定义清楚表格的列名和类型,数据就按行列去填。这种做法的好处是极度规整,像 Excel 一样,谁都能看懂,各种统计也很容易做。

对象模型的做法,则更像是摆积木。一个订单对象内部包含订单号、金额、客户信息,而客户对象自己又有一堆属性和方法。对象之间通过引用互相连接,不要求你强行把数据拆成一张张二维表。

这两种思路从根上就不同,后面几十年数据库领域的发展,其实都绕不开这个“二维表”与“对象图”之间的拉扯。

1.2 数据模型的三要素:结构、操作、约束

正经教材里会把数据模型拆成三部分:数据结构、数据操作、完整性约束。我用自己的话翻译一下:

数据结构解决的是“数据长什么样”的问题。关系模型说长成表那样,对象模型说长成对象图那样。这一条决定了数据库底层存储和对外接口的基本形态。

数据操作解决的是“能对数据做什么”的问题。关系模型有查询语言和集合运算,对象模型有方法调用和消息传递。操作方式直接决定了开发者的编码体验和查询性能。

完整性约束解决的是“数据怎么保证不出错”的问题。关系模型有主键、外键、唯一约束,对象模型则是靠对象自身的封装性和类的定义来保证数据合理性。

这三个维度共同构成了一个数据模型的完整面貌。判断一个模型好不好用,也往往从这三个角度分别打量。

1.3 为什么说模型选择不只是技术选型,而是一种世界观

如果说数据结构决定了数据怎么存,那么模型的选择其实进一步决定了业务逻辑该往哪里放。

关系模型的天然导向是“数据优先”:先把业务拆成规范化程度很高的表,然后通过查询把它们临时拼装出来。业务规则往往体现在查询语句和约束条件里。

对象模型的天然导向是“行为优先”:业务对象本身封装数据和行为,存储方式只是对象的一种持久化手段。写程序的时候不用考虑表结构,定义的类就是数据的天然形态。

我见过不少团队在这两种世界观之间摇摆。有人觉得关系模型教条,写起多表关联的 SQL 非常痛苦;也有人觉得对象模型不落地,存个数据还得做各种映射。实际上,这两种模型各有明确的适用土壤,搞清楚这点比纠结“哪个更先进”有用得多。

2. 对象模型:贴近业务代码的世界观

2.1 对象模型的核心构成:对象、属性、关联、继承

对象模型的核心概念其实不多,但每一个都直接对应着面向对象程序设计里的东西。

对象是最基本的存储单元,这东西自带唯一标识,也就是说每个对象不管它的内容怎么变化,它在数据库里的身份是固定的。这个特性非常重要,因为对象的识别不依赖它的属性值,而是依赖身份本身。

属性就是对象的特征数据,比如一个员工的姓名、工号、工资。属性和方法共同封装在对象内部,外部只能通过公共接口来访问,这是封装思想在数据模型层面的体现。

关联描述的是对象之间的引用关系。一个订单对象引用一个客户对象,一个客户对象引用一组订单对象,这种引用在对象模型里可以直接表达,不需要像关系模型那样靠外键列去模拟。

继承则是对象模型最亮眼的部分。子类对象继承父类对象的属性和方法,同时还拥有自己的特有属性。放在数据库语境里,就意味着我们可以把不同类但有关联的对象统一管理,查询时还能保持多态行为。

2.2 对象数据库是怎么存取数据的

基于对象模型建立的数据库,就是通常说的对象数据库。它与关系数据库最大的区别在于:存入的数据就是完整对象,取出来的也是完整对象,中间没有把对象拆开变成多张表的过程。

在实际的对象数据库系统里,每个对象有全局唯一的对象标识符,类似内存里的地址,但是被持久化到磁盘。对象之间的引用也直接通过这个标识符完成,查询的时候顺着引用跳转到下一个对象就行,不需要做表的连接操作。

这个设计比较符合人的直觉。比如要查某个客户的未支付订单,对象数据库里就是“找到客户对象,顺着订单集合引用筛选状态为未支付的”,一步到位。而关系数据库呢,是先关联两张表,再做条件过滤,虽然结果一样,但思考方式完全不同。

2.3 对象模型之所以存在,本质是为了消除“阻抗失配”

这里有个概念叫阻抗失配,指的是程序设计语言和数据库系统之间,因为数据组织方式不同导致的需要做转换的额外开销。

举个最简单的例子。面向对象语言里你定义一个订单类,订单属于某个客户,类里面直接嵌套一个客户对象引用,这是天然的对象图。但如果你用关系数据库存储,必须把订单存一张表、客户存一张表,中间用外键关联。每次读写都要在代码和 SQL 之间来回翻译,这就产生了阻抗失配。

对象数据库的初衷,就是让持久化存储和程序内存里的模型保持一致,省掉翻译那一步。20世纪80年代到90年代,一批对象数据库产品就是这么打市场的,主打的是“消除阻抗失配、开发效率提升”。

但后来的历史大家都看到了,这类产品并没有取代关系数据库。原因不是它不好,而是关系模型几十年的积累太深厚,工具链、人才、标准化程度都远超对象数据库。这是个很典型的“更好的技术未必赢得市场”的例子,后面我会详细讲。

3. 关系模型:统治数据库四十年的“表思维”

3.1 关系模型的数学基础:集合论与关系代数

关系模型之所以叫“关系”模型,不是说它研究人与人的关系,而是因为它建立在数学中“关系”这个概念之上。数理逻辑里的关系,指的就是元组构成的集合。

一套关系模型里有若干关系模式,每个关系模式相当于一张表的定义。一个关系实例就是一张具体的表,表中的一行是一个元组,每个元组由若干属性值组成。所有元组构成的集合就是这张表的全部内容。

因为关系是集合,所以关系代数天然支持集合运算:并、交、差、笛卡尔积,还有专门的关系运算:选择、投影、连接。

选择是从表中挑出满足条件的行,比如“挑出所有 1995 年以后出生的员工”。投影是选出指定的列,比如“只要姓名和工资这两列”。连接是把两张表根据关联条件拼在一起。

我非常推崇关系模型的一点就是:它把数据操作建立在严格的数学基础上,所以查询优化的空间非常大。你写一条查询,数据库可以自动选择先做选择还是先做连接,排序怎么做最省,这在后续的性能调优里极有价值。

3.2 关系模型里的关键操作和约束

关系模型的操作集合非常清晰,先挑选行、再挑选列、再做集合运算、再连接多张表,这四类操作覆盖了绝大多数的查询需求。标准化之后,就是今天大家都熟悉的 SQL。

约束方面,关系模型把完整性保证做进了系统底层,而不是留给应用层自己处理。主键约束保证每一行都是唯一可识别的,外键约束保证引用关系不会指向不存在的行,唯一约束保证某些取值不会重复。这些约束一旦设定,数据库在写入时就会自动拦截非法数据。

规范化理论是关系模型另一大遗产。这个理论把表结构拆分成多个范式,每个范式都解决一类数据冗余或更新异常的问题。第一范式要求属性不可再分,第二范式要求非主属性完全依赖主键,第三范式要求非主属性不依赖其他非主属性。

规范化的本质是让表结构尽量稳定、信息不重复。虽然过度规范化在真实项目中也会引发性能问题和查询复杂度问题,但在设计阶段,规范化思维依然是审查表结构是否合理的最强工具。

3.3 关系模型为什么能赢:三个不可忽略的理由

关系模型能统治数据库领域这么多年,我认为有三个关键原因,缺一不可。

第一是数据独立性。在关系模型里,应用程序面对的是表的一个抽象视图,底层数据怎么存、索引怎么建、文件怎么分布,应用统统不关心。这个抽象级别给了数据库系统极大的优化自由度,也让应用的生命周期远超底层存储技术。

第二是集合操作能力。基于集合的模型使得一条 SQL 就能描述“跨多表筛选多个条件”的复杂查询,而且执行计划可以交给底层的优化器处理。相比之下,面向对象查询要是没有配套查询引擎,很容易退化成逐个对象遍历,效率差距非常明显。

第三是事务和并发保障。关系数据库经过几十年发展,把事务的原子性、一致性、隔离性、持久性,也就是俗称的事务特性,打磨得非常成熟。金融、电商、ERP 这类对数据一致性要求极高的场景,目前还真没有比关系数据库更让人放心的选择。

这三点恰好都是企业在做技术选型时最看重的东西。所以哪怕程序员的开发体验里掺杂着 SQL 拼接的痛,关系数据库也依然是绝大多数系统的默认底座。

4. 对象模型与关系模型正面对比:核心差异拆解

4.1 九个关键维度的对照表

这一节把两个模型直接摆在台面上做一次全维度对比,方便你建立整体感。

对比维度对象模型关系模型
数据结构对象图,对象互相引用二维表,元组构成的集合
数据标识对象标识符,独立于属性值主键,基于属性值的唯一标识
数据操作方法调用与消息传递,偏过程式声明式查询语言,偏集合运算
关系表达对象引用直接表达关系外键列表达关系,查询时再连接
继承支持原生支持,类继承体系不支持,需要额外设计策略
数据独立性较弱,与程序设计语言深度绑定强,数据逻辑独立于应用
查询能力依赖导航式访问,表达能力有限集合操作强大,复杂查询简洁
事务与并发多用于内存态,持久化支撑较弱成熟完备,是核心优势之一
市场成熟度早期产品多,如今主流为补充角色成熟稳定,标准统一,工具链完整

从这张表能看出一个很关键的结论:对象模型赢在表达能力,关系模型赢在系统能力。前者跟人靠得近,后者跟基础设施靠得近。

4.2 为什么对象数据库最终没有取代关系数据库

这是个很有意思的历史问题。我梳理下来,主要有四个层面的因素。

技术生态层面,关系数据库有 SQL 标准,有一套统一的教学和人才体系,工具链渗透到方方面面,包括报表工具、备份恢复工具、连接驱动、ORM 框架。对象数据库虽然模型先进,但各家实现彼此不兼容,学了这个不会那个,用户迁移成本极高。

工程实践层面,关系数据库的查询优化器经过数十年打磨,已经非常成熟。即便一条 SQL 写得不太理想,优化器也能通过代价估算自动调整执行计划,制造出不错的性能表现。对象数据库的导航式查询在性能调优上更依赖应用层开发者的个人功力,不容易推广到普通团队。

事务可靠性层面,前面已经提到,关系数据库把事务和并发控制做到了近乎工业级的标准。企业数据最怕的就是出错之后无法恢复,评标时“能让我睡个好觉”这个指标的真实权重,远超技术爱好者的想象。

开发收益层面,对象模型消灭阻抗失配的初衷很美好,但真落地的代价是创建一套全新的数据访问方式,意味着团队要学新语言语法、新驱动、新工具。而关系模型配合 ORM 也能把阻抗失配降到可接受的程度。既然两条路都要学东西,大家自然会选更稳定、社区更大的那一条。

所以对象数据库输给关系数据库,不是输在模型表达力上,而是输在生态、标准、可靠性和成本这几项硬指标上。

4.3 对象关系数据库与 ORM:两条现实中的折衷路线

既然纯对象数据库没能成为主流,行业里自然演化出两种折衷方案。

第一种是对象关系数据库。这类系统在关系数据库的表结构之上,增加了一些对象化的能力,比如支持自定义类型、支持嵌套表、支持用户定义函数。PostgreSQL 就是比较典型的例子,它提供表继承、复合类型、数组等能力,让开发者能写更接近对象定义的建表语句。不过实践下来,这些特性的使用频率其实不算特别高,多数团队还是会选择更传统的表设计。

第二种是对象关系映射框架,也就是 ORM。这才是绝大多数开发者日常接触最多的折衷方案。ORM 做的事情是:在应用程序层把关系表的数据映射成对象,让你用操作对象的方式去操作数据,同时底层还是走标准 SQL 访问关系数据库。

用 ORM 的好处是日常增删改查非常舒服,代码模型干净,开发效率高。代价是当查询特别复杂的时候,ORM 生成的 SQL 可能不够优化,这时候还得退回手写 SQL。这其实也从侧面验证了关系模型对复杂查询的表达优势,是绕不开的。

5. 实操参考:把对象模型落到关系模型上的经典映射方案

5.1 映射的基本套路:类对应表,对象对应行,属性能对应列

在实际项目里,最普遍的做法并不是在对象数据库和关系数据库之间二选一,而是采用关系数据库做底座,同时用一套约定把对象模型映射成表结构。这套约定不断发展,成了今天各种 ORM 框架的底层逻辑。

最基本的映射规则有三条:一个业务类对应一张表,类的属性对应表的列,类的一个实例对应表的一行。比如有一个用户类,包含用户标识、用户名、邮箱三个字段,那么对应到数据库就是一张用户表,表里三列,每条数据一行。

这是最理想的情况,也就是所谓的一对一映射。但现实业务往往没那么整齐,类和类之间的继承关系、嵌套关系、集合关系,都需要额外设计。

在设计映射方案的时候,我有一条实践经验:不要机械地要求一个类必须对应一张表。先梳理业务对象之间的关联强度和查询路径,再决定哪些对象可以合并到一张表,哪些必须拆开。映射方案最终是给查询和写入服务的,不是给代码美学服务的。

5.2 继承关系在二维表里怎么表达:三种常见策略

在关系模型里,继承关系没有原生表达方式,但有三种成熟的落表策略,各有取舍。

第一种是单表继承。把整个继承体系中的所有子类的属性全部合并到一张表里,用一个类型字段区分某一行到底属于哪个子类。优点是访问简单,查父类数据不用连接;缺点是表中会出现大量空列,浪费存储,列膨胀以后不好维护。适合继承层次浅、子类差异小的场景,比如员工分成正式和兼职。

第二种是类表继承。父类单独建一张表存放公共属性,每个子类单独建表存放特有属性,并通过外键与父表关联。查询一个完整子类对象时必须做连接操作。优点是没有空列,结构干净,维护性好;缺点是查询成本上升,对每个子类的读写都至少涉及一次连接。适合子类差异大、各自独有字段多的情况,比如一个产品体系下不同品类有完全不同的规格。

第三种是实体表继承。完全不单独给父类建表,每个子类自成一张完整表,父类的公共属性在每个子类表里重复存放。好处是读写最高效,数据都集中在一个地方;坏处是父类级别的查询会变得很麻烦,需要扫多张子类表再做合并,修改公共属性时要同步多张表。适合子类数量少、公共属性也偏少、且很少有父类级别整体查询的场景。

这三种策略没有绝对的好坏,结合实际业务我是这样判断的:如果对公共属性的变更频繁,优先考虑类表继承;如果查询性能压力极大且子类之间几乎独立,优先考虑实体表继承;如果只是系统里的小分支,单表继承就够了。

5.3 关联关系落表:一对一、一对多、多对多怎么建外键

对象模型里的关联关系,落到关系数据库后,表达方式取决于关联的基数。

一对一关系表现为两个对象互相唯一引用,比如用户和用户档案。实现方式可以把两张表的主键设为相同值来关联,也可以在任意一方建外键引用另一方的唯一列,再给外键加唯一约束。前者更直观,后者更灵活。

一对多关系是最常见的一类,比如一个客户多个订单。实现方式是“在多的一方加外键”。订单表加一列客户标识,引用客户表的主键。这条规则既反直觉又很重要:很多人下意识觉得“一个客户对应多个订单,那客户表里应该存订单列表”,这正是对象模型思维在作祟。

多对多关系,比如学生和课程之间的关系,需要引入第三张关联表。关联表里只存两个外键,分别指向学生表和课程表,再配合唯一约束保证两个学生不会重复选修同一门课。查询时通过连接两张表,就能拿到某个学生的全部课程或某门课程的全部学生。

这里值得强调一条原则:关系模型里,关系的方向永远是用外键的存放位置来表达的,而不是用列表字段来表达。这是我带项目时反复纠正新人的一个点。

5.4 一个完整的映射案例:订单系统的模型落地过程

把上面这些规则串起来,我用一个典型的订单系统给你演示一次完整的建模流程。

假设业务对象有四个:用户、订单、订单项、商品。在对象模型里,用户与订单是一对多,订单与订单项是一对多,订单项与商品是多对一。

按映射规则操作:用户表、订单表、订单项表、商品表,四张表分别对应四个类。订单表加一列用户标识,作为外键指向用户表;订单项表加一列订单标识,作为外键指向订单表;订单项表还加一列商品标识,作为外键指向商品表。

这样建完后,查询“某个用户的所有订单”是写一条连接用户表和订单表的查询;查询“订单总额”是对订单金额字段做汇总。整个过程用关系模型的标准操作非常顺手,没有任何歧义。

这个案例也展示了对象模型和关系模型的协作方式:我们首先用对象模型来梳理业务领域,确定有哪些实体、实体之间是什么关系,然后用关系模型来完成物理落地,通过外键和连接来表达对象引用。两者不是二选一的竞争关系,而是表达层与存储层的分工关系。

6. 常见问题与排查心得:模型设计中最容易踩的坑

6.1 坑一:把对象模型思维直接套在建表上

有一个非常典型的坏习惯,是直接用定义对象的方式定义表,结果设计出来的表语义混乱。

比如在对象里有商品列表属性,有人就直接在客户表里加一个商品列表字段,希望存一个数组。但在关系模型里,这是反模式。列表对应的是另一个实体的一组行,应该在对应的子表中建立外键,而不是在主表中塞一个字段。

类似的问题还有在字段里存 JSON 来规避表拆分。这种做法偶尔可以,比如存放偶尔变化的可选信息,但当成主策略用,最终会付出惨痛代价:无法对内部字段做高效查询、无法利用外键约束、统计复杂得离谱。

我的经验是,对象模型负责“理解业务”,关系模型负责“存储数据”,两者之间的翻译,请交给映射规则,而不是让表结构去模仿对象的形状。

6.2 坑二:设计表的时候完全不考虑业务对象的边界

反过来也有坑。有些团队过度强调表设计要遵循范式理论,设计出来的表在概念上非常漂亮,但完全脱离了业务对象的使用习惯。

举例来说,一个商品的基本信息、库存信息、销售属性信息被拆成了四五张表,每张表都满足高范式要求。结果业务上每次展示商品详情,都要连接五张表,代价高且查询慢。

在这个问题上,我的建议是先梳理核心业务流,识别出哪些对象会被频繁一起访问,然后再决定表要不要合并。对于强关联、高并发访问的对象组,适当冗余字段是合理的。范式是工具,不是教条,它的目的是消除异常,不是制造更多连接。

6.3 坑三:主键设计随意,导致关联脆弱且迁移困难

主键是关系模型的基石,但很多项目在这上面特别随意。

使用自增整数做主键完全可行,也是大量系统的默认选择,但它的问题是:自增依赖数据库状态,数据迁移、分库分表时会带来不少麻烦。如果是跨系统合并数据,自增主键很容易产生冲突。

如果业务数据本身有稳定的唯一标识,比如订单号本身就是全局唯一的业务编号,那用它做主键完全没问题。唯一要注意的是主键列本身不要参与业务变更,否则未来会有人试图去改它,那后果就严重了。

对于需要大规模分布部署或者频繁合并数据的系统,使用全局唯一标识做主键,确实能省掉很多同步问题。哪怕我觉得自带排序能力的全局唯一标识方案也有点绕,但至少它让后续的分片和迁移轻松得多。

注意:任何方案都没必要迷信。选主键之前先问三个问题:这个系统的数据会跨库迁移吗?会横向拆分吗?有没有跨系统合并数据的可能?确定性回答之后再选,心里踏实。

6.4 关于模型选择与项目实践的真实体会

做到这一步,我想聊点更面向实际的东西。很多人纠结于“对象模型是不是更好”或者“关系模型是不是过时”,但在我接触过的系统里,真正决定项目命运的从来不是模型本身的先进程度,而是数据规模、团队能力、运维水平这几个因素的综合结果。

选关系模型并不意味着不能面向对象编程。今天的 Java、Python、Go 语言生态里,几乎都有成熟的 ORM 框架,本身就是把对象模型和关系模型连接起来的桥梁。用好这套桥接机制,代码侧保持对象思维,存储侧保持表思维,基本能满足绝大多数项目的需求。

需要刻意避开的是在关系模型里硬凹对象化表达,以及在对象模型里硬塞集合操作。前者会造成查询地狱,后者会让业务逻辑散落成碎片。想清楚每种模型擅长什么,剩下的就是把它们放在合适的位置上。

最后送一条非常实用的建议:模型是否合理的检验标准,永远不是文档里的框图好不好看,而是能否用一条相对清晰的思路回答“数据从写入到读取的完整路径是怎么走的”。如果这条路径里充满了特殊处理和临时方案,那大概率是模型设计需要回头把关了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询