【问题标题】:Structuring DB tables and @Relation to create a questionaire from db entities? (kotlin, Room)构建数据库表和@Relation 以从数据库实体创建问卷? (科特林,房间)
【发布时间】:2021-10-13 08:41:55
【问题描述】:

在进入一个新项目之前,我正在寻求一些指导。

我的目标是根据问题的Group及其Category在应用程序中生成一个表单/问卷,以提取Question数据。每个类别包含许多组,每个组包含许多问题。

为了支持 UI 和嵌套回收器视图,目的是为视图模型提供一个包含嵌套列表的单个对象。即具有类别列表的对象,包含组列表,包含问题列表。

就设置房间、实体和 DAO 及其关系而言,我的理解是,实现这一目标的最佳方法是:

  • 创建问题实体(包含文本、选项等)
  • 为问题和组之间的关系创建参考表(多对多)
  • 为组和类别之间的关系创建父/子表(一对多)

在此之后,应该使用一组基于关系的数据类来连接每一对。

  • GroupWithQuestions 数据类(使用 @Relation 使用参考表列出每个组中的问题)
  • CategoryWithGroupsWithQuestions 数据类(使用@Relation 使用父/子表列出每个类别中的组)
  • QuestionaireWithCategoriesWith...Questions 数据类(包含 CategoryWithGroupsWithQuestions 列表)

这很复杂,需要通过多个表来跟踪关系,因此更新起来很困难,解决错误也很耗时。我觉得我在考虑这种方法(或遗漏了一些东西)。

有没有更简单/更智能的方法?

(单对象方法是问题的一部分吗?)

提前感谢您的建议和 cmets。

【问题讨论】:

    标签: kotlin android-room


    【解决方案1】:

    创建一个 Questions 实体(包含文本、选项等) 为问题和组之间的关系创建参考表(多对多) 为Groups和Categories之间的关系创建一个父/子表(一对多)

    一对多只需要在子项中为父项提供一列。

    这很复杂,需要通过多个表来跟踪关系,因此更新起来很困难,解决错误也很耗时。我觉得我在考虑这种方法(或遗漏了一些东西)。

    这并不是真的那么复杂:我相信以下内容非常接近您的要求:-

    实体

    类别:-

    @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 列表,它也是指定的 Group 实体。

    一些可能有用的附加功能:-

    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(即为了简洁/方便,全部集中在一个地方):-

    @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 类 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 为简洁/方便起见

    最后将上述内容付诸于活动中,从而提取出一个 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
    

    【讨论】:

    • 哇! @miket 那是一个令人难以置信的答案。感谢您花时间和精力来介绍所有这些细节。是的,你是对的,这就是我在设置实体和关系时所指的内容。因为这是一种更简单的方法,所以我很乐意继续学习。
    猜你喜欢
    • 2014-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多