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

如何在DAO中高效定义嵌套关系的删除函数?

在Room DAO中实现嵌套关系的批量删除

方法一:利用Room外键级联删除(推荐)

这是最简洁高效的方式,让数据库自动处理关联数据的删除,不需要手动编写多步删除逻辑。

步骤1:修改实体类,添加外键与级联规则

给Exercise和TrainingSet实体添加外键约束,并设置onDelete = CASCADE,这样当父实体被删除时,关联的子实体也会自动被删除。

修改后的实体代码:

@Entity(
    tableName = "workouts"
)
data class Workout(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo("workout_id")
    val workoutId: Long = 0,
    val name: String,
    @ColumnInfo(name = "set_count")
    val setsCount: Int,
)

@Entity(
    tableName = "exercises",
    foreignKeys = [
        ForeignKey(
            entity = Workout::class,
            parentColumns = ["workout_id"],
            childColumns = ["workout_id"],
            onDelete = ForeignKey.CASCADE // 删除Workout时自动删除关联的Exercise
        )
    ]
)
data class Exercise(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo("exercise_id")
    val exerciseId: Long = 0,
    @ColumnInfo("workout_id")
    val workoutId: Long,
    @ColumnInfo("exercise_name")
    val exerciseName: String
)

@Entity(
    tableName = "training_sets",
    foreignKeys = [
        ForeignKey(
            entity = Exercise::class,
            parentColumns = ["exercise_id"],
            childColumns = ["exercise_id"],
            onDelete = ForeignKey.CASCADE // 删除Exercise时自动删除关联的TrainingSet
        )
    ]
)
data class TrainingSet (
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo("training_set_id")
    val trainingSetId: Long = 0,
    @ColumnInfo("exercise_id")
    val exerciseId: Long,
    val weight: Int,
    val repetitions: Int
)

// 嵌套关系类保持不变
data class ExerciseWithTrainingSets(
    @Embedded
    val exercise: Exercise,
    @Relation(
        parentColumn = "exercise_id",
        entityColumn = "exercise_id"
    )
    val trainingSets: List<TrainingSet>
)

data class WorkoutWithExercisesAndSets(
    @Embedded
    val workout: Workout,
    @Relation(
        entity = Exercise::class,
        parentColumn = "workout_id",
        entityColumn = "workout_id"
    )
    val exercisesWithTrainingSets: List<ExerciseWithTrainingSets>
)

步骤2:在DAO中定义单个删除方法

只需要删除目标Workout,数据库会自动级联删除所有关联的Exercise和TrainingSet:

@Dao
interface WorkoutDao {
    // 删除指定Workout,级联删除关联的Exercise和TrainingSet
    @Delete
    suspend fun deleteWorkout(workout: Workout)

    // 或者通过ID删除
    @Query("DELETE FROM workouts WHERE workout_id = :workoutId")
    suspend fun deleteWorkoutById(workoutId: Long)
}

方法二:手动事务删除(适合无法修改实体的场景)

如果不能修改实体添加外键,可以通过Room的@Transaction注解,手动按顺序删除关联数据,确保操作的原子性(要么全部成功,要么全部回滚)。

在DAO中定义事务方法:

@Dao
interface WorkoutDao {
    // 手动事务删除:先删最底层的TrainingSet,再删Exercise,最后删Workout
    @Transaction
    suspend fun deleteWorkoutWithRelations(workoutId: Long) {
        // 1. 删除该Workout下所有Exercise对应的TrainingSet
        deleteTrainingSetsByWorkoutId(workoutId)
        // 2. 删除该Workout下的所有Exercise
        deleteExercisesByWorkoutId(workoutId)
        // 3. 删除目标Workout
        deleteWorkoutById(workoutId)
    }

    // 辅助方法:通过WorkoutID删除关联的TrainingSet
    @Query("DELETE FROM training_sets WHERE exercise_id IN (SELECT exercise_id FROM exercises WHERE workout_id = :workoutId)")
    suspend fun deleteTrainingSetsByWorkoutId(workoutId: Long)

    // 辅助方法:通过WorkoutID删除关联的Exercise
    @Query("DELETE FROM exercises WHERE workout_id = :workoutId")
    suspend fun deleteExercisesByWorkoutId(workoutId: Long)

    // 辅助方法:通过ID删除Workout
    @Query("DELETE FROM workouts WHERE workout_id = :workoutId")
    suspend fun deleteWorkoutById(workoutId: Long)
}

两种方法对比

  • 级联删除:代码简洁,数据库层面自动处理,性能更优,推荐优先使用。需要注意添加外键后,数据库结构会有约束,确保数据一致性。
  • 手动事务删除:无需修改实体,但需要手动编写多步删除逻辑,适合已有项目无法修改实体的场景,同样能保证操作的原子性。

内容的提问来源于stack exchange,提问作者Giuseppe D'arrò

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:20:16