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

Room @Relation注解实现带中间表的一对多关系映射方法

结论

不需要手动编写中间绑定逻辑,Room原生支持通过中间表的关联关系自动映射嵌套列表,你之前的代码失效是因为@Relation注解没有配置中间表关联规则。

修正步骤

1. 调整Chore类的@Relation注解配置

你之前的注解默认会直接关联Chore和UserEntity表,不会识别ChoreToUser中间表,需要通过Junction参数指定关联的中间表规则,同时配置投影列匹配自定义User POJO的字段:

data class Chore(
    var name: String,
    @Relation(
        parentColumn = "id", // Chore实体在自身表中的主键列名
        entityColumn = "id", // User实体在自身表中的主键列名
        entity = UserEntity::class,
        // 指定中间表关联规则
        associateBy = Junction(
            value = ChoreToUser::class,
            parentColumn = "choreId", // 中间表中存储Chore主键的列名
            entityColumn = "userId" // 中间表中存储User主键的列名
        ),
        // 指定从UserEntity中查询的列,和自定义User POJO的字段对应
        projection = ["name", "imageRes"]
    )
    var contributingUsers: List<User>,
    @DrawableRes var drawableRes: Int,
    var done: Boolean
)

2. 匹配User POJO的列名映射

因为你在SQL查询中给UserEntity的字段取了别名,需要给User类加上字段和列名的对应关系,避免Room映射失败:

data class User(
    @ColumnInfo(name = "userName")
    val userName: String,
    @DrawableRes
    @ColumnInfo(name = "userPhoto")
    val userPhoto: Int
)

3. 修正DAO层查询语句

使用@Relation时,Room会在查询到主实体(Chore)列表后,自动批量查询关联的User数据并完成映射,不需要你在主查询中手动JOIN User表和中间表。另外你原SQL用了COUNT聚合函数但没加分组,会导致返回结果异常,需要补充分组规则:

@Query("""
    SELECT Chores.id, Chores.name, Chores.drawableRes,
    (COUNT(ChoreLogs.id) = 1) as done 
    FROM Chores 
    LEFT JOIN ChoreToUsers ON ChoreToUsers.choreId = Chores.id 
    LEFT JOIN ChoreLogs ON ChoreLogs.choreToUserId = ChoreToUsers.id
    GROUP BY Chores.id
""")
fun getChoreTiles(): List<Chore>

注意:主查询必须返回Chore表的主键id字段,否则Room无法获取关联依据,会导致列表映射失败。

额外说明
  • 该实现方式不会产生N+1查询问题,Room会先批量查询所有Chore数据,再根据拿到的所有Chore主键一次性查询关联的User数据,性能和手动写绑定逻辑无差异。
  • 如果需要对关联的User做过滤(比如只返回canFulfill为true的用户),可以在@Relation注解中添加where参数编写SQL过滤条件即可。

内容的提问来源于stack exchange,提问作者Marc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 20:51:06