4. 存储模型对比:表结构 vs 键值对 vs 类型化对象
存储模型是数据持久化的核心骨架。Room 与 DataStore 在“如何组织数据”这一底层设计上截然不同:Room 采用传统关系型数据库的表-行-列结构,DataStore 则基于键值对存储,而两者之上都支持通过编译时生成的类型化对象提供类型安全访问。理解这三种组织方式的本质差异,是选择正确工具的前提。
4.1 三种存储模型的本质
| 存储模型 | 核心概念 | 数据形状 | 查询能力 | 典型代表 |
|---|---|---|---|---|
| 表结构(Relational) | 表、行、列、主键、外键 | 二维网格,行间通过外键关联 | SQL 过滤、排序、聚合、多表 JOIN | Room(基于 SQLite) |
| 键值对(Key-Value) | 键(唯一)、值(任意类型) | 扁平的字典或 MAP | 仅支持按键精确查询 | DataStore Preferences |
| 类型化对象(Typed Object) | 实体类、属性、类型映射 | 强类型编程对象,可嵌套 | 通过键提取,且自带编译期检查 | DataStore Proto / Room DAO 返回值 |
关键区分:“类型化对象”并非独立的存储引擎,而是封装层。Room 通过
@Entity 注解将 Kotlin 类映射到表行;DataStore Proto 通过 Protocol Buffers 的 .proto 文件定义结构化消息。两者都让开发者以面向对象的方式操作数据,但底层存储机制完全不同。
4.2 原理图解:三种模型的结构对比
4.3 生活类比:三种数据组织形式
- 表结构 → 图书馆书架:每个书架(表)有固定的分类和编号,图书(行)按索引整齐排列,可以通过查询系统(SQL)按作者、主题、出版年份等任意维度快速检索,甚至跨书架联合查询。
- 键值对 → 个人储物柜:每个柜子只有一个编号(键)和一个格子(值)。你只能通过编号打开柜子,无法按身高、体重搜索柜子主人。适合存放单个设置项,比如“主题颜色”“最后登录时间”。
- 类型化对象 → 智能快递柜的包裹:包裹本身是一个结构化的对象(内含物品、收件人、时效等字段),但柜子仍然通过唯一订单号(键)存取。开发者拿到包裹后可以直接读取结构化属性,不必自己拆包解析。
4.4 常见误区
误区 1:“DataStore 只能存键值对,Room 只能存表”
实际上:DataStore Preferences 确实是扁平键值对,但 DataStore Proto 通过 Protocol Buffers 支持嵌套结构、枚举、列表等复杂类型,本质上是一种类型化对象存储。而 Room 底层虽然是表,但 DAO 返回的 List<User> 也是类型化对象。两者在“类型化对象”这一抽象层次上殊途同归。
实际上:DataStore Preferences 确实是扁平键值对,但 DataStore Proto 通过 Protocol Buffers 支持嵌套结构、枚举、列表等复杂类型,本质上是一种类型化对象存储。而 Room 底层虽然是表,但 DAO 返回的 List<User> 也是类型化对象。两者在“类型化对象”这一抽象层次上殊途同归。
误区 2:“键值对存储比表结构更快,所以 DataStore 比 Room 快”
性能取决于使用场景。键值对因为无模式、无索引,写入更快,但复杂查询性能几乎为零。表结构在有合适索引时,范围查询、聚合运算远快于人工遍历所有键。不能单凭“键值对轻量”就断定整体性能。
性能取决于使用场景。键值对因为无模式、无索引,写入更快,但复杂查询性能几乎为零。表结构在有合适索引时,范围查询、聚合运算远快于人工遍历所有键。不能单凭“键值对轻量”就断定整体性能。
误区 3:“类型化对象等于 ORM”
Room 的实体映射是 ORM(对象关系映射),将对象映射到关系表。DataStore Proto 的
Room 的实体映射是 ORM(对象关系映射),将对象映射到关系表。DataStore Proto 的
.proto 消息并不是 ORM,它直接序列化为二进制文件,没有关系运算能力。类型化只是对数据的结构化描述,而非对查询能力的扩展。
4.5 应用边界:何时选择哪种模型
| 数据特征 | 推荐模型 | 典型场景 |
|---|---|---|
| 多实体、多关联、需要 SQL 查询 | 表结构 (Room) | 通讯录、订单管理、笔记应用 |
| 简单、扁平的配置项,无查询需求 | 键值对 (DataStore Preferences) | 主题切换、用户引导标志、音量设置 |
| 结构化配置(嵌套、枚举、列表),需类型安全 | 类型化对象 (DataStore Proto) | 用户偏好 schema、应用配置文件 |
| 混合:少量关联 + 频繁写入偏好 | Room 主数据 + DataStore 配置 | 电商 App 订单 + 购物车设置 |
4.6 思考练习题
- 你正在开发一个健身 App,需要记录用户的每日运动数据(运动类型、时长、消耗卡路里)并支持按日期范围统计总卡路里。你会选择哪种存储模型?为什么?
- 写一个简单的“用户登录状态”存储:仅需保存一个布尔值表示是否已登录。用 DataStore Preferences 和 DataStore Proto 各需要几行代码?哪个更合适?
- 如果需要存储一组“商品收藏列表”(商品 ID 集合),且希望在不反序列化整个集合的情况下判断某个 ID 是否已收藏,应该用 Room 表结构还是 DataStore 类型化对象?请给出理由。
本章要点:表结构适用于关系数据与复杂查询;键值对适用于简单配置项;类型化对象提供编译期安全,但底层存储方式决定了查询能力边界。选择存储模型不是选择“最好的”,而是选择“最适配数据特征与操作模式的”。