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



如果答案将嵌入类型更改为外键类型,我不会寻找这个。

我有一个学生表和一个嵌入式地址。

学生模式

+----+------+------------+
| 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
}

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

用例的常见方法是使用额外的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
}

更新

第二种方法是使用一对一Room的@Relation,而不是使用SQLite Joins

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

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类应包括ONLYaddressId(而不是整个Address对象(,并且使用同时具有StudentAddress的单独类StudentWithAdress。因此,要将值插入到Student,您应该从相应的Address表中插入addressId。这是一种常见而正确的方式
  2. 两种描述的方式相似,您可以选择任何一种。就我个人而言,我更喜欢关系方式
  3. 从技术上讲,只使用两个没有辅助类的实体是可能的(并在Stident实体中保存所有Address内容(,但我认为这违反了关系表规范化的原则,我不想在我的回答中包括这一点,因为这是反模式的

更新2(反模式(

我不推荐它,但若你们坚持你们可以使用下一个模式:

@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 
)

它是如何工作的@Embedded意味着实际上Sqlite表Student包括Address的所有字段。在实际的Sqlite表中,将有4列-id、name、idAddress、addressLine(这就是为什么有必要更改Address主键的名称,因为不可能有具有相同名称的字段(。Room稍微隐藏了这一点,您可以通过address字段处理Student对象。为什么这很糟糕?让我们看看场景:

  1. 您保存的Addressid=1,addressLine=";一些地址#1">
  2. 您在某个Student对象中设置了此地址并将其持久化。在Student表中的Sqlite将包含值id = 1addressLine = Some address #1
  3. 然后出于某种原因,将CCD_ 23中的CCD_;一些地址#2">
  4. 但在CCD_ 24表中仍然存在CCD_ 25的旧值。这是个bug。或者,您应该在Student表中保留此更改,这也很糟糕

最新更新