many-many 一个扩展的 1-M,允许每一边与另一边的任意数量相关。因此,一个帐户可以关联许多其他帐户可以关联的电影类别。典型的解决方案是有一个包含 2 个核心列的中间表。一个存储独特价值的人ID确定关系的一侧和存储唯一值的另一侧ID实体化另一方。通常这 2 列用于主键。
- 这样的中间表有很多术语来描述这样的表,比如关联表,映射表,引用表......
- 注意如何ID已突出显示。只需创建一个名为ID在表中(实体)不建立关系,它只支持建立关系的可能性。
您的问题似乎会勾选多对多关系的复选框,因此会勾选额外的表(如果是帐户安全类别则为 2)。
该表将有一列用于唯一标识 accountData 行的值(总账户数据).
- 因为 totalAccountData 是主键(即用@PrimaryKey 注释)并且 PrimaryKey 是隐式唯一的
该表将有第二列用于 movieCategory 的ID柱子。
所以你可以开始
@Entity
data class AccountMovieMap(
val accountDataMap: String,
val movieCategoryMap: String
)
但是,没有哪个房间需要 PrimaryKey,但 @PrimaryKey 注释仅适用于单个列。如果使用其中任何一个,那么由于隐含的唯一性,关系将被限制为一对多。需要一个复合(多列/值)主键,它根据组合值形成唯一性。要在 Room 中指定复合主键,使用 @Entity 注释的 primaryKeys 参数。
所以 AccountMovieMap 变成:-
@Entity(
primaryKeys = ["accountDataMap","movieCategoryMap"]
)
data class AccountMovieMap(
val accountDataMap: String,
val movieCategoryMap: String
)
就目前而言,上述内容存在潜在问题,因为可以将数据插入到一个或两个列中,而这些列不是相应表中的值。也就是说,在这种情况下,关系的完整性是不存在的。
SQLite 和 Room(与许多关系数据库一样)适合强制执行参照完整性. SQLite 通过 ForeignKey 子句来做到这一点。 Room 使用@Entity 注释的foreignKeys 参数来提供ForeignKeys 的列表。
-
除了强制引用完整性外,SQlite 有 2 个子句 ON DELETE 和 ON UPDATE 有助于维护引用完整性(取决于指定的操作,最有用的是 CASCADE,它允许通过将对父项的更改应用到子项来破坏引用完整性的更改).
-
如果索引在它认为应该存在的地方不存在,Room 也会发出警告,例如warning: movieCategoryMap column references a foreign key but it is not part of an index. This may trigger full table scans whenever parent table is modified so you are highly advised to create an index that covers this column. 因此,@ColumnInfo 注释可用于在 movieCategoryMap 列上添加索引。
所以账户电影地图可能更完整:-
@Entity(
primaryKeys = ["accountDataMap","movieCategoryMap"]
, foreignKeys = [
ForeignKey(
entity = AccountData::class,
parentColumns = ["totalAccountData"],
childColumns = ["accountDataMap"],
/* Optional but helps to maintain Referential Integrity */
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = MovieCategory::class,
parentColumns = ["id"],
childColumns = ["movieCategoryMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class AccountMovieMap(
val accountDataMap: String,
@ColumnInfo(index = true)
val movieCategoryMap: String
)
要添加(插入)行,您可以拥有/使用(在 @Dao 注释类中):-
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(accountMovieMap: AccountMovieMap)
- 请注意,为了避免引用完整性冲突,引用/映射的帐户数据和引用/映射的电影类别需要存在。
正如您想要提取 AccountData 的 MovieCategories 一样,您需要一个 POJO,其中包含带有 MovieCategory 列表的 AccountData。
这可能是:-
data class AccountWithMovieCategoryList(
@Embedded
val accountData: AccountData,
@Relation(
entity = MovieCategory::class,
parentColumn = "totalAccountData", /* The column referenced in the @Embedded */
entityColumn = "id", /* The column referenced in the @Relation (MovieCategory) */
/* The mapping table */
associateBy = (
Junction(
value = AccountMovieMap::class, /* The @Entity annotated class for the mapping table */
parentColumn = "accountDataMap", /* the column in the mapping table that references the @Embedded */
entityColumn = "movieCategoryMap" /* the column in the mapping table that references the @Relation */
)
)
)
val movieCategoryList: List<MovieCategory>
)
以下可能是 @Dao 注释接口中的函数,该接口检索给定帐户的 AccountWithMovieCategoryList:-
@Transaction
@Query("SELECT * FROM accountData WHERE totalAccountData=:totalAccountData")
fun getAnAccountWithMovieCategoryList(totalAccountData: String): List<AccountWithMovieCategoryList>
然而Room 将检索所有 MovieCategories,但您希望能够指定一个限制为帐户编辑了 MovieCategories 的数量,因此需要一种方法来覆盖 Room 获取所有映射/关联对象的方法。
为了促进这一点,可以使用一个带有主体的函数来 a) 获取相应的 AccountData 和 b) 然后通过指定了 LIMIT 的映射表根据帐户获取 MovieCategory 列表。因此 2 个 @Query 函数执行 2 被总体函数调用。
所以要获取 AccountData:-
@Query("SELECT * FROM accountData WHERE totalAccountData=:totalAccountData")
fun getSingleAccount(totalAccountData: String): AccountData
然后通过(加入)映射表获取 AccountData 的有限 MovieCategories :-
@Query("SELECT movieCategory.* FROM accountMovieMap JOIN movieCategory ON accountMovieMap.MovieCategoryMap = movieCategory.id WHERE accountMovieMap.accountDataMap=:totalAccountData LIMIT :limit")
fun getLimitedMovieCategoriesForAnAccount(totalAccountData: String,limit: Int): List<MovieCategory>
把它们放在一起,也就是总体功能:-
@Transaction
@Query("")
fun getAccountWithLimitedMovieCategoryList(totalAccountData: String,categoryLimit: Int): AccountWithMovieCategoryList {
return AccountWithMovieCategoryList(
getSingleAccount(totalAccountData),
getLimitedMovieCategoriesForAnAccount(totalAccountData,categoryLimit)
)
}