You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Kotlin Room的表结构与@Relation设计问卷功能 有无简化方案?

方案建议

首先明确:你给UI层提供单嵌套对象的思路是完全合理的

嵌套RecyclerView本身就需要层级匹配的嵌套数据结构,这个设计不是导致方案复杂的原因,复杂的根源是你过度遵循关系型数据库范式做了表拆分,移动端本地存储不需要严格对齐后端的库表设计,适当做冗余或者结构化存储可以大幅降低复杂度。

两种更简单的实现方案,按需选择

方案1:单Question表冗余字段(适合需要本地单独修改问题/分组/分类属性的场景)

不需要做任何关联表,仅保留2张核心表即可:

  • 主表Questionnaire:存储问卷的基础属性(问卷id、名称、生效时间等)
  • 子表Question:存储每个问题的所有属性,额外冗余category_id、category_name、group_id、group_name4个字段,直接和所属分类、分组绑定。

查询组装嵌套结构的逻辑也非常简单:

  1. 用DISTINCT查询当前问卷下所有去重的分类列表
  2. 按分类id查询对应下去重的分组列表
  3. 按分组id查询对应下的所有问题
  4. 手动组装成你需要的三层嵌套对象即可,全程都是单表查询,性能高,排查问题也方便。

如果不想手动组装,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 04:36:06