如何指定与嵌入式实体的外交关系

时间:2020-09-27 18:01:03

标签: android android-room

给出的答案将嵌入式类型更改为外键类型,我不是要找这个。

我有一个学生桌和一个嵌入式类型地址。

学生模式

+----+------+------------+
| id | name | address_id |
+----+------+------------+
|  1 | John |          1 |
|  2 | Jane |          2 |
+----+------+------------+

地址架构

+----+------------------+
| id |     addressLine  |
+----+------------------+
|  1 | 123 Acme Street  |
|  2 | 456 Beach Street |
+----+------------------+

学生实体

@Entity(tableName = "student")
data class Student(
     @PrimaryKey var id: Long, 
     var name: String?, 
     @Embedded var address: Address // can't change, given answer changed it
)

地址实体

@Entity(tableName = "address")
data class Address(
     @PrimaryKey var id: Long, 
     val addressLine: String
)

DAO界面

@Dao
interface StudentDao {

    @Query("SELECT * FROM student WHERE id = :id INNER JOIN address ON student.address_id = address.id")
    fun findById(id: Long): Student
}

问题:当前无法填充学生的嵌入式地址。

1 个答案:

答案 0 :(得分:0)

用例的常用方法是使用额外的POJO类(不是实体)从两个表中获取联接结果。

因此,实体:

@Entity(tableName = "student")
data class Student(
     @PrimaryKey var id: Long, 
     var name: String?, 
     var address_id: Long // <- you can make it foreign key in addition 
)

@Entity(tableName = "address")
data class Address(
     @PrimaryKey var id: Long, 
     val addressLine: String
)

和辅助POJO类:

data class StudentWithAdress( // <-- you can change the class name to more descriptive one 
     @Embedded var student: Student 
     @Embedded var address: Address 
)

那你的道:

@Dao
interface StudentDao {

@Query("SELECT * FROM student WHERE id = :id INNER JOIN address ON student.address_id = address.id")
    fun findById(id: Long): StudentWithAdress
}

已更新

第二种方法是使用one-to-one Room's @Relation而不是SQLite联接

此方法使用相同实体,但查询类和辅助类稍有不同:

data class StudentWithAdress(
     @Embedded var student: Student 
     @Relation(
         parentColumn = "addressId",
         entityColumn = "id"
    )
    var address: Address 
)

查询会更简单:

@Transaction
@Query("SELECT * FROM student WHERE id = :id")
    fun findById(id: Long): StudentWithAdress
}

结论

  1. Student类应包含 ONLY addressId(不是整个Address对象),并且您使用单独的类StudentWithAdress 1}}和Student。因此,要将值插入Address,您应该在相应的Student表中插入addressId。这是常见且正确的方法。
  2. 两种描述的方式都相似,您可以选择任何一种。我个人更喜欢使用关系方式。
  3. 从技术上讲,可以仅使用两个没有辅助类的实体(并将所有Address内容保存在Address实体中),但是我认为这违反了关系表规范化的原理,我不希望将此内容包含在我的答案中,因为它是反模式。

更新2(反模式)

我不建议这样做,但是如果您坚持可以使用下一个模式:

Stident

它如何工作? @Embedded表示实际上Sqlite表@Entity(tableName = "address") data class Address( @PrimaryKey var idAddress: Long, // <-- changed val addressLine: String ) @Entity(tableName = "student") data class Student( @PrimaryKey var id: Long, var name: String?, @Embedded var address: Address ) 包含Student的所有字段。在您的实际Sqlite表中,您将有4列-id,name,idAddress,addressLine(这就是为什么必须更改Address主键名称的原因,因为不能存在具有相同名称的字段)。 Room将其隐藏了一点,您可以通过Address字段使用Student对象。为什么这不好?让我们看一下场景:

  1. 您保存的address的ID = 1,addressLine =“某些地址#1”。
  2. 您在某个Address对象中设置了该地址,并将其保留。在Student表中的Sqlite的内部将保存值Studentid = 1
  3. 然后出于某种原因将addressLine = Some address #1中的addressLine更改为“某些地址#2”。
  4. 但是在Address表中仍然有Student的旧值。那是一个错误。或者您应该在addressLine表上保留此更改,这也很糟糕。