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
相关产品推荐
相关产品推荐

