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

Room数据库多对多关联:同ID插入Cast数据被覆盖问题求助

问题原因及修正方案

核心问题分析

  1. CastDbModel主键逻辑错误:你将电影ID作为CastDbModel的主键,导致同一电影下的所有演员条目共用同一个主键值。插入时OnConflictStrategy.REPLACE会直接覆盖之前的条目,最终仅保留最后一条数据。
  2. 关联实体定义错位:MovieWithListOfCast中@Embedded的应该是MovieDbModel(电影作为父实体),而非CastDbModel,否则整个关联关系逻辑完全颠倒。
  3. 查询逻辑无效:直接查询cast表并过滤电影ID没有意义,因为cast表的id是演员主键,和电影ID无直接关联,必须通过交叉表movie_cast建立关联查询。
  4. 插入流程不完整:仅插入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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 05:05:48