构建数据库表和@Relation 以从数据库实体创建问卷? (科特林,房间)
Structuring DB tables and @Relation to create a questionaire from db entities? (kotlin, Room)
在开始新项目之前,我正在寻求一些指导。
我的目标是根据问题的 Group 在提取 Question 数据的应用程序中生成 form/questionaire 和它的类别。其中每个类别包含许多组,每个组包含许多问题。
为了支持 UI 和嵌套回收器视图,目的是为视图模型提供包含嵌套列表的 单个对象 。即具有类别列表的对象,包含组列表,包含问题列表。
在设置房间、实体和 DAO 以及它们之间的关系方面,我的理解是实现此目标的最佳方法是:
- 创建问题实体(包含文本、选项等)
- 为问题和组之间的关系(多对多)创建引用table
- 创建parent/child table Groups和Categories的关系(一对多)
接下来应该使用一组基于关系的数据 classes 来结合每一对。
- GroupWithQuestions 数据class(使用@Relation 列出每个组中使用引用的问题table)
- CategoryWithGroupsWithQuestions 数据class(使用@Relation 列出每个类别中的组,使用parent/childtable)
- QuestionaireWithCategoriesWith...问题数据class(包含 CategoryWithGroupsWithQuestions 列表)
这很复杂,需要通过多个 table 来跟踪关系,因此很难更新并且解决错误也很耗时。我觉得我过度思考了这个方法(或者遗漏了一些东西)。
有simpler/smarter方法吗?
(单对象方法是问题的一部分吗?)
提前感谢您的建议和意见。
Create a Questions entity (conatining text,options..etc.)
Create a reference table for the relationship between Questions and Groups (many-to-many)
Create a parent/child table for the relationship between Groups and Categories (one-to-many)
一对多只需要在父项的子项中有一列。
This is complicated, relationships need to be followed through multiple tables, and thus will be hard to update and time consuming to resolve errors. I feel like Im over thinking the approach (or am missing something).
这并没有那么复杂:我相信以下内容与您所要求的非常接近:-
实体
类别:-
@Entity(
indices = [
Index(value = ["categoryName"],unique = true) /* Assume that a category name should be unique */
]
)
data class Category(
@PrimaryKey
val categoryId: Long? = null,
@ColumnInfo
val categoryName: String
)
组:-
@Entity(
foreignKeys = [
ForeignKey(
entity = Category::class,
parentColumns = ["categoryId"],
childColumns = ["categoryIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class Group(
@PrimaryKey
val groupId: Long? = null,
@ColumnInfo(index = true)
val categoryIdMap: Long,
val groupName: String
)
- 外键约束不是必需的,但它们有助于加强参照完整性。
- onDelete 和 onUpdate 不是必需的,但很有用
问题
@Entity(
)
data class Question(
@PrimaryKey
val questionId: Long? = null,
@ColumnInfo(index = true)
val questionText: String,
val questionOption: Int
)
QuestiongroupMap(可能是 GroupQuestionMap):-
@Entity(
primaryKeys = ["questionIdMap","groupIdMap"],
foreignKeys = [
ForeignKey(
entity = Question::class,
parentColumns = ["questionId"],
childColumns = ["questionIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE),
ForeignKey(
entity = Group::class,
parentColumns = ["groupId"],
childColumns = ["groupIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class QuestionGroupMap(
val questionIdMap: Long,
@ColumnInfo(index = true)
val groupIdMap: Long
)
POJO 的
GroupWithQuestions
data class GroupWithQuestions(
@Embedded
val group: Group,
@Relation(
entity = Question::class,
entityColumn = "questionId",
parentColumn = "groupId",
associateBy = Junction(
QuestionGroupMap::class,
parentColumn = "groupIdMap",
entityColumn = "questionIdMap"
)
)
val questionList: List<Question>
)
- 通过 QuestiongroupMap 以及关联和 Junction
CategoryWithGroupsWithQuestions
data class CategoryWithGroupWithQuestions(
@Embedded
val category: Category,
@Relation(entity = Group::class,entityColumn = "categoryIdMap",parentColumn = "categoryId")
val groupWithQuestionsList: List<GroupWithQuestions>
)
- 注意即使您正在获取 GroupWithQuestions 列表,它也是指定的组实体。
一些可能有用的额外内容:-
data class CategoryWithGroup(
@Embedded
val category: Category,
@Relation(entity = Group::class,entityColumn = "categoryIdMap",parentColumn = "categoryId")
val group: Group
)
data class GroupWithCategory(
@Embedded
val group: Group,
@Relation(entity = Category::class,entityColumn = "categoryId",parentColumn = "categoryIdMap")
val category: Category
)
道氏
AllDao(即 brevity/convenience 全部集中在一处):-
@Dao
abstract class AllDao {
@Insert
abstract fun insert(category: Category): Long
@Insert
abstract fun insert(group: Group): Long
@Insert
abstract fun insert(question: Question): Long
@Insert
abstract fun insert(questionGroupMap: QuestionGroupMap): Long
@Transaction
@Query("SELECT * FROM `group`")
abstract fun getAllGroupsWithCategory(): List<GroupWithCategory>
@Transaction
@Query("SELECT * FROM category")
abstract fun getAllCategoriesWithGroups(): List<CategoryWithGroup>
@Transaction
@Query("SELECT * FROM `group`")
abstract fun getAllGroupsWithQuestions(): List<GroupWithQuestions>
@Transaction
@Query("SELECT * FROM category")
abstract fun getAllCategoriesWithGroupsWithQuestions(): List<CategoryWithGroupWithQuestions>
}
一个@Database class TheDatabase :-
@Database(entities = [Category::class,Group::class,Question::class,QuestionGroupMap::class],exportSchema = false,version = 1)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
companion object {
@Volatile
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase::class.java,
"thedatabase.db"
)
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
- allowMainThreadQueries brevity/convenience
最终在 activity 中将上述内容付诸实践,导致提取 CategoreiesWithgroupsWithQuestions 列表并输出到日志 :-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
val TAG = "DBINFO"
val cat1 = dao.insert(Category(categoryName = "Cat1"))
val cat2 = dao.insert(Category(categoryName = "Cat2"))
val cat3 = dao.insert(Category(categoryName = "Cat3"))
val grp1 = dao.insert(Group(groupName = "Grp1",categoryIdMap = cat1))
val grp11 = dao.insert(Group(groupName = "Grp11",categoryIdMap = cat1))
val grp111 = dao.insert(Group(groupName = "Grp111",categoryIdMap = cat1))
val grp1111 = dao.insert(Group(groupName = "Grp1111",categoryIdMap = cat1))
val grp2 = dao.insert(Group(groupName = "Grp2",categoryIdMap = cat2))
val grp22 = dao.insert(Group(groupName = "Grp22",categoryIdMap = cat2))
val grp3 = dao.insert(Group(groupName = "Grp3",categoryIdMap = cat3))
val q1 = dao.insert(Question(questionText = "Q1 ....", questionOption = 11110000))
val q2 = dao.insert(Question(questionText = "Q2....", questionOption = 11010101))
val q3 = dao.insert(Question(questionText = "Q3....", questionOption = 10000001))
val q4 = dao.insert(Question(questionText = "Q4....",questionOption = 11000001))
val q5 = dao.insert(Question(questionText = "Q5....",questionOption = 11100011))
dao.insert(QuestionGroupMap(q1,grp1))
dao.insert(QuestionGroupMap(q1,grp2))
dao.insert(QuestionGroupMap(q1,grp3))
dao.insert(QuestionGroupMap(q2,grp2))
dao.insert(QuestionGroupMap(q2,grp22))
dao.insert(QuestionGroupMap(q3,grp3))
dao.insert(QuestionGroupMap(q4,grp11))
dao.insert(QuestionGroupMap(q4,grp111))
dao.insert(QuestionGroupMap(q4,grp1111))
dao.insert(QuestionGroupMap(q5,grp22))
/* extract the data via the geAllCategoriesWithGroupsWithQuestions query*/
for (cwgwq: CategoryWithGroupWithQuestions in dao.getAllCategoriesWithGroupsWithQuestions()) {
Log.d(TAG,"Category is ${cwgwq.category.categoryName} ID is ${cwgwq.category.categoryId}, it has ${cwgwq.groupWithQuestionsList.size} groups, which are:-")
for(gwq: GroupWithQuestions in cwgwq.groupWithQuestionsList) {
Log.d(TAG,"\tGroup is ${gwq.group.groupName} ID is ${gwq.group.groupId}, it has ${gwq.questionList.size} questions, which are:-")
for(q: Question in gwq.questionList) {
Log.d(TAG,"\t\tQuestion is ${q.questionText} options are ${q.questionOption} ID is ${q.questionId}")
}
}
}
}
}
结果:-
D/DBINFO: Category is Cat1 ID is 1, it has 4 groups, which are:-
D/DBINFO: Group is Grp1 ID is 1, it has 1 questions, which are:-
D/DBINFO: Question is Q1 .... options are 11110000 ID is 1
D/DBINFO: Group is Grp11 ID is 2, it has 1 questions, which are:-
D/DBINFO: Question is Q4.... options are 11000001 ID is 4
D/DBINFO: Group is Grp111 ID is 3, it has 1 questions, which are:-
D/DBINFO: Question is Q4.... options are 11000001 ID is 4
D/DBINFO: Group is Grp1111 ID is 4, it has 1 questions, which are:-
D/DBINFO: Question is Q4.... options are 11000001 ID is 4
D/DBINFO: Category is Cat2 ID is 2, it has 2 groups, which are:-
D/DBINFO: Group is Grp2 ID is 5, it has 2 questions, which are:-
D/DBINFO: Question is Q1 .... options are 11110000 ID is 1
D/DBINFO: Question is Q2.... options are 11010101 ID is 2
D/DBINFO: Group is Grp22 ID is 6, it has 2 questions, which are:-
D/DBINFO: Question is Q2.... options are 11010101 ID is 2
D/DBINFO: Question is Q5.... options are 11100011 ID is 5
D/DBINFO: Category is Cat3 ID is 3, it has 1 groups, which are:-
D/DBINFO: Group is Grp3 ID is 7, it has 2 questions, which are:-
D/DBINFO: Question is Q1 .... options are 11110000 ID is 1
D/DBINFO: Question is Q3.... options are 10000001 ID is 3
在开始新项目之前,我正在寻求一些指导。
我的目标是根据问题的 Group 在提取 Question 数据的应用程序中生成 form/questionaire 和它的类别。其中每个类别包含许多组,每个组包含许多问题。
为了支持 UI 和嵌套回收器视图,目的是为视图模型提供包含嵌套列表的 单个对象 。即具有类别列表的对象,包含组列表,包含问题列表。
在设置房间、实体和 DAO 以及它们之间的关系方面,我的理解是实现此目标的最佳方法是:
- 创建问题实体(包含文本、选项等)
- 为问题和组之间的关系(多对多)创建引用table
- 创建parent/child table Groups和Categories的关系(一对多)
接下来应该使用一组基于关系的数据 classes 来结合每一对。
- GroupWithQuestions 数据class(使用@Relation 列出每个组中使用引用的问题table)
- CategoryWithGroupsWithQuestions 数据class(使用@Relation 列出每个类别中的组,使用parent/childtable)
- QuestionaireWithCategoriesWith...问题数据class(包含 CategoryWithGroupsWithQuestions 列表)
这很复杂,需要通过多个 table 来跟踪关系,因此很难更新并且解决错误也很耗时。我觉得我过度思考了这个方法(或者遗漏了一些东西)。
有simpler/smarter方法吗?
(单对象方法是问题的一部分吗?)
提前感谢您的建议和意见。
Create a Questions entity (conatining text,options..etc.) Create a reference table for the relationship between Questions and Groups (many-to-many) Create a parent/child table for the relationship between Groups and Categories (one-to-many)
一对多只需要在父项的子项中有一列。
This is complicated, relationships need to be followed through multiple tables, and thus will be hard to update and time consuming to resolve errors. I feel like Im over thinking the approach (or am missing something).
这并没有那么复杂:我相信以下内容与您所要求的非常接近:-
实体
类别:-
@Entity(
indices = [
Index(value = ["categoryName"],unique = true) /* Assume that a category name should be unique */
]
)
data class Category(
@PrimaryKey
val categoryId: Long? = null,
@ColumnInfo
val categoryName: String
)
组:-
@Entity(
foreignKeys = [
ForeignKey(
entity = Category::class,
parentColumns = ["categoryId"],
childColumns = ["categoryIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class Group(
@PrimaryKey
val groupId: Long? = null,
@ColumnInfo(index = true)
val categoryIdMap: Long,
val groupName: String
)
- 外键约束不是必需的,但它们有助于加强参照完整性。
- onDelete 和 onUpdate 不是必需的,但很有用
问题
@Entity(
)
data class Question(
@PrimaryKey
val questionId: Long? = null,
@ColumnInfo(index = true)
val questionText: String,
val questionOption: Int
)
QuestiongroupMap(可能是 GroupQuestionMap):-
@Entity(
primaryKeys = ["questionIdMap","groupIdMap"],
foreignKeys = [
ForeignKey(
entity = Question::class,
parentColumns = ["questionId"],
childColumns = ["questionIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE),
ForeignKey(
entity = Group::class,
parentColumns = ["groupId"],
childColumns = ["groupIdMap"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class QuestionGroupMap(
val questionIdMap: Long,
@ColumnInfo(index = true)
val groupIdMap: Long
)
POJO 的
GroupWithQuestions
data class GroupWithQuestions(
@Embedded
val group: Group,
@Relation(
entity = Question::class,
entityColumn = "questionId",
parentColumn = "groupId",
associateBy = Junction(
QuestionGroupMap::class,
parentColumn = "groupIdMap",
entityColumn = "questionIdMap"
)
)
val questionList: List<Question>
)
- 通过 QuestiongroupMap 以及关联和 Junction
CategoryWithGroupsWithQuestions
data class CategoryWithGroupWithQuestions(
@Embedded
val category: Category,
@Relation(entity = Group::class,entityColumn = "categoryIdMap",parentColumn = "categoryId")
val groupWithQuestionsList: List<GroupWithQuestions>
)
- 注意即使您正在获取 GroupWithQuestions 列表,它也是指定的组实体。
一些可能有用的额外内容:-
data class CategoryWithGroup(
@Embedded
val category: Category,
@Relation(entity = Group::class,entityColumn = "categoryIdMap",parentColumn = "categoryId")
val group: Group
)
data class GroupWithCategory(
@Embedded
val group: Group,
@Relation(entity = Category::class,entityColumn = "categoryId",parentColumn = "categoryIdMap")
val category: Category
)
道氏
AllDao(即 brevity/convenience 全部集中在一处):-
@Dao
abstract class AllDao {
@Insert
abstract fun insert(category: Category): Long
@Insert
abstract fun insert(group: Group): Long
@Insert
abstract fun insert(question: Question): Long
@Insert
abstract fun insert(questionGroupMap: QuestionGroupMap): Long
@Transaction
@Query("SELECT * FROM `group`")
abstract fun getAllGroupsWithCategory(): List<GroupWithCategory>
@Transaction
@Query("SELECT * FROM category")
abstract fun getAllCategoriesWithGroups(): List<CategoryWithGroup>
@Transaction
@Query("SELECT * FROM `group`")
abstract fun getAllGroupsWithQuestions(): List<GroupWithQuestions>
@Transaction
@Query("SELECT * FROM category")
abstract fun getAllCategoriesWithGroupsWithQuestions(): List<CategoryWithGroupWithQuestions>
}
一个@Database class TheDatabase :-
@Database(entities = [Category::class,Group::class,Question::class,QuestionGroupMap::class],exportSchema = false,version = 1)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDao(): AllDao
companion object {
@Volatile
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase::class.java,
"thedatabase.db"
)
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
- allowMainThreadQueries brevity/convenience
最终在 activity 中将上述内容付诸实践,导致提取 CategoreiesWithgroupsWithQuestions 列表并输出到日志 :-
class MainActivity : AppCompatActivity() {
lateinit var db: TheDatabase
lateinit var dao: AllDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = TheDatabase.getInstance(this)
dao = db.getAllDao()
val TAG = "DBINFO"
val cat1 = dao.insert(Category(categoryName = "Cat1"))
val cat2 = dao.insert(Category(categoryName = "Cat2"))
val cat3 = dao.insert(Category(categoryName = "Cat3"))
val grp1 = dao.insert(Group(groupName = "Grp1",categoryIdMap = cat1))
val grp11 = dao.insert(Group(groupName = "Grp11",categoryIdMap = cat1))
val grp111 = dao.insert(Group(groupName = "Grp111",categoryIdMap = cat1))
val grp1111 = dao.insert(Group(groupName = "Grp1111",categoryIdMap = cat1))
val grp2 = dao.insert(Group(groupName = "Grp2",categoryIdMap = cat2))
val grp22 = dao.insert(Group(groupName = "Grp22",categoryIdMap = cat2))
val grp3 = dao.insert(Group(groupName = "Grp3",categoryIdMap = cat3))
val q1 = dao.insert(Question(questionText = "Q1 ....", questionOption = 11110000))
val q2 = dao.insert(Question(questionText = "Q2....", questionOption = 11010101))
val q3 = dao.insert(Question(questionText = "Q3....", questionOption = 10000001))
val q4 = dao.insert(Question(questionText = "Q4....",questionOption = 11000001))
val q5 = dao.insert(Question(questionText = "Q5....",questionOption = 11100011))
dao.insert(QuestionGroupMap(q1,grp1))
dao.insert(QuestionGroupMap(q1,grp2))
dao.insert(QuestionGroupMap(q1,grp3))
dao.insert(QuestionGroupMap(q2,grp2))
dao.insert(QuestionGroupMap(q2,grp22))
dao.insert(QuestionGroupMap(q3,grp3))
dao.insert(QuestionGroupMap(q4,grp11))
dao.insert(QuestionGroupMap(q4,grp111))
dao.insert(QuestionGroupMap(q4,grp1111))
dao.insert(QuestionGroupMap(q5,grp22))
/* extract the data via the geAllCategoriesWithGroupsWithQuestions query*/
for (cwgwq: CategoryWithGroupWithQuestions in dao.getAllCategoriesWithGroupsWithQuestions()) {
Log.d(TAG,"Category is ${cwgwq.category.categoryName} ID is ${cwgwq.category.categoryId}, it has ${cwgwq.groupWithQuestionsList.size} groups, which are:-")
for(gwq: GroupWithQuestions in cwgwq.groupWithQuestionsList) {
Log.d(TAG,"\tGroup is ${gwq.group.groupName} ID is ${gwq.group.groupId}, it has ${gwq.questionList.size} questions, which are:-")
for(q: Question in gwq.questionList) {
Log.d(TAG,"\t\tQuestion is ${q.questionText} options are ${q.questionOption} ID is ${q.questionId}")
}
}
}
}
}
结果:-
D/DBINFO: Category is Cat1 ID is 1, it has 4 groups, which are:-
D/DBINFO: Group is Grp1 ID is 1, it has 1 questions, which are:-
D/DBINFO: Question is Q1 .... options are 11110000 ID is 1
D/DBINFO: Group is Grp11 ID is 2, it has 1 questions, which are:-
D/DBINFO: Question is Q4.... options are 11000001 ID is 4
D/DBINFO: Group is Grp111 ID is 3, it has 1 questions, which are:-
D/DBINFO: Question is Q4.... options are 11000001 ID is 4
D/DBINFO: Group is Grp1111 ID is 4, it has 1 questions, which are:-
D/DBINFO: Question is Q4.... options are 11000001 ID is 4
D/DBINFO: Category is Cat2 ID is 2, it has 2 groups, which are:-
D/DBINFO: Group is Grp2 ID is 5, it has 2 questions, which are:-
D/DBINFO: Question is Q1 .... options are 11110000 ID is 1
D/DBINFO: Question is Q2.... options are 11010101 ID is 2
D/DBINFO: Group is Grp22 ID is 6, it has 2 questions, which are:-
D/DBINFO: Question is Q2.... options are 11010101 ID is 2
D/DBINFO: Question is Q5.... options are 11100011 ID is 5
D/DBINFO: Category is Cat3 ID is 3, it has 1 groups, which are:-
D/DBINFO: Group is Grp3 ID is 7, it has 2 questions, which are:-
D/DBINFO: Question is Q1 .... options are 11110000 ID is 1
D/DBINFO: Question is Q3.... options are 10000001 ID is 3