房间与条件的关系
Room relation with conditions
我有一个对象类别
@Entity
data class Category(
@PrimaryKey
val id: String,
val name: String,
val is_free: Boolean
)
和对象部分
@Entity
data class Section(
@PrimaryKey
val name: String,
val category_ids: List<String>
)
类别对象的示例数据如下
[
{
"id": "quotes",
"name": "General",
"is_free": true
},
{
"id": "favorites",
"name": "Favorites",
"is_free": true
},
{
"id": "positivity",
"name": "Positive Thinking",
"is_free": false
},
{
"id": "love",
"name": "Love",
"is_free": false
}
]
这是 Section 对象的样本数据
[
{
"name": "Categories",
"category_ids": [
"quotes",
"favorites"
]
},
{
"name": "Most Popular",
"category_ids": [
"positivity",
"love"
]
}
]
现在我需要创建新对象作为 SectionWithCategory,方法是将 Section 对象的 category_ids 替换为相关的 Category 对象,
data class SectionWithCategories(
val name: String,
val categories: List<Category>
)
我的问题是如何创建 SectionWithCategories 对象以获得以下结果?
[
{
"name": "Categories",
"categories": [
{
"id": "quotes",
"name": "General",
"is_free": true
},
{
"id": "favorites",
"name": "Favorites",
"is_free": true
}
]
},
{
"name": "Most Popular",
"categories": [
{
"id": "positivity",
"name": "Positive Thinking",
"is_free": false
},
{
"id": "love",
"name": "Love",
"is_free": false
}
]
}
]
My problem is how can I create SectionWithCategories object to get following result?
有一些困难,因为假设您有适当的类型转换器,存储不利于关系的数据,从而提取相关数据。
您必须提取数据,然后从正在存储的 JOSN 字符串构建对象。
如果您没有类型转换器,那么您的部分实体将无法编译,因为您不能使用具有列表类型的列。
我建议,因为您似乎在“部分”和“类别”之间存在多对多关系,所以您采用第三个 table 来保持关系 (并消除需要有类型转换器).
- 这也消除了将对象存储为 JSON
所带来的膨胀
因此,不要在实体部分中使用 val category_ids: List<String>
,例如:-
@Entity
data class Section(
@PrimaryKey
val id: String,
val name: String,
//val category_ids: List<String> use mapping/associatetive table instead
)
- 已注释掉但可能应该删除。
您删除此行并拥有第三个实体,该实体存储部分的 ID 和允许任意组合的类别的相应 ID。以下是添加了建议选项(例如外键定义)的完整版本:-
@Entity(
primaryKeys = ["sectionMap","categoryMap"] /* MUST have a primary key */
/* Foreign Keys are optional, BUT enforce referential integrity */
/* A Foreign Key is a rule that says that a child must have a parent */
/* If the rule is broken then a Foreign Key conflict results */
, foreignKeys = [
/* Defining the Section's id as the parent and the SectionMap as the child */
ForeignKey(
entity = Section::class,
parentColumns = ["id"],
childColumns = ["sectionMap"],
/* Optional within a Foreign Key are the following two Actions that can
onDelete - action to be taken if the parent is deleted
CASCADE will delete the rows that are a child of the parent
onUpdate - action taken if the value of the parent's mapped column changes
CASCADE will update the rows that are a child of the parent
*/
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
/* Defining the Category's id as the parent and the CategoryMap as the child */
ForeignKey(
entity = Category::class,
parentColumns = ["id"],
childColumns = ["categoryMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class SectionCategoryMap(
val sectionMap: String,
@ColumnInfo(index = true) /* Room warns if not indexed */
val categoryMap: String
)
- 添加了一些注释,作为评论,应该阅读并可能进一步调查。
现在您的 SectionWithCategories 可能是:-
data class SectionWithCategories(
@Embedded
val section: Section,
@Relation(
entity = Category::class, parentColumn = "id", entityColumn = "id",
associateBy = Junction(
value = SectionCategoryMap::class, /* The mapping/associative table Entity */
parentColumn = "sectionMap", /* The column that maps to the parent (Section) */
entityColumn = "categoryMap" /* The column that maps to the children (Categories) */
)
)
val categories: List<Category>
)
演示
与@Dao class (AllDao) 如:-
@Dao
abstract class AllDao {
@Insert(onConflict = IGNORE)
abstract fun insert(category: Category): Long
@Insert(onConflict = IGNORE)
abstract fun insert(section: Section): Long
@Insert(onConflict = IGNORE)
abstract fun insert(sectionCategoryMap: SectionCategoryMap): Long
@Transaction
@Query("SELECT * FROM section")
abstract fun getAllSectionWithCategories(): List<SectionWithCategories>
}
- onConflict IGNORE 已针对插入进行编码,以便忽略重复项而不会发生异常,因此可以重新运行以下代码
- Long 将是 rowid(通常隐藏的列),如果插入被忽略,它将是 -1。
假设使用了一个非常标准的@Database class(除了使用 allowMainThreadQueries,为了演示的简洁和方便),它是:-
@Database(
entities = [
Category::class,
Section::class,
SectionCategoryMap::class
],
version = DBVERSION
)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
companion object {
const val DBVERSION = 1
const val DBNAME = "my.db"
@Volatile
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase::class.java,
DBNAME
)
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
然后在 activity 以下 :-
db = TheDatabase.getInstance(this)
dao = db.getAllDao();
dao.insert(Category("quotes","General",true))
dao.insert(Category("favorites","Favorites",true))
dao.insert(Category("positivity","Positive Thinking", false))
dao.insert(Category("love","Love",false))
dao.insert(Section("Section1","Section1"))
dao.insert(Section("Section2","Section2"))
dao.insert(Section("Bonus","Bonus Section"))
dao.insert(SectionCategoryMap("Section1","quotes"))
dao.insert(SectionCategoryMap("Section1","favorites"))
dao.insert(SectionCategoryMap("Section2","positivity"))
dao.insert(SectionCategoryMap("Section2","love"))
/* Extra Data */
dao.insert(SectionCategoryMap("Bonus","love"))
dao.insert(SectionCategoryMap("Bonus","favorites"))
dao.insert(SectionCategoryMap("Bonus","positivity"))
dao.insert(SectionCategoryMap("Bonus","quotes"))
dao.insert(Section("Empty","Section with no Categories"))
val TAG = "DBINFO"
for(swc: SectionWithCategories in dao.getAllSectionWithCategories()) {
Log.d(TAG,"Section is ${swc.section.name} ID is ${swc.section.id} categories are:-")
for (c: Category in swc.categories) {
Log.d(TAG,"\tCategory is ${c.name} ID is ${c.id} IS FREE is ${c.is_free}")
}
}
- 根据您的数据插入类别
- 插入部分,根据您的数据插入两个部分,另外两个部分
- 插入反映您的数据的 SectionCategoryMap 行以及两个新部分的附加映射(奖励部分映射到所有类别,空白部分映射到无类别)。
- 提取所有具有相应类别的部分作为 SectionWithCategories 对象并将结果写入日志。
输出到日志的结果为:-
2021-10-03 09:05:04.250 D/DBINFO: Section is Section1 ID is Section1 categories are:-
2021-10-03 09:05:04.250 D/DBINFO: Category is Favorites ID is favorites IS FREE is true
2021-10-03 09:05:04.251 D/DBINFO: Category is General ID is quotes IS FREE is true
2021-10-03 09:05:04.251 D/DBINFO: Section is Section2 ID is Section2 categories are:-
2021-10-03 09:05:04.251 D/DBINFO: Category is Love ID is love IS FREE is false
2021-10-03 09:05:04.251 D/DBINFO: Category is Positive Thinking ID is positivity IS FREE is false
2021-10-03 09:05:04.251 D/DBINFO: Section is Bonus Section ID is Bonus categories are:-
2021-10-03 09:05:04.251 D/DBINFO: Category is Favorites ID is favorites IS FREE is true
2021-10-03 09:05:04.251 D/DBINFO: Category is Love ID is love IS FREE is false
2021-10-03 09:05:04.251 D/DBINFO: Category is Positive Thinking ID is positivity IS FREE is false
2021-10-03 09:05:04.251 D/DBINFO: Category is General ID is quotes IS FREE is true
2021-10-03 09:05:04.251 D/DBINFO: Section is Section with no Categories ID is Empty categories are:-
如果你真的想要数据为 JSON 那么你可以使用 :-
for(swc: SectionWithCategories in dao.getAllSectionWithCategories()) {
Log.d(TAG, " SECTION JSON = ${Gson().toJson(swc.section)} CATEGROIES JSON = ${Gson().toJson(swc.categories)}")
}
在这种情况下,输出将是:-
2021-10-03 09:24:34.954 D/DBINFO: SECTION JSON = {"id":"Section1","name":"Section1"} CATEGROIES JSON = [{"id":"favorites","is_free":true,"name":"Favorites"},{"id":"quotes","is_free":true,"name":"General"}]
2021-10-03 09:24:34.956 D/DBINFO: SECTION JSON = {"id":"Section2","name":"Section2"} CATEGROIES JSON = [{"id":"love","is_free":false,"name":"Love"},{"id":"positivity","is_free":false,"name":"Positive Thinking"}]
2021-10-03 09:24:34.960 D/DBINFO: SECTION JSON = {"id":"Bonus","name":"Bonus Section"} CATEGROIES JSON = [{"id":"favorites","is_free":true,"name":"Favorites"},{"id":"love","is_free":false,"name":"Love"},{"id":"positivity","is_free":false,"name":"Positive Thinking"},{"id":"quotes","is_free":true,"name":"General"}]
2021-10-03 09:24:34.962 D/DBINFO: SECTION JSON = {"id":"Empty","name":"Section with no Categories"} CATEGROIES JSON = []
我有一个对象类别
@Entity
data class Category(
@PrimaryKey
val id: String,
val name: String,
val is_free: Boolean
)
和对象部分
@Entity
data class Section(
@PrimaryKey
val name: String,
val category_ids: List<String>
)
类别对象的示例数据如下
[
{
"id": "quotes",
"name": "General",
"is_free": true
},
{
"id": "favorites",
"name": "Favorites",
"is_free": true
},
{
"id": "positivity",
"name": "Positive Thinking",
"is_free": false
},
{
"id": "love",
"name": "Love",
"is_free": false
}
]
这是 Section 对象的样本数据
[
{
"name": "Categories",
"category_ids": [
"quotes",
"favorites"
]
},
{
"name": "Most Popular",
"category_ids": [
"positivity",
"love"
]
}
]
现在我需要创建新对象作为 SectionWithCategory,方法是将 Section 对象的 category_ids 替换为相关的 Category 对象,
data class SectionWithCategories(
val name: String,
val categories: List<Category>
)
我的问题是如何创建 SectionWithCategories 对象以获得以下结果?
[
{
"name": "Categories",
"categories": [
{
"id": "quotes",
"name": "General",
"is_free": true
},
{
"id": "favorites",
"name": "Favorites",
"is_free": true
}
]
},
{
"name": "Most Popular",
"categories": [
{
"id": "positivity",
"name": "Positive Thinking",
"is_free": false
},
{
"id": "love",
"name": "Love",
"is_free": false
}
]
}
]
My problem is how can I create SectionWithCategories object to get following result?
有一些困难,因为假设您有适当的类型转换器,存储不利于关系的数据,从而提取相关数据。
您必须提取数据,然后从正在存储的 JOSN 字符串构建对象。
如果您没有类型转换器,那么您的部分实体将无法编译,因为您不能使用具有列表类型的列。
我建议,因为您似乎在“部分”和“类别”之间存在多对多关系,所以您采用第三个 table 来保持关系 (并消除需要有类型转换器).
- 这也消除了将对象存储为 JSON 所带来的膨胀
因此,不要在实体部分中使用 val category_ids: List<String>
,例如:-
@Entity
data class Section(
@PrimaryKey
val id: String,
val name: String,
//val category_ids: List<String> use mapping/associatetive table instead
)
- 已注释掉但可能应该删除。
您删除此行并拥有第三个实体,该实体存储部分的 ID 和允许任意组合的类别的相应 ID。以下是添加了建议选项(例如外键定义)的完整版本:-
@Entity(
primaryKeys = ["sectionMap","categoryMap"] /* MUST have a primary key */
/* Foreign Keys are optional, BUT enforce referential integrity */
/* A Foreign Key is a rule that says that a child must have a parent */
/* If the rule is broken then a Foreign Key conflict results */
, foreignKeys = [
/* Defining the Section's id as the parent and the SectionMap as the child */
ForeignKey(
entity = Section::class,
parentColumns = ["id"],
childColumns = ["sectionMap"],
/* Optional within a Foreign Key are the following two Actions that can
onDelete - action to be taken if the parent is deleted
CASCADE will delete the rows that are a child of the parent
onUpdate - action taken if the value of the parent's mapped column changes
CASCADE will update the rows that are a child of the parent
*/
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
/* Defining the Category's id as the parent and the CategoryMap as the child */
ForeignKey(
entity = Category::class,
parentColumns = ["id"],
childColumns = ["categoryMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class SectionCategoryMap(
val sectionMap: String,
@ColumnInfo(index = true) /* Room warns if not indexed */
val categoryMap: String
)
- 添加了一些注释,作为评论,应该阅读并可能进一步调查。
现在您的 SectionWithCategories 可能是:-
data class SectionWithCategories(
@Embedded
val section: Section,
@Relation(
entity = Category::class, parentColumn = "id", entityColumn = "id",
associateBy = Junction(
value = SectionCategoryMap::class, /* The mapping/associative table Entity */
parentColumn = "sectionMap", /* The column that maps to the parent (Section) */
entityColumn = "categoryMap" /* The column that maps to the children (Categories) */
)
)
val categories: List<Category>
)
演示
与@Dao class (AllDao) 如:-
@Dao
abstract class AllDao {
@Insert(onConflict = IGNORE)
abstract fun insert(category: Category): Long
@Insert(onConflict = IGNORE)
abstract fun insert(section: Section): Long
@Insert(onConflict = IGNORE)
abstract fun insert(sectionCategoryMap: SectionCategoryMap): Long
@Transaction
@Query("SELECT * FROM section")
abstract fun getAllSectionWithCategories(): List<SectionWithCategories>
}
- onConflict IGNORE 已针对插入进行编码,以便忽略重复项而不会发生异常,因此可以重新运行以下代码
- Long 将是 rowid(通常隐藏的列),如果插入被忽略,它将是 -1。
假设使用了一个非常标准的@Database class(除了使用 allowMainThreadQueries,为了演示的简洁和方便),它是:-
@Database(
entities = [
Category::class,
Section::class,
SectionCategoryMap::class
],
version = DBVERSION
)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
companion object {
const val DBVERSION = 1
const val DBNAME = "my.db"
@Volatile
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase::class.java,
DBNAME
)
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
然后在 activity 以下 :-
db = TheDatabase.getInstance(this)
dao = db.getAllDao();
dao.insert(Category("quotes","General",true))
dao.insert(Category("favorites","Favorites",true))
dao.insert(Category("positivity","Positive Thinking", false))
dao.insert(Category("love","Love",false))
dao.insert(Section("Section1","Section1"))
dao.insert(Section("Section2","Section2"))
dao.insert(Section("Bonus","Bonus Section"))
dao.insert(SectionCategoryMap("Section1","quotes"))
dao.insert(SectionCategoryMap("Section1","favorites"))
dao.insert(SectionCategoryMap("Section2","positivity"))
dao.insert(SectionCategoryMap("Section2","love"))
/* Extra Data */
dao.insert(SectionCategoryMap("Bonus","love"))
dao.insert(SectionCategoryMap("Bonus","favorites"))
dao.insert(SectionCategoryMap("Bonus","positivity"))
dao.insert(SectionCategoryMap("Bonus","quotes"))
dao.insert(Section("Empty","Section with no Categories"))
val TAG = "DBINFO"
for(swc: SectionWithCategories in dao.getAllSectionWithCategories()) {
Log.d(TAG,"Section is ${swc.section.name} ID is ${swc.section.id} categories are:-")
for (c: Category in swc.categories) {
Log.d(TAG,"\tCategory is ${c.name} ID is ${c.id} IS FREE is ${c.is_free}")
}
}
- 根据您的数据插入类别
- 插入部分,根据您的数据插入两个部分,另外两个部分
- 插入反映您的数据的 SectionCategoryMap 行以及两个新部分的附加映射(奖励部分映射到所有类别,空白部分映射到无类别)。
- 提取所有具有相应类别的部分作为 SectionWithCategories 对象并将结果写入日志。
输出到日志的结果为:-
2021-10-03 09:05:04.250 D/DBINFO: Section is Section1 ID is Section1 categories are:-
2021-10-03 09:05:04.250 D/DBINFO: Category is Favorites ID is favorites IS FREE is true
2021-10-03 09:05:04.251 D/DBINFO: Category is General ID is quotes IS FREE is true
2021-10-03 09:05:04.251 D/DBINFO: Section is Section2 ID is Section2 categories are:-
2021-10-03 09:05:04.251 D/DBINFO: Category is Love ID is love IS FREE is false
2021-10-03 09:05:04.251 D/DBINFO: Category is Positive Thinking ID is positivity IS FREE is false
2021-10-03 09:05:04.251 D/DBINFO: Section is Bonus Section ID is Bonus categories are:-
2021-10-03 09:05:04.251 D/DBINFO: Category is Favorites ID is favorites IS FREE is true
2021-10-03 09:05:04.251 D/DBINFO: Category is Love ID is love IS FREE is false
2021-10-03 09:05:04.251 D/DBINFO: Category is Positive Thinking ID is positivity IS FREE is false
2021-10-03 09:05:04.251 D/DBINFO: Category is General ID is quotes IS FREE is true
2021-10-03 09:05:04.251 D/DBINFO: Section is Section with no Categories ID is Empty categories are:-
如果你真的想要数据为 JSON 那么你可以使用 :-
for(swc: SectionWithCategories in dao.getAllSectionWithCategories()) {
Log.d(TAG, " SECTION JSON = ${Gson().toJson(swc.section)} CATEGROIES JSON = ${Gson().toJson(swc.categories)}")
}
在这种情况下,输出将是:-
2021-10-03 09:24:34.954 D/DBINFO: SECTION JSON = {"id":"Section1","name":"Section1"} CATEGROIES JSON = [{"id":"favorites","is_free":true,"name":"Favorites"},{"id":"quotes","is_free":true,"name":"General"}]
2021-10-03 09:24:34.956 D/DBINFO: SECTION JSON = {"id":"Section2","name":"Section2"} CATEGROIES JSON = [{"id":"love","is_free":false,"name":"Love"},{"id":"positivity","is_free":false,"name":"Positive Thinking"}]
2021-10-03 09:24:34.960 D/DBINFO: SECTION JSON = {"id":"Bonus","name":"Bonus Section"} CATEGROIES JSON = [{"id":"favorites","is_free":true,"name":"Favorites"},{"id":"love","is_free":false,"name":"Love"},{"id":"positivity","is_free":false,"name":"Positive Thinking"},{"id":"quotes","is_free":true,"name":"General"}]
2021-10-03 09:24:34.962 D/DBINFO: SECTION JSON = {"id":"Empty","name":"Section with no Categories"} CATEGROIES JSON = []