如何使用Room实现组合模式(多级嵌套列表的存储与查询)
Room 多级树形嵌套查询解决方案
你目前的实现只能查询一级层级,核心原因是关联类ContentTaskEntity里的子节点列表类型是ContentEntity,本身没有携带子列表字段,只要调整类型定义即可实现自动嵌套查询,也可以用递归CTE的方案手动组装适配大数据量场景。
方案1:直接调整关联类实现自动多级查询
将ContentTaskEntity里的contentList类型从List<ContentEntity>改为List<ContentTaskEntity>即可,Room会自动递归处理每一层的关联关系,支持无限层级的树形结构查询:
data class ContentTaskEntity( @Embedded val content: ContentEntity, @Relation( parentColumn = "id", entityColumn = "parentId", entity = ContentEntity::class ) val contentList: List<ContentTaskEntity> // 仅修改这一行的类型 )
原有Dao层的getAllContentTask()方法不需要任何修改,直接调用就能返回完整的多级嵌套结构。
该方案优势是代码改动极小,不需要手动处理数据组装,适合层级不超过3层、单表数据量小于1000条的场景。
方案2:递归CTE+手动组装适配大数据量场景
如果数据层级深、数据量大,Room自动递归查询会产生多次数据库IO,性能更低,可以用SQLite的递归CTE先一次性查询出所有关联节点,再手动组装成树形结构:
第一步:新增递归查询Dao方法
@Query(""" WITH RECURSIVE content_tree(id, name, type, parentId) AS ( -- 查根节点 SELECT id, name, type, parentId FROM content WHERE parentId = :rootParentId UNION ALL -- 递归查所有子节点 SELECT c.id, c.name, c.type, c.parentId FROM content c INNER JOIN content_tree ct ON c.parentId = ct.id ) SELECT * FROM content_tree """) fun getWholeContentTree(rootParentId: Int = -1): List<ContentEntity>
第二步:手动组装树形结构
fun assembleContentTree(flatNodeList: List<ContentEntity>): List<ContentTaskEntity> { // 存储id对应节点的映射 val nodeMap = flatNodeList.associate { it.id to ContentTaskEntity(content = it, contentList = mutableListOf()) } val rootNodes = mutableListOf<ContentTaskEntity>() flatNodeList.forEach { node -> if (node.parentId == -1) { rootNodes.add(nodeMap[node.id]!!) } else { (nodeMap[node.parentId]?.contentList as? MutableList<ContentTaskEntity>)?.add(nodeMap[node.id]!!) } } return rootNodes }
调用方式
@Transaction fun getCompleteContentTree(): List<ContentTaskEntity> { val flatList = getWholeContentTree() return assembleContentTree(flatList) }
该方案仅需要一次数据库IO,性能远高于自动递归查询,适合层级深、数据量大的场景,也支持自定义组装规则。
内容的提问来源于stack exchange,提问作者user2494863
相关产品推荐
相关产品推荐

