如果答案将嵌入类型更改为外键类型,我不会寻找这个。
我有一个学生表和一个嵌入式地址。
学生模式
+----+------+------------+
| 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
}
结论
Student
类应包括ONLYaddressId
(而不是整个Address
对象(,并且使用同时具有Student
和Address
的单独类StudentWithAdress
。因此,要将值插入到Student
,您应该从相应的Address
表中插入addressId
。这是一种常见而正确的方式- 两种描述的方式相似,您可以选择任何一种。就我个人而言,我更喜欢关系方式
- 从技术上讲,只使用两个没有辅助类的实体是可能的(并在
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
对象。为什么这很糟糕?让我们看看场景:
- 您保存的
Address
id=1,addressLine=";一些地址#1"> - 您在某个
Student
对象中设置了此地址并将其持久化。在Student
表中的Sqlite将包含值id = 1
、addressLine = Some address #1
- 然后出于某种原因,将CCD_ 23中的CCD_;一些地址#2">
- 但在CCD_ 24表中仍然存在CCD_ 25的旧值。这是个bug。或者,您应该在
Student
表中保留此更改,这也很糟糕