Android – 房间内的实体关系
本文介绍如何在 Room 持久化库中定义实体之间的关系。因为 SQLite 是一种关系数据库,所以实体可以相互关联。 Room 中的实体不能直接引用其他实体,因为这样做可能会导致每次加载不必要的数据。即便如此,您有时可能希望从您的实体中引用其他实体。这可以通过多种方式实现。
嵌入的对象
@Embedded注解可用于表示要分解为表(实体)中子字段的对象。然后可以以与其他单个列相同的方式查询嵌入的字段。
Kotlin
data class GeeksforGeeks(
val course: String?,
val courseName: String?,
val city: String?,
val coursePrice: Int
)
@Entity
data class User(
@PrimaryKey val id: Int,
val firstName: String?,
@Embedded val GeeksforGeeks: GeeksforGeeks?
)
Kotlin
@Entity
data class GfgCourseName(
@PrimaryKey val gfgLoginNumber: Long,
val name: String,
val class: Int
)
@Entity(foreignKeys = @ForeignKey(entity = GfgCourseName.class,
parentColumns = "gfgLoginNumber",
childColumns = "courseActiveID",
onDelete = CASCADE))
data class GfgCourse(
@PrimaryKey val gfgCourseId: Long,
val title: String,
val courseActiveID: Long
)
data class GfgCourseNameAndGfgCourse(
@Embedded val gfgCourseName: GfgCourseName,
@Relation(
parentColumn = "gfgLoginNumber",
entityColumn = "courseActiveID"
)
val gfgCourse: GfgCourse
)
Kotlin
@Entity
data class GfgCourseName(
@PrimaryKey val gfgCourseNameId: Long,
val name: String,
val age: Int
)
@Entity(foreignKeys = @ForeignKey(entity = GfgCourseName.class,
parentColumns = "gfgCourseNameId",
childColumns = "gfgCourseNameCreatorId",
onDelete = CASCADE))
data class Playlist(
@PrimaryKey val courseId: Long,
val gfgCourseNameCreatorId: Long,
val playlistName: String
)
data class GfgCourseNameWithPlaylists(
@Embedded val gfgCourseName: GfgCourseName,
@Relation(
parentColumn = "gfgCourseNameId",
entityColumn = "gfgCourseNameCreatorId"
)
val playlists: List
)
Kotlin
@Entity
data class CourseVideo(
@PrimaryKey val id: Long,
val courseVideoName: String
)
@Entity
data class CourseNameSignture(
@PrimaryKey val id: Long,
val courseNameSigntureName: String,
val artist: String
)
@Entity(primaryKeys = ["courseVideoId", "courseNameSigntureId"],
foreignKeys = {
@ForeignKey(entity = CourseVideo.class,
parentColumns = "id",
childColumns = "courseVideoId"),
@ForeignKey(entity = CourseNameSignture.class,
parentColumns = "id",
childColumns = "courseNameSigntureId")
}))
data class CourseVideoCourseNameSigntureCrossRef(
val courseVideoId: Long,
val courseNameSigntureId: Long
)
Kotlin
data class CourseNameSignature(
@Embedded val videoPlaylist: VideoPlaylist,
@Relation(
parentColumn = "videoPlaylistId",
entityColumn = "courseID",
associateBy = @Junction(VideoPlaylistSongCrossRef::class)
)
val songs: List
)
data class CourseNamePlaylist(
@Embedded val song: Song,
@Relation(
parentColumn = "courseID",
entityColumn = "videoPlaylistId",
associateBy = @Junction(VideoPlaylistSongCrossRef::class)
)
val videoPlaylists: List
)
Kotlin
@Transaction
@Query("SELECT * FROM Playlist")
fun getPlaylistCourse(): List
@Transaction
@Query("SELECT * FROM CourseName")
fun getCourseNamesWithPlaylists(): List
GeekTip#1: If an entity has multiple embedded fields of the same type, you can use the prefix property to make each column unique. The provided value is then appended to the beginning of each column name in the embedded object.
在前面的示例中,对象的字段被分解为一个实体。 @Embedded 注解不能用于表示多个实体之间的关系。您可以使用@Relation 注释或@Entity 注释的外键参数定义两个实体之间的关系。它们的不同之处在于@Relation注解只能应用于非实体类,而 ForeignKey 注解只能应用于实体类。 ForeignKey 还对要求子列存在于父列中的实体的架构产生影响。 @Relation用于连接表而不影响它们的模式。
定义实体之间的关系有以下三种方式:
- 一对一互动
- 一对多或一对多关系 一对多关系
- 多对多连接
一对一互动
当父实体的每个实例恰好对应于子实体的一个实例时,就存在一对一的关系,反之亦然。考虑一个音乐流应用程序,其中用户拥有自己的歌曲库。每个用户都有一个库,每个库对应一个用户。
科特林
@Entity
data class GfgCourseName(
@PrimaryKey val gfgLoginNumber: Long,
val name: String,
val class: Int
)
@Entity(foreignKeys = @ForeignKey(entity = GfgCourseName.class,
parentColumns = "gfgLoginNumber",
childColumns = "courseActiveID",
onDelete = CASCADE))
data class GfgCourse(
@PrimaryKey val gfgCourseId: Long,
val title: String,
val courseActiveID: Long
)
data class GfgCourseNameAndGfgCourse(
@Embedded val gfgCourseName: GfgCourseName,
@Relation(
parentColumn = "gfgLoginNumber",
entityColumn = "courseActiveID"
)
val gfgCourse: GfgCourse
)
在前面的示例中,具有一对一关系的实体是 gfgCourseName 和 coursegfg。其中一个实体必须包含一个变量,该变量引用另一个实体的主键(gfgCourse 实体中的 userOwnerId)。要查询用户列表和对应的库,我们必须首先对两个实体的一对一关系进行建模,这是使用 UserAndLibrary 类完成的。 CourseandGfgCourse 类包含一个父实体实例 (gfgCourseName) 和一个子实体实例 (gfgCourse)。
然后,与父级一起,将@Relation 注释添加到子实体的实例中。列设置为父实体的名称和实体的主键列 列设置为引用父实体主键的子实体列的名称。
一对多的关系
一对多关系是父实体的每个实例对应零个或多个子实体的实例,但子实体的每个实例只能对应父实体的一个实例。在前面的 courseStructure 应用示例中,一个课程可以有多个 videoPlaylist。每门课程可以创建无限数量的播放列表,但每个播放列表仅由一门课程创建。
科特林
@Entity
data class GfgCourseName(
@PrimaryKey val gfgCourseNameId: Long,
val name: String,
val age: Int
)
@Entity(foreignKeys = @ForeignKey(entity = GfgCourseName.class,
parentColumns = "gfgCourseNameId",
childColumns = "gfgCourseNameCreatorId",
onDelete = CASCADE))
data class Playlist(
@PrimaryKey val courseId: Long,
val gfgCourseNameCreatorId: Long,
val playlistName: String
)
data class GfgCourseNameWithPlaylists(
@Embedded val gfgCourseName: GfgCourseName,
@Relation(
parentColumn = "gfgCourseNameId",
entityColumn = "gfgCourseNameCreatorId"
)
val playlists: List
)
可以看出,这种方法非常类似于一对一的关系;唯一的区别在于关系模型(UserWithPlaylists)。它现在有一个子实体列表,而不是单个子实体。查询数据库的过程也很相似。
多对多连接
当父实体的每个实例对应于子实体的零个或多个实例时,两个实体之间存在多对多关系,反之亦然。对于音乐流应用程序,每个播放列表可以包含许多歌曲,并且每首歌曲可以是多个播放列表的一部分。因此,播放列表实体和歌曲实体应该具有多对多的关系。
多对多关系不同于其他类型的关系,因为在子实体中没有对父实体的引用。相反,第三类用于表示两个实体的关联实体(或交叉引用表)。交叉引用表必须包含表的多对多关系中每个实体的主键列。
科特林
@Entity
data class CourseVideo(
@PrimaryKey val id: Long,
val courseVideoName: String
)
@Entity
data class CourseNameSignture(
@PrimaryKey val id: Long,
val courseNameSigntureName: String,
val artist: String
)
@Entity(primaryKeys = ["courseVideoId", "courseNameSigntureId"],
foreignKeys = {
@ForeignKey(entity = CourseVideo.class,
parentColumns = "id",
childColumns = "courseVideoId"),
@ForeignKey(entity = CourseNameSignture.class,
parentColumns = "id",
childColumns = "courseNameSigntureId")
}))
data class CourseVideoCourseNameSigntureCrossRef(
val courseVideoId: Long,
val courseNameSigntureId: Long
)
下一步取决于您要如何查询这些相关实体。创建一个新数据类,其中包含单个播放列表对象和播放列表包含的所有歌曲对象的列表(如果您要查询播放列表和每个播放列表的相应歌曲列表)。创建一个新的数据类,其中包含单个 Song 对象和包含该歌曲的所有 Playlist 对象的列表(如果您要查询歌曲)以及每个对应播放列表的列表。
在任何一种情况下,通过识别提供播放列表实体和歌曲实体之间关系的交叉引用实体来建模实体之间的关系,使用这些类中每个类的@Relation注释中的 associateBy 属性。
科特林
data class CourseNameSignature(
@Embedded val videoPlaylist: VideoPlaylist,
@Relation(
parentColumn = "videoPlaylistId",
entityColumn = "courseID",
associateBy = @Junction(VideoPlaylistSongCrossRef::class)
)
val songs: List
)
data class CourseNamePlaylist(
@Embedded val song: Song,
@Relation(
parentColumn = "courseID",
entityColumn = "videoPlaylistId",
associateBy = @Junction(VideoPlaylistSongCrossRef::class)
)
val videoPlaylists: List
)
数据库的查询与前面的方法类似。
科特林
@Transaction
@Query("SELECT * FROM Playlist")
fun getPlaylistCourse(): List
@Transaction
@Query("SELECT * FROM CourseName")
fun getCourseNamesWithPlaylists(): List