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

如何在Android中配置包含嵌套实体的Room数据库?

在Room中处理包含嵌套实体列表的配置

针对你提供的API响应结构(Teacher包含多个Student),Room提供两种常用解决方案,可根据业务需求选择:


方案一:使用TypeConverter(适合无需单独操作Student的场景)

如果不需要单独查询、更新Student数据,仅需将整个Teacher对象(包含学生列表)作为整体存储/读取,用TypeConverter将学生列表转为JSON字符串存储是最简单的方式。

1. 定义数据类

// Student数据类
data class Student(
    val contact: Long,
    val name: String
)

// Teacher实体类
@Entity(tableName = "teachers")
data class Teacher(
    @PrimaryKey val id: String,
    val teacherName: String,
    val subject: String,
    val status: String,
    // 学生列表,通过TypeConverter转成JSON存储
    val student_enrolled: List<Student>
)

2. 创建TypeConverter

class Converters {
    private val gson = Gson()

    @TypeConverter
    fun fromStudentList(students: List<Student>): String {
        return gson.toJson(students)
    }

    @TypeConverter
    fun toStudentList(studentJson: String): List<Student> {
        val type = object : TypeToken<List<Student>>() {}.type
        return gson.fromJson(studentJson, type)
    }
}

3. 配置Room数据库

@Database(entities = [Teacher::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun teacherDao(): TeacherDao
}

4. Dao接口

@Dao
interface TeacherDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertTeachers(teachers: List<Teacher>)

    @Query("SELECT * FROM teachers")
    suspend fun getAllTeachers(): List<Teacher>
}

方案二:使用一对多关系(适合需要单独操作Student的场景)

如果需要单独查询某个老师的学生、更新学生信息等,建议将Teacher和Student分别存为两张表,用Room的@Relation注解建立关联。

1. 定义实体类

// Teacher实体,主键为id
@Entity(tableName = "teachers")
data class Teacher(
    @PrimaryKey val id: String,
    val teacherName: String,
    val subject: String,
    val status: String
)

// Student实体,包含teacherId作为外键关联Teacher
@Entity(
    tableName = "students",
    foreignKeys = [ForeignKey(
        entity = Teacher::class,
        parentColumns = ["id"],
        childColumns = ["teacherId"],
        onDelete = ForeignKey.CASCADE
    )],
    indices = [Index(value = ["teacherId"])]
)
data class Student(
    @PrimaryKey(autoGenerate = true) val studentId: Long = 0,
    val contact: Long,
    val name: String,
    val teacherId: String // 关联Teacher的id
)

2. 创建关联数据类

// 用来封装Teacher和对应的学生列表
data class TeacherWithStudents(
    @Embedded val teacher: Teacher,
    @Relation(
        parentColumn = "id",
        entityColumn = "teacherId"
    )
    val students: List<Student>
)

3. 配置Room数据库

@Database(entities = [Teacher::class, Student::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun teacherDao(): TeacherDao
}

4. Dao接口(带事务查询)

@Dao
interface TeacherDao {
    // 插入Teacher和对应的Students,用事务保证原子性
    @Transaction
    suspend fun insertTeacherWithStudents(teacher: Teacher, students: List<Student>) {
        insertTeacher(teacher)
        insertStudents(students)
    }

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertTeacher(teacher: Teacher)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertStudents(students: List<Student>)

    // 查询所有老师及其学生列表
    @Transaction
    @Query("SELECT * FROM teachers")
    suspend fun getAllTeachersWithStudents(): List<TeacherWithStudents>

    // 查询指定老师的学生列表
    @Transaction
    @Query("SELECT * FROM teachers WHERE id = :teacherId")
    suspend fun getTeacherWithStudents(teacherId: String): TeacherWithStudents?
}

注意事项

  • 使用TypeConverter时,需引入Gson依赖(在build.gradle中添加implementation 'com.google.code.gson:gson:2.10.1')
  • 一对多关系中,外键的onDelete策略可根据业务需求调整,CASCADE表示删除Teacher时自动删除关联的Student
  • 所有数据库操作建议在协程中执行,避免阻塞主线程

内容的提问来源于stack exchange,提问作者Deepak Gupta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 03:53:01