4. 存储模型对比:表结构 vs 键值对 vs 类型化对象

存储模型是数据持久化的核心骨架。Room 与 DataStore 在“如何组织数据”这一底层设计上截然不同:Room 采用传统关系型数据库的表-行-列结构,DataStore 则基于键值对存储,而两者之上都支持通过编译时生成的类型化对象提供类型安全访问。理解这三种组织方式的本质差异,是选择正确工具的前提。

4.1 三种存储模型的本质

存储模型核心概念数据形状查询能力典型代表
表结构(Relational)表、行、列、主键、外键二维网格,行间通过外键关联SQL 过滤、排序、聚合、多表 JOINRoom(基于 SQLite)
键值对(Key-Value)键(唯一)、值(任意类型)扁平的字典或 MAP仅支持按键精确查询DataStore Preferences
类型化对象(Typed Object)实体类、属性、类型映射强类型编程对象,可嵌套通过键提取,且自带编译期检查DataStore Proto / Room DAO 返回值
关键区分:“类型化对象”并非独立的存储引擎,而是封装层。Room 通过 @Entity 注解将 Kotlin 类映射到表行;DataStore Proto 通过 Protocol Buffers 的 .proto 文件定义结构化消息。两者都让开发者以面向对象的方式操作数据,但底层存储机制完全不同。

4.2 原理图解:三种模型的结构对比

存储模型对比:表结构 vs 键值对 vs 类型化对象 存储模型对比:表结构 · 键值对 · 类型化对象 表结构 (Room) User 表 1 | Alice | alice@... 2 | Bob | bob@... 3 | Carol | carol@... 外键 → Order.userId SQL 查询 SELECT * FROM User WHERE id = ? JOIN Order ON ... 键值对 (Preferences) 扁平字典 theme → "dark" is_onboarding_done → true last_sync_time → 2025-04-07 font_size → 16 仅键查询 getBoolean("is_onboarding") putString("theme", "light") 类型化对象 (Proto) .proto 消息 message UserPreferences { string theme = 1; bool onboarding = 2; int32 font_size = 3; } 编译期类型安全 dataStore.updateData { it.toBuilder() .setTheme("dark") .build() } 类型化对象可以覆盖表结构和键值对:Room 返回 User 对象,DataStore Proto 返回自定义 Message 对象。

4.3 生活类比:三种数据组织形式

  • 表结构 → 图书馆书架:每个书架(表)有固定的分类和编号,图书(行)按索引整齐排列,可以通过查询系统(SQL)按作者、主题、出版年份等任意维度快速检索,甚至跨书架联合查询。
  • 键值对 → 个人储物柜:每个柜子只有一个编号(键)和一个格子(值)。你只能通过编号打开柜子,无法按身高、体重搜索柜子主人。适合存放单个设置项,比如“主题颜色”“最后登录时间”。
  • 类型化对象 → 智能快递柜的包裹:包裹本身是一个结构化的对象(内含物品、收件人、时效等字段),但柜子仍然通过唯一订单号(键)存取。开发者拿到包裹后可以直接读取结构化属性,不必自己拆包解析。

4.4 常见误区

误区 1:“DataStore 只能存键值对,Room 只能存表”
实际上:DataStore Preferences 确实是扁平键值对,但 DataStore Proto 通过 Protocol Buffers 支持嵌套结构、枚举、列表等复杂类型,本质上是一种类型化对象存储。而 Room 底层虽然是表,但 DAO 返回的 List<User> 也是类型化对象。两者在“类型化对象”这一抽象层次上殊途同归。
误区 2:“键值对存储比表结构更快,所以 DataStore 比 Room 快”
性能取决于使用场景。键值对因为无模式、无索引,写入更快,但复杂查询性能几乎为零。表结构在有合适索引时,范围查询、聚合运算远快于人工遍历所有键。不能单凭“键值对轻量”就断定整体性能。
误区 3:“类型化对象等于 ORM”
Room 的实体映射是 ORM(对象关系映射),将对象映射到关系表。DataStore Proto 的 .proto 消息并不是 ORM,它直接序列化为二进制文件,没有关系运算能力。类型化只是对数据的结构化描述,而非对查询能力的扩展。

4.5 应用边界:何时选择哪种模型

数据特征推荐模型典型场景
多实体、多关联、需要 SQL 查询表结构 (Room)通讯录、订单管理、笔记应用
简单、扁平的配置项,无查询需求键值对 (DataStore Preferences)主题切换、用户引导标志、音量设置
结构化配置(嵌套、枚举、列表),需类型安全类型化对象 (DataStore Proto)用户偏好 schema、应用配置文件
混合:少量关联 + 频繁写入偏好Room 主数据 + DataStore 配置电商 App 订单 + 购物车设置

4.6 思考练习题

  1. 你正在开发一个健身 App,需要记录用户的每日运动数据(运动类型、时长、消耗卡路里)并支持按日期范围统计总卡路里。你会选择哪种存储模型?为什么?
  2. 写一个简单的“用户登录状态”存储:仅需保存一个布尔值表示是否已登录。用 DataStore Preferences 和 DataStore Proto 各需要几行代码?哪个更合适?
  3. 如果需要存储一组“商品收藏列表”(商品 ID 集合),且希望在不反序列化整个集合的情况下判断某个 ID 是否已收藏,应该用 Room 表结构还是 DataStore 类型化对象?请给出理由。

本章要点:表结构适用于关系数据与复杂查询;键值对适用于简单配置项;类型化对象提供编译期安全,但底层存储方式决定了查询能力边界。选择存储模型不是选择“最好的”,而是选择“最适配数据特征与操作模式的”。