Android room - 3 一对多关系

Android room - 3 one-to-many relationship

我正在尝试在 3 个表之间创建关系,这些表彼此之间是一对多的顺序。所以 Table1 与 Table 2 具有多对一关系,而 Table2 与 Table3.

具有多对一关系

我想做的是创建一个关系,这样我就可以拥有 Table3 以及相关 Table2 个实体的列表和另一个 Table1 个实体的列表与 Table2 有关。我一直在查看此处的文档和问题,但我尝试过的关系要么给我例外,要么缺少实体,要么提供的实体与我查询的内容无关。

如果我尝试的方法无法做到这一点,您能否指导我朝着正确的方向前进?我目前拥有的是查询第一个关系 (Table3-Table2) 的事务,然后是基于第一个实体 (Table2-Table 的第二个关系1) 并返回所有这些关系的对象。

请让我知道是否已经有 question/blog post 回答这个问题,因为我找不到它。

Link to the simplified table relationships

您必须以分层方式(自下而上)处理此问题

您有一个表 2 (@Embedded) 的 POJO 和表 3 children (@Relation)

的列表

然后您有一个 Table1 (@Embedded) 的 POJO 和一个 Table2 POJO children (@Relation) 的列表。

例如:-

表1

@Entity(tableName = "table1")
data class Table1(
    @PrimaryKey
    @ColumnInfo(name = "table1_id")
    val id: Long? = null,
    @ColumnInfo(name = "table1_name")
    val name: String
)

表2

@Entity(
    tableName = "table2",
    foreignKeys = [
        ForeignKey(
            entity = Table1::class,
            parentColumns = ["table1_id"],
            childColumns = ["map_to_parent_in_table1"],
            onDelete = CASCADE,
            onUpdate = CASCADE
        )
    ]
)
data class Table2(
    @PrimaryKey
    @ColumnInfo(name = "table2_id")
    val id: Long? = null,
    @ColumnInfo(name = "map_to_parent_in_table1", index = true)
    val map_to_parent: Long,
    @ColumnInfo(name = "table2_name")
    val name: String
)

表3

@Entity(
    tableName = "table3",
    foreignKeys = [
        ForeignKey(
            entity = Table2::class,
            parentColumns = ["table2_id"],
            childColumns = ["map_to_parent_in_table2"],
            onDelete = ForeignKey.CASCADE,
            onUpdate = ForeignKey.CASCADE
        )
    ]
)
data class Table3(
    @PrimaryKey
    @ColumnInfo(name = "table3_id")
    val id: Long? = null,
    @ColumnInfo(name = "map_to_parent_in_table2", index = true)
    val map_to_parent: Long,
    @ColumnInfo(name = "table3_name")
    val name: String
)
  • foreignKeys 是可选的,但会强制执行并帮助维护参照完整性。在外键中 onDeleteonUpdate 是可选的

所以从最低的开始(具有 children 的最小兄弟,即表 2)然后是 POJO

Table2WithTable3Children

data class Table2WithTable3Children(
    @Embedded
    val table2: Table2,
    @Relation(
        entity = Table3::class,
        parentColumn = "table2_id",
        entityColumn = "map_to_parent_in_table2"
    )
    val children: List<Table3>
)

然后是 POJO

Table1WithTable2ChildrenIncludingTable3Children

data class Table1WithTable2ChildrenIncludingTable3Children(
    @Embedded
    val table1: Table1,
    @Relation(
        entity = Table2::class,
        parentColumn = "table1_id",
        entityColumn = "map_to_parent_in_table1"
    )
    val children: List<Table2WithTable3Children>
)

因此,如果您获得 Table1WithTable2ChildrenIncludingTable3Children,那么它会包含 Table2WithTable3Children 的列表。

您可以有这样的查询:-

@Query("SELECT * FROM table1" )
@Transaction
abstract fun getAllTable1sWithTable2ChildrenAndTable3Children(): List<Table1WithTable2ChildrenIncludingTable3Children>

这可以像这样使用:-

    dao.getAllTable1sWithTable2ChildrenAndTable3Children()
    for(t1: Table1WithTable2ChildrenIncludingTable3Children in dao.getAllTable1sWithTable2ChildrenAndTable3Children()) {
        Log.d(TAG,"Table1 is ${t1.table1.name} ID is ${t1.table1.id} it has ${t1.children.size} children in Table2 they are:-")
        for(t2: Table2WithTable3Children in t1.children) {
            Log.d(TAG,"\tTable2 is ${t2.table2.name} ID is ${t2.table2.id} it has ${t2.children} in Table3 they are :-")
            for(t3: Table3 in t2.children) {
                Log.d(TAG,"\t\tTable3 is ${t3.name} ID is ${t3.id}")
            }
        }
    }

