Room数据库多对多关联:同ID插入Cast数据被覆盖问题求助
问题原因及修正方案
核心问题分析
- CastDbModel主键逻辑错误:你将电影ID作为
CastDbModel的主键,导致同一电影下的所有演员条目共用同一个主键值。插入时OnConflictStrategy.REPLACE会直接覆盖之前的条目,最终仅保留最后一条数据。 - 关联实体定义错位:
MovieWithListOfCast中@Embedded的应该是MovieDbModel(电影作为父实体),而非CastDbModel,否则整个关联关系逻辑完全颠倒。 - 查询逻辑无效:直接查询
cast表并过滤电影ID没有意义,因为cast表的id是演员主键,和电影ID无直接关联,必须通过交叉表movie_cast建立关联查询。 - 插入流程不完整:仅插入
CastDbModel未建立电影与演员的关联关系,需要同时插入交叉表数据才能维护多对多映射。
具体修正步骤
1. 修正CastDbModel实体
将CastDbModel的主键改为演员自身的唯一ID(比如接口返回的演员专属id),确保每个演员条目都是独立的:
@Entity(tableName = "cast") data class CastDbModel( @PrimaryKey(autoGenerate = false) val castId: Int, // 改为演员自身的唯一ID val name: String, val profile_path: String?, val character: String ) // 可删除原独立的Cast类,将字段合并到CastDbModel中,结构更清晰
2. 修正MovieWithListOfCast关联类
将父实体改为MovieDbModel,并修正关联字段的映射关系:
data class MovieWithListOfCast( @Embedded val movie: MovieDbModel, // 父实体为电影 @Relation( entity = CastDbModel::class, parentColumn = "id", // 电影表的主键id entityColumn = "castId", // 演员表的主键castId associateBy = Junction( value = MovieCastCrossRef::class, parentColumn = "movieIdMap", // 交叉表关联电影的字段 entityColumn = "castIdMap" // 交叉表关联演员的字段 ) ) val castList: List<CastDbModel> )
3. 修正交叉表MovieCastCrossRef
确保交叉表字段与实体主键对应:
@Entity( tableName = "movie_cast", primaryKeys = ["movieIdMap","castIdMap"], foreignKeys = [ ForeignKey( entity = MovieDbModel::class, parentColumns = ["id"], childColumns = ["movieIdMap"], onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE ), ForeignKey( entity = CastDbModel::class, parentColumns = ["castId"], // 对应演员表的主键castId childColumns = ["castIdMap"], onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.CASCADE ) ] ) data class MovieCastCrossRef( val movieIdMap: Int, // 关联的电影ID @ColumnInfo(index = true) val castIdMap: Int // 关联的演员ID )
4. 修正DAO层的插入与查询方法
- 插入时需要同时处理电影、演员、交叉表数据,确保关联关系建立:
// 插入电影 @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertMovie(movie: MovieDbModel) // 插入演员 @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertCasts(casts: List<CastDbModel>) // 插入交叉表关联数据 @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertMovieCastCrossRef(crossRefs: List<MovieCastCrossRef>) // 封装完整插入流程 suspend fun insertMovieWithCasts(movie: MovieDbModel, casts: List<CastDbModel>) { insertMovie(movie) insertCasts(casts) val crossRefs = casts.map { MovieCastCrossRef(movie.id, it.castId) } insertMovieCastCrossRef(crossRefs) }
- 查询时通过关联类获取电影及其对应的演员列表:
@Transaction @Query("SELECT * FROM movie WHERE id = :movieId") fun getMovieWithCasts(movieId: Int): MovieWithListOfCast?
5. 优化插入冲突策略(可选)
如果不需要覆盖已存在的演员数据,可将insertCasts的冲突策略改为OnConflictStrategy.IGNORE,避免误删已有条目:
@Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insertCasts(casts: List<CastDbModel>)
内容的提问来源于stack exchange,提问作者Alex20280
相关产品推荐
相关产品推荐

