Android Room多表关联疑问:三/四表关系实现正确性验证
Android Room 多对多关联实体类修正方案
你当前的代码存在几个关键问题,导致无法正确实现阅读计划和书籍的关联,以下是修正后的代码及说明:
1. 修正后的实体类代码
// 阅读计划表 @Entity(tableName = "book_reading_schedule") data class BookReadingSchedule( @PrimaryKey(autoGenerate = true) // 建议自增主键,避免手动管理ID @ColumnInfo(name = "schedule_id") val id: Long = 0, val name: String = "" // 阅读计划名称 ) // 书籍表 @Entity(tableName = "books") data class Book( // 类名用单数更符合命名规范 @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "book_id") val id: Long = 0, val title: String = "" // 书籍标题 ) // 关联中间表:阅读计划 ↔ 书籍 @Entity( tableName = "book_schedule_join", foreignKeys = [ // 关联阅读计划表 ForeignKey( entity = BookReadingSchedule::class, parentColumns = ["schedule_id"], // 对应父表的主键列名 childColumns = ["schedule_id"], // 对应中间表的关联字段 onDelete = ForeignKey.CASCADE, // 删除计划时自动删除关联记录 onUpdate = ForeignKey.CASCADE ), // 关联书籍表 ForeignKey( entity = Book::class, parentColumns = ["book_id"], childColumns = ["book_id"], onDelete = ForeignKey.CASCADE, // 删除书籍时自动删除关联记录 onUpdate = ForeignKey.CASCADE ) ], // 复合主键:确保一个计划和一本书只能关联一次 primaryKeys = ["schedule_id", "book_id"] ) data class BookScheduleJoin( @ColumnInfo(name = "schedule_id", index = true) val scheduleId: Long, @ColumnInfo(name = "book_id", index = true) val bookId: Long // 中间表无需额外字段,除非要记录关联附加信息(比如阅读顺序) )
2. 关键修正点说明
- 命名规范优化:实体类用单数(如
Book而非Books),表名用下划线分隔,贴合SQL命名习惯。 - 外键配置修复:原代码外键的
parentColumns写错(应为父表主键schedule_id),且缺失了关联书籍表的外键配置。 - 中间表设计优化:用复合主键替代自增ID,避免同一计划重复关联同一本书;移除冗余的
title字段(书籍标题已在Book表存储)。 - 主键自增设置:添加
autoGenerate = true让Room自动生成主键,减少手动管理ID的麻烦。
3. 关联查询实现(类比LINQ Join)
如果要查询某个阅读计划下的所有书籍,可以创建包含关联数据的POJO类,用Room的@Relation注解实现关联查询:
// 包含阅读计划及对应书籍的POJO data class ScheduleWithBooks( @Embedded // 嵌入阅读计划实体 val schedule: BookReadingSchedule, @Relation( parentColumn = "schedule_id", // 父表(计划)的主键 entityColumn = "book_id", // 子表(书籍)的主键 associateBy = Junction( value = BookScheduleJoin::class, // 中间表实体 parentColumn = "schedule_id", // 中间表关联计划的字段 entityColumn = "book_id" // 中间表关联书籍的字段 ) ) val books: List<Book> )
然后在Dao中定义查询方法:
@Dao interface ScheduleDao { @Transaction // 关联查询需要事务保证原子性 @Query("SELECT * FROM book_reading_schedule WHERE schedule_id = :scheduleId") suspend fun getScheduleWithBooks(scheduleId: Long): ScheduleWithBooks? }
这个操作类似C# LINQ中的Join,通过中间表把两个主表关联起来,一次性获取关联数据。
内容的提问来源于stack exchange,提问作者Alix Blaine
相关产品推荐
相关产品推荐