工作Example/Demo

@Dao 注释 class AllDao 扩展上面的内容(这包括上面的查询):-

@Dao
abstract class AllDao {

    @Insert
    abstract fun insert(table1: Table1): Long
    @Insert
    abstract fun insert(table2: Table2): Long
    @Insert
    abstract fun insert(table3: Table3): Long

    @Query("SELECT * FROM table1" )
    @Transaction
    abstract fun getAllTable1sWithTable2ChildrenAndTable3Children(): List<Table1WithTable2ChildrenIncludingTable3Children>
}

和一个基本的 @Database 注释 class ThisDatabase :-

@Database(entities = [Table1::class,Table2::class,Table3::class], exportSchema = false, version = 1)
abstract class ThisDatabase: RoomDatabase() {
    abstract fun getAllDao(): AllDao
    companion object {
        private var instance: ThisDatabase? = null
        fun getInstance(context: Context): ThisDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(context,ThisDatabase::class.java,"this_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as ThisDatabase
        }
    }
}

并全面测试 MainActivity :-

class MainActivity : AppCompatActivity() {
    lateinit var db: ThisDatabase
    lateinit var dao: AllDao
    val TAG = "DBINFO"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = ThisDatabase.getInstance(this)
        dao = db.getAllDao()

        val t1_1 = dao.insert(Table1( name = "First Table1"))
        val t1_2 = dao.insert(Table1(name = "Second Table1"))
        val t1_3 = dao.insert(Table1(name = "Third Table1"))

        val t2_1 = dao.insert(Table2(name = "First Table2 with First Table1 as Parent", map_to_parent = t1_1))
        val t2_2 = dao.insert(Table2(name = "Second Table2 with First Table1 as Parent", map_to_parent = t1_1))
        val t2_3 = dao.insert(Table2(name = "Third Table2 with Second Table1 as Parent", map_to_parent = t1_2))

        dao.insert(Table3(name = "T31", map_to_parent = t2_1))
        dao.insert(Table3(name = "T32", map_to_parent = t2_2))
        dao.insert(Table3(name = "T33", map_to_parent = t2_3))
        dao.insert(Table3(name = "T34", map_to_parent = t2_1))
        dao.insert(Table3(name = "T35", map_to_parent = t2_1))

        dao.getAllTable1sWithTable2ChildrenAndTable3Children()
        for(t1: Table1WithTable2ChildrenIncludingTable3Children in dao.getAllTable1sWithTable2ChildrenAndTable3Children()) {
            Log.d(TAG,"Table1 is ${t1.table1.name} ID is ${t1.table1.id} it has ${t1.children.size} children in Table2 they are:-")
            for(t2: Table2WithTable3Children in t1.children) {
                Log.d(TAG,"\tTable2 is ${t2.table2.name} ID is ${t2.table2.id} it has ${t2.children.size} in Table3 they are :-")
                for(t3: Table3 in t2.children) {
                    Log.d(TAG,"\t\tTable3 is ${t3.name} ID is ${t3.id}")
                }
            }
        }
    }
}

然后当运行第一次写入日志的结果是:-

D/DBINFO: Table1 is First Table1 ID is 1 it has 2 children in Table2 they are:-
D/DBINFO:   Table2 is First Table2 with First Table1 as Parent ID is 1 it has 3 in Table3 they are :-
D/DBINFO:       Table3 is T31 ID is 1
D/DBINFO:       Table3 is T34 ID is 4
D/DBINFO:       Table3 is T35 ID is 5
D/DBINFO:   Table2 is Second Table2 with First Table1 as Parent ID is 2 it has 1 in Table3 they are :-
D/DBINFO:       Table3 is T32 ID is 2
D/DBINFO: Table1 is Second Table1 ID is 2 it has 1 children in Table2 they are:-
D/DBINFO:   Table2 is Third Table2 with Second Table1 as Parent ID is 3 it has 1 in Table3 they are :-
D/DBINFO:       Table3 is T33 ID is 3
D/DBINFO: Table1 is Third Table1 ID is 3 it has 0 children in Table2 they are:-