Room框架1-N关联查询未按ID过滤时返回结果异常
问题原因与解决方案
问题根源
你的异常查询出现重复Friend的核心原因是SQL逻辑与Room的@Relation注解工作机制不匹配:
Room处理FriendMeetings这类包含@Embedded和@Relation的类时,会分两步执行:
- 执行你定义的
@Query语句,获取父实体(Friend)的记录 - 针对每个父实体的ID,自动执行子查询获取对应的子实体(Meeting)列表
但你当前的异常查询使用了INNER JOIN meetings,导致第一步返回的是Friend与Meeting的笛卡尔积——比如一个Friend有3条符合日期的Meeting,SQL会返回3条「相同Friend+不同Meeting」的记录。Room拿到这些重复的Friend记录后,会将每条都视为独立的父实体,最终生成多个重复的FriendMeetings对象(每个对象仅包含一条Meeting),而非一个FriendMeetings对应多条Meeting。
解决方案
以下两种方案无需逐个加载朋友,直接通过Room的机制解决重复问题:
方案1:修改SQL返回唯一Friend,配合@Relation过滤Meeting
- 调整DAO查询,仅返回符合条件的唯一Friend记录:
@Query("SELECT DISTINCT friends.* FROM friends INNER JOIN meetings ON friends.id = meetings.friendId WHERE meetings.date BETWEEN :startOfDay AND :endOfDay") fun getMeetingsBetweenDates(startOfDay: Long, endOfDay: Long): List<FriendMeetings>?
- 修改
FriendMeetings的@Relation,添加日期过滤条件,确保仅返回指定范围内的Meeting:
data class FriendMeetings( @Embedded val friend: Friend = Friend(), @Relation( parentColumn = "id", entityColumn = "friendId", query = "SELECT * FROM meetings WHERE date BETWEEN :startOfDay AND :endOfDay" ) val meetings: List<Meeting> )
Room会自动将DAO方法的参数传递给@Relation的查询,既保证Friend唯一,又过滤出符合日期的Meeting。
方案2:用子查询定位目标Friend,配合事务与@Relation
这种方式逻辑更清晰,先通过子查询找到所有有符合日期Meeting的Friend ID,再获取对应Friend:
- DAO方法添加
@Transaction注解并修改查询:
@Transaction @Query("SELECT * FROM friends WHERE id IN (SELECT DISTINCT friendId FROM meetings WHERE date BETWEEN :startOfDay AND :endOfDay)") fun getMeetingsBetweenDates(startOfDay: Long, endOfDay: Long): List<FriendMeetings>?
- 同样给
@Relation添加日期过滤(同方案1的FriendMeetings修改)。
为什么其他查询正常?
getAll():直接查询friends表,每个Friend唯一,Room自动关联所有Meeting,结果无重复。getMeetingsWithFriendBetweenDates():仅查询单个Friend,即使JOIN返回多条记录,Room会根据父实体ID合并为一个FriendMeetings对象,不会产生重复。
内容的提问来源于stack exchange,提问作者cobolero
相关产品推荐
相关产品推荐

