基于Kotlin Room的表结构与@Relation设计问卷功能 有无简化方案?
方案建议
首先明确:你给UI层提供单嵌套对象的思路是完全合理的
嵌套RecyclerView本身就需要层级匹配的嵌套数据结构,这个设计不是导致方案复杂的原因,复杂的根源是你过度遵循关系型数据库范式做了表拆分,移动端本地存储不需要严格对齐后端的库表设计,适当做冗余或者结构化存储可以大幅降低复杂度。
两种更简单的实现方案,按需选择
方案1:单Question表冗余字段(适合需要本地单独修改问题/分组/分类属性的场景)
不需要做任何关联表,仅保留2张核心表即可:
- 主表
Questionnaire:存储问卷的基础属性(问卷id、名称、生效时间等) - 子表
Question:存储每个问题的所有属性,额外冗余category_id、category_name、group_id、group_name4个字段,直接和所属分类、分组绑定。
查询组装嵌套结构的逻辑也非常简单:
- 用
DISTINCT查询当前问卷下所有去重的分类列表 - 按分类id查询对应下去重的分组列表
- 按分组id查询对应下的所有问题
- 手动组装成你需要的三层嵌套对象即可,全程都是单表查询,性能高,排查问题也方便。
如果不想手动组装,Room 2.4及以上版本支持多级@Relation注解,你可以直接定义最终的嵌套POJO,不需要中间的关联表映射:
data class GroupWithQuestions( @Embedded val group: Group, @Relation( parentColumn = "group_id", entityColumn = "group_id", entity = Question::class ) val questions: List<Question> ) data class CategoryWithGroups( @Embedded val category: Category, @Relation( parentColumn = "category_id", entityColumn = "category_id" ) val groups: List<GroupWithQuestions> ) data class QuestionnaireFull( @Embedded val questionnaire: Questionnaire, @Relation( parentColumn = "questionnaire_id", entityColumn = "questionnaire_id" ) val categories: List<CategoryWithGroups> )
方案2:JSON序列化存储(适合本地仅做缓存、不需要单独修改单条数据的场景)
如果你的问卷数据都是从后端拉取,本地修改也是整份提交,不需要单独修改某个问题/分组的属性,直接用TypeConverter存整份结构化数据是最简单的方案:
- 只需要一张
Questionnaire表,新增一个content字段,类型为String - 写Room TypeConverter,实现你需要的三层嵌套对象和JSON字符串的互转
- 存数据的时候直接把后端返回的嵌套结构序列化后存进去,读的时候直接反序列化,一行
SELECT * FROM Questionnaire WHERE id = :id就能拿到完整的嵌套对象,完全不需要做任何关联查询,代码量不到原来的20%,维护成本极低。
原方案的问题
你原来的设计是严格按照关系型数据库第三范式做的表拆分,适合后端需要大量复用问题、分组的场景,但移动端本地存储的核心诉求是简单、高性能、易维护,适当冗余或者非结构化存储反而更适配。
内容的提问来源于stack exchange,提问作者Cameron
相关产品推荐
相关产品推荐

