POJO 中的 POJO,实体关系问题
POJO within POJO, Entity-Relation problem
我的数据库中有五个 table:AREA、AREA_TYPE、SAMPLE、PACK、UNIT
@Entity(tableName = "AREA")
data class AreaEntity(
@PrimaryKey val id:String,
val title:String,
@ColumnInfo(name = "area_type_id") val areaTypeId:Int,
@ColumnInfo(name = "is_active") val isActive:Boolean
)
@Entity(tableName = "AREA_TYPE")
data class AreaTypeEntity(
@PrimaryKey val id:String,
val title:String,
@ColumnInfo(name = "title") val parentAreaId : String
)
@Entity(tableName = "SAMPLE")
data class SampleEntity(
@PrimaryKey val id:String,
val title:String,
)
@Entity(tableName = "PACK")
data class PackEntity(
@PrimaryKey val id:String,
val title:String,
)
@Entity(tableName = "UNIT")
data class UnitEntity(
@PrimaryKey val id:String,
@ColumnInfo(name = "sample_id") val parentAreaId : String,
@ColumnInfo(name = "area_id") val areaId:Int,
@ColumnInfo(name = "pack_type_id") val packTypeId: Int,
@ColumnInfo(name = "is_active") val isActive:Boolean
)
UNIT table 有三个外键:sample_id、area_id、pack_id
每个区域都与区域类型一一对应
我有一个 AreaPOJO 用于 Area-AreaType 关系:
data class AreaPOJO (
@Embedded val areaEntity : AreaEntity
@Relation (
parentColumn = "area_id",
entityColumn = "id"
)
val areaTypeEntity : AreaTypeEntity
)
tables (https://i.stack.imgur.com/bXzl5.png)
的视觉视图
所以我假设我将有一个 POJO 用于 UNIT 的关系如下:
data class UnitPOJO (
@Embedded val unitEntity : UnitEntity
@Relation (
parentColumn = "area_id",
entityColumn = "id"
)
val areaEntity : AreaEntity
@Relation (
parentColumn = "pack_id",
entityColumn = "id"
)
val packEntity : PackEntity
@Relation (
parentColumn = "sample_id",
entityColumn = "id"
)
val sampleEntity : SampleEntity
)
使用此 POJO,我可以获得 AreaEntity、SampleEntity、UnitEntity,但我无法获得 UnitPOJO 的 AreaTypeEntity。
当我使用 AreaPOJO 而不是 AreaEntity 时,出现编译错误,提示我对 AreaPOJO 使用“前缀”。当我使用前缀时,这次 AreaPOJO 报错说它找不到关系的列名。
所以我卡住了:)
简而言之,我需要所有五个 table 的所有字段来进行此查询:
"SELECT * FROM UNIT
INNER JOIN AREA ON UNIT.AREA_ID = AREA.ID
INNER JOIN AREA_TYPE ON AREA.AREA_TYPE_ID = AREA_TYPE.ID
INNER JOIN SAMPLE ON UNIT.SAMPLE_ID = SAMPLE.ID
INNER JOIN PACK ON UNIT.PACK_ID = PACK.ID"
首先使用 prefix,这是一个避免歧义列名的选项(例如 哪个 id 列是正确的使用?(修辞)) 但是您随后必须尝试使用查询来包含 AS(隐式或显式)以重命名提取的列。
我建议使用唯一的列名是避免此类歧义的方法。
进入 grandparent/grandchild。
简而言之,您接近了,但是您检索到的是 AreaPOJO(带类型的区域)而不是 AreaEntity,但是您必须告诉 Room 使用 AreaEntity class(因为那是 class用于确定 AreaEntity 的列,然后 AreaPOJO 中的@relation 知道获取 inderlying AreaType)。
因此,尽管未经测试但已成功编译,请考虑以下事项:-
@Entity(tableName = "UNIT")
data class UnitEntity(
@PrimaryKey val id:String,
@ColumnInfo(name = "sample_id") val parentAreaId : String,
@ColumnInfo(name = "area_id") val areaId:Int,
@ColumnInfo(name = "pack_type_id") val packTypeId: Int,
@ColumnInfo(name = "is_active") val isActive:Boolean
)
@Entity(tableName = "AREA_TYPE")
data class AreaTypeEntity(
@PrimaryKey @ColumnInfo(name = "area_type_id") val id:String, //<<<<< unique name
val title:String,
@ColumnInfo(name = "area_type_title") val parentAreaId : String
)
data class AreaPOJO(
@Embedded val areaEntity : AreaEntity,
@Relation(
parentColumn = "area_type_id", //<<<<< changed accrodingly
entityColumn = "area_type_id" //<<<<< changed accordingly
)
val areaTypeEntity : AreaTypeEntity
)
data class UnitPOJO (
@Embedded val unitEntity : UnitEntity,
@Relation (
entity = AreaEntity::class, //<<<<< ADDED
parentColumn = "area_id",
entityColumn = "area_id"
)
val areaWithAreaType : AreaPOJO,
@Relation (
parentColumn = "pack_type_id",
entityColumn = "pack_id"
)
val packEntity : PackEntity,
@Relation (
parentColumn = "sample_id",
entityColumn = "sample_id"
)
val sampleEntity : SampleEntity
)
- 请注意 UnitPOJO 中的
pack_type_id
和 pack_id
,而不是示例 reference/relationship 中的 sample_id
和 sample_id
。
- 我建议考虑使用唯一名称,例如pack_type_id是referencing/mapping单元和pack的关系,或许命名单元中的列pack_id_map。因此,列名更具描述性和唯一性。缺点是编码比较多
使用上面的@Query 可以是:-
@Transaction
@Query("SELECT * FROM UNIT")
fun getUnitsWithRelations(): List<UnitPOJO>
- 明显根据Flow/Live数据等调整
也就是说,当 Room 处理它构建的 @Relation
时,上面的代码效率低下,并且每个 @Relation
的基础查询从父级获取所有子级(我相信基于每个父级) .在您的情况下,您似乎有一对多关系,因此可以使用 @Embedded
但查询必须更复杂。
工作示例
以下是基于您的代码的工作示例
- 同时使用
@Relation
和 @Embedded
分辨率
- @Relation POJO 是 UnitPOJO 和 AreaPOJO
- @Embedded 版本以 Alternative
为前缀
- 添加数据(每行 3 行 table,5 个单位除外)
- 使用两种备选方案提取单位和相关数据
- 包括强制引用和维护完整性的外键约束,请参阅 https://sqlite.org/foreignkeys.html
应该注意的是,我相信您有一些不寻常的和猜测不必要的关系,因此已经进行了一些更改。例如当只需要一个时,您似乎有 Area 与 AreaType 两种方式相关。也就是说,一个 Area 将有一个 AreaType 作为父级,但是如果一个 AreaType 也有一个 Area 作为父级,那么你就会遇到先有鸡还是先有蛋的情况。
- 假设 Area 具有许多可用 AreaType 之一作为父级。
首先是 classes(见评论):-
@Entity(
tableName = "AREA",
/* Enforces/Maintains referential Integrity */
/* i.e does not allow orphans */
foreignKeys = [
ForeignKey(
entity = AreaTypeEntity::class,
parentColumns = ["area_type_id"],
childColumns = ["area_type_id_map" ],
onDelete = ForeignKey.CASCADE /* ????? */,
onUpdate = ForeignKey.CASCADE /* ????? */
)
]
)
data class AreaEntity(
@PrimaryKey @ColumnInfo(name = "area_id")val id:String, //<<<<< unique name
@ColumnInfo(name = "area_title") val title:String,
@ColumnInfo(name = "area_type_id_map") val areaTypeId:String, //<<<<< see Area Type
@ColumnInfo(name = "area_is_active") val isActive:Boolean
)
@Entity(tableName = "SAMPLE")
data class SampleEntity(
@PrimaryKey @ColumnInfo(name = "sample_id") val id:String, //<<<<< unique name
@ColumnInfo(name = "sample_title") val title:String,
)
@Entity(tableName = "PACK")
data class PackEntity(
@PrimaryKey @ColumnInfo(name = "pack_id") val id:String, //<<<<< unique name
@ColumnInfo(name = "pack_title") val title:String, //<<<<< unique name
)
@Entity(
tableName = "UNIT",
foreignKeys = [
ForeignKey(
entity = SampleEntity::class,
parentColumns = ["sample_id"],
childColumns = ["sample_id_map"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = AreaEntity::class,
parentColumns = ["area_id"],
childColumns = ["area_id_map"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = PackEntity::class,
parentColumns = ["pack_id"],
childColumns = ["pack_id_map"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class UnitEntity(
@PrimaryKey val id:String,
@ColumnInfo(name = "sample_id_map") val sampleId : String,
@ColumnInfo(name = "area_id_map") val areaId:String,
@ColumnInfo(name = "pack_id_map") val packTypeId: String,
@ColumnInfo(name = "unit_is_active") val isActive:Boolean
)
@Entity(
tableName = "AREA_TYPE"
)
data class AreaTypeEntity(
@PrimaryKey @ColumnInfo(name = "area_type_id") val id:String, //<<<<< unique name
@ColumnInfo(name = "area_type_title") val title:String,
/* ???? should an area type have an area as a parent? potential issues if so */
/* commented out
@ColumnInfo(name = "area_type_title") val parentAreaId : String //<<<<< unique name
*/
)
data class AreaPOJO(
@Embedded val areaEntity : AreaEntity,
@Relation(
parentColumn = "area_type_id_map", //<<<<< changed accordingly
entityColumn = "area_type_id" //<<<<< changed accordingly
)
val areaTypeEntity : AreaTypeEntity
)
data class UnitPOJO (
@Embedded val unitEntity : UnitEntity,
@Relation (
entity = AreaEntity::class, //<<<<< ADDED
parentColumn = "area_id_map",
entityColumn = "area_id"
)
val areaWithAreaType : AreaPOJO,
@Relation (
parentColumn = "pack_id_map",
entityColumn = "pack_id"
)
val packEntity : PackEntity,
@Relation (
parentColumn = "sample_id_map",
entityColumn = "sample_id"
)
val sampleEntity : SampleEntity
)
data class AlternativeAreaPOJO (
@Embedded val areaEntity: AreaEntity,
@Embedded val areaTypeEntity: AreaTypeEntity
)
data class AlternativeUnitPOJO (
@Embedded val unitEntity: UnitEntity,
@Embedded val alternativeAreaPOJO: AlternativeAreaPOJO,
@Embedded val packEntity: PackEntity,
@Embedded val sampleEntity: SampleEntity
)
@Dao注解接口AllDao :-
@Dao
interface AllDAO {
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(areaEntity: AreaEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(unitEntity: UnitEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(sampleEntity: SampleEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(packEntity: PackEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(areaTypeEntity: AreaTypeEntity)
@Transaction
@Query("SELECT * FROM UNIT")
fun getUnitsWithRelations(): List<UnitPOJO>
@Query("SELECT * FROM UNIT " +
"INNER JOIN AREA ON UNIT.area_id_map = AREA.area_id " +
"INNER JOIN AREA_TYPE ON AREA.area_type_id_map = AREA_TYPE.area_type_id " +
"INNER JOIN SAMPLE ON UNIT.sample_id_map = SAMPLE.sample_id " +
"INNER JOIN PACK ON UNIT.pack_id_map = PACK.pack_id")
fun getAlternativeUnitsWithRelations(): List<AlternativeUnitPOJO>
}
@Database 注解 class TheDatabase :-
@Database(entities = [
AreaEntity::class,
SampleEntity::class,
PackEntity::class,
UnitEntity::class,
AreaTypeEntity::class
],
version = 1,
exportSchema = false
)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDAO(): AllDAO
companion object {
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase::class.java,
"the_database.db"
)
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
- 为方便起见,请注意 .allowMainThreadQueries 已被使用。
activity 中的代码(旨在 运行 一次):-
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 p1 = PackEntity("P001","Pack1")
val p2 = PackEntity("P002","Pack2")
val p3 = PackEntity("P003","Pack3")
dao.insert(p1)
dao.insert(p2)
dao.insert(p3)
val s1 = SampleEntity("S001","Sample1")
val s2 = SampleEntity("S002","Sample2")
val s3 = SampleEntity("S003","Sample3")
dao.insert(s1)
dao.insert(s2)
dao.insert(s3)
val at1 = AreaTypeEntity("AT001","AreaType1")
val at2 = AreaTypeEntity("AT002","AreaType2")
val at3 = AreaTypeEntity("AT003","AreaType3",)
dao.insert(at1)
dao.insert(at2)
dao.insert(at3)
val a1 = AreaEntity("A001","Area1",at1.id,true)
val a2 = AreaEntity("A002","Area2",at2.id,false)
val a3 = AreaEntity("A003","Area3",at1.id,true)
dao.insert(a1)
dao.insert(a2)
dao.insert(a3)
dao.insert(UnitEntity("U001",s1.id,a1.id,p1.id,true))
dao.insert(UnitEntity("U002",s2.id,a2.id,p2.id, false))
dao.insert(UnitEntity("U003",s3.id,a3.id,p3.id,true))
dao.insert(UnitEntity("U004",s1.id,a2.id,p3.id,false))
dao.insert(UnitEntity("U005",s3.id,a2.id,p1.id, true))
for(uwr in dao.getUnitsWithRelations()) {
Log.d(TAG,
"Unit is ${uwr.unitEntity.id} " +
"Active = ${uwr.unitEntity.isActive} " +
"Sample is ${uwr.sampleEntity.title} " +
"Area is ${uwr.areaWithAreaType.areaEntity.title} " +
"AreaType is ${uwr.areaWithAreaType.areaTypeEntity.title}"
)
}
for (auwr in dao.getAlternativeUnitsWithRelations()) {
Log.d(TAG,
"Unit is ${auwr.unitEntity.id} " +
"Active is ${auwr.unitEntity.isActive} " +
"Sample is ${auwr.sampleEntity.title} " +
"Area is ${auwr.alternativeAreaPOJO.areaEntity.title} " +
"AreaType is ${auwr.alternativeAreaPOJO.areaTypeEntity.title}"
)
}
}
}
日志的最后结果输出:-
2022-04-05 09:32:40.528 D/DBINFO: Unit is U001 Active = true Sample is Sample1 Area is Area1 AreaType is AreaType1
2022-04-05 09:32:40.528 D/DBINFO: Unit is U002 Active = false Sample is Sample2 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.529 D/DBINFO: Unit is U003 Active = true Sample is Sample3 Area is Area3 AreaType is AreaType1
2022-04-05 09:32:40.529 D/DBINFO: Unit is U004 Active = false Sample is Sample1 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.529 D/DBINFO: Unit is U005 Active = true Sample is Sample3 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.537 D/DBINFO: Unit is U001 Active is true Sample is Sample1 Area is Area1 AreaType is AreaType1
2022-04-05 09:32:40.537 D/DBINFO: Unit is U002 Active is false Sample is Sample2 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.537 D/DBINFO: Unit is U003 Active is true Sample is Sample3 Area is Area3 AreaType is AreaType1
2022-04-05 09:32:40.537 D/DBINFO: Unit is U004 Active is false Sample is Sample1 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.537 D/DBINFO: Unit is U005 Active is true Sample is Sample3 Area is Area2 AreaType is AreaType2
- 即两种选择的结果相同,当然关系按预期工作
其实我已经通过这个POJO解决了:
data class UnitPOJO
(
@Embedded val unit: UnitEntity,
@Relation(
parentColumn = "sample_id",
entityColumn = "id"
)
val sampleEntity: SampleEntity,
@Relation(
parentColumn = "pack_type_id",
entityColumn = "id"
)
val pack: PackEntity,
@Relation(
parentColumn = "area_id",
entityColumn = "id",
entity = AreaEntity::class
)
val area: AreaPOJO
)
AreaPOJO 是这样的:
data class AreaPOJO(
@Embedded val areaEntity: AreaEntity,
@Relation(
parentColumn = "area_type_id",
entityColumn = "id"
)
val areaTypeEntity: AreaTypeEntity
)
但我肯定会考虑你的警告@MikeT 关于在使用分层数据时命名 fields/column 名称。如果出现问题或者我得到意想不到的结果,我一定会使用这种方法来解决问题。
我的数据库中有五个 table:AREA、AREA_TYPE、SAMPLE、PACK、UNIT
@Entity(tableName = "AREA")
data class AreaEntity(
@PrimaryKey val id:String,
val title:String,
@ColumnInfo(name = "area_type_id") val areaTypeId:Int,
@ColumnInfo(name = "is_active") val isActive:Boolean
)
@Entity(tableName = "AREA_TYPE")
data class AreaTypeEntity(
@PrimaryKey val id:String,
val title:String,
@ColumnInfo(name = "title") val parentAreaId : String
)
@Entity(tableName = "SAMPLE")
data class SampleEntity(
@PrimaryKey val id:String,
val title:String,
)
@Entity(tableName = "PACK")
data class PackEntity(
@PrimaryKey val id:String,
val title:String,
)
@Entity(tableName = "UNIT")
data class UnitEntity(
@PrimaryKey val id:String,
@ColumnInfo(name = "sample_id") val parentAreaId : String,
@ColumnInfo(name = "area_id") val areaId:Int,
@ColumnInfo(name = "pack_type_id") val packTypeId: Int,
@ColumnInfo(name = "is_active") val isActive:Boolean
)
UNIT table 有三个外键:sample_id、area_id、pack_id
每个区域都与区域类型一一对应
我有一个 AreaPOJO 用于 Area-AreaType 关系:
data class AreaPOJO (
@Embedded val areaEntity : AreaEntity
@Relation (
parentColumn = "area_id",
entityColumn = "id"
)
val areaTypeEntity : AreaTypeEntity
)
tables (https://i.stack.imgur.com/bXzl5.png)
的视觉视图所以我假设我将有一个 POJO 用于 UNIT 的关系如下:
data class UnitPOJO (
@Embedded val unitEntity : UnitEntity
@Relation (
parentColumn = "area_id",
entityColumn = "id"
)
val areaEntity : AreaEntity
@Relation (
parentColumn = "pack_id",
entityColumn = "id"
)
val packEntity : PackEntity
@Relation (
parentColumn = "sample_id",
entityColumn = "id"
)
val sampleEntity : SampleEntity
)
使用此 POJO,我可以获得 AreaEntity、SampleEntity、UnitEntity,但我无法获得 UnitPOJO 的 AreaTypeEntity。 当我使用 AreaPOJO 而不是 AreaEntity 时,出现编译错误,提示我对 AreaPOJO 使用“前缀”。当我使用前缀时,这次 AreaPOJO 报错说它找不到关系的列名。
所以我卡住了:) 简而言之,我需要所有五个 table 的所有字段来进行此查询:
"SELECT * FROM UNIT
INNER JOIN AREA ON UNIT.AREA_ID = AREA.ID
INNER JOIN AREA_TYPE ON AREA.AREA_TYPE_ID = AREA_TYPE.ID
INNER JOIN SAMPLE ON UNIT.SAMPLE_ID = SAMPLE.ID
INNER JOIN PACK ON UNIT.PACK_ID = PACK.ID"
首先使用 prefix,这是一个避免歧义列名的选项(例如 哪个 id 列是正确的使用?(修辞)) 但是您随后必须尝试使用查询来包含 AS(隐式或显式)以重命名提取的列。
我建议使用唯一的列名是避免此类歧义的方法。
进入 grandparent/grandchild。
简而言之,您接近了,但是您检索到的是 AreaPOJO(带类型的区域)而不是 AreaEntity,但是您必须告诉 Room 使用 AreaEntity class(因为那是 class用于确定 AreaEntity 的列,然后 AreaPOJO 中的@relation 知道获取 inderlying AreaType)。
因此,尽管未经测试但已成功编译,请考虑以下事项:-
@Entity(tableName = "UNIT")
data class UnitEntity(
@PrimaryKey val id:String,
@ColumnInfo(name = "sample_id") val parentAreaId : String,
@ColumnInfo(name = "area_id") val areaId:Int,
@ColumnInfo(name = "pack_type_id") val packTypeId: Int,
@ColumnInfo(name = "is_active") val isActive:Boolean
)
@Entity(tableName = "AREA_TYPE")
data class AreaTypeEntity(
@PrimaryKey @ColumnInfo(name = "area_type_id") val id:String, //<<<<< unique name
val title:String,
@ColumnInfo(name = "area_type_title") val parentAreaId : String
)
data class AreaPOJO(
@Embedded val areaEntity : AreaEntity,
@Relation(
parentColumn = "area_type_id", //<<<<< changed accrodingly
entityColumn = "area_type_id" //<<<<< changed accordingly
)
val areaTypeEntity : AreaTypeEntity
)
data class UnitPOJO (
@Embedded val unitEntity : UnitEntity,
@Relation (
entity = AreaEntity::class, //<<<<< ADDED
parentColumn = "area_id",
entityColumn = "area_id"
)
val areaWithAreaType : AreaPOJO,
@Relation (
parentColumn = "pack_type_id",
entityColumn = "pack_id"
)
val packEntity : PackEntity,
@Relation (
parentColumn = "sample_id",
entityColumn = "sample_id"
)
val sampleEntity : SampleEntity
)
- 请注意 UnitPOJO 中的
pack_type_id
和pack_id
,而不是示例 reference/relationship 中的sample_id
和sample_id
。- 我建议考虑使用唯一名称,例如pack_type_id是referencing/mapping单元和pack的关系,或许命名单元中的列pack_id_map。因此,列名更具描述性和唯一性。缺点是编码比较多
使用上面的@Query 可以是:-
@Transaction
@Query("SELECT * FROM UNIT")
fun getUnitsWithRelations(): List<UnitPOJO>
- 明显根据Flow/Live数据等调整
也就是说,当 Room 处理它构建的 @Relation
时,上面的代码效率低下,并且每个 @Relation
的基础查询从父级获取所有子级(我相信基于每个父级) .在您的情况下,您似乎有一对多关系,因此可以使用 @Embedded
但查询必须更复杂。
工作示例
以下是基于您的代码的工作示例
- 同时使用
@Relation
和@Embedded
分辨率- @Relation POJO 是 UnitPOJO 和 AreaPOJO
- @Embedded 版本以 Alternative 为前缀
- 添加数据(每行 3 行 table,5 个单位除外)
- 使用两种备选方案提取单位和相关数据
- 包括强制引用和维护完整性的外键约束,请参阅 https://sqlite.org/foreignkeys.html
应该注意的是,我相信您有一些不寻常的和猜测不必要的关系,因此已经进行了一些更改。例如当只需要一个时,您似乎有 Area 与 AreaType 两种方式相关。也就是说,一个 Area 将有一个 AreaType 作为父级,但是如果一个 AreaType 也有一个 Area 作为父级,那么你就会遇到先有鸡还是先有蛋的情况。
- 假设 Area 具有许多可用 AreaType 之一作为父级。
首先是 classes(见评论):-
@Entity(
tableName = "AREA",
/* Enforces/Maintains referential Integrity */
/* i.e does not allow orphans */
foreignKeys = [
ForeignKey(
entity = AreaTypeEntity::class,
parentColumns = ["area_type_id"],
childColumns = ["area_type_id_map" ],
onDelete = ForeignKey.CASCADE /* ????? */,
onUpdate = ForeignKey.CASCADE /* ????? */
)
]
)
data class AreaEntity(
@PrimaryKey @ColumnInfo(name = "area_id")val id:String, //<<<<< unique name
@ColumnInfo(name = "area_title") val title:String,
@ColumnInfo(name = "area_type_id_map") val areaTypeId:String, //<<<<< see Area Type
@ColumnInfo(name = "area_is_active") val isActive:Boolean
)
@Entity(tableName = "SAMPLE")
data class SampleEntity(
@PrimaryKey @ColumnInfo(name = "sample_id") val id:String, //<<<<< unique name
@ColumnInfo(name = "sample_title") val title:String,
)
@Entity(tableName = "PACK")
data class PackEntity(
@PrimaryKey @ColumnInfo(name = "pack_id") val id:String, //<<<<< unique name
@ColumnInfo(name = "pack_title") val title:String, //<<<<< unique name
)
@Entity(
tableName = "UNIT",
foreignKeys = [
ForeignKey(
entity = SampleEntity::class,
parentColumns = ["sample_id"],
childColumns = ["sample_id_map"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = AreaEntity::class,
parentColumns = ["area_id"],
childColumns = ["area_id_map"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
),
ForeignKey(
entity = PackEntity::class,
parentColumns = ["pack_id"],
childColumns = ["pack_id_map"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE
)
]
)
data class UnitEntity(
@PrimaryKey val id:String,
@ColumnInfo(name = "sample_id_map") val sampleId : String,
@ColumnInfo(name = "area_id_map") val areaId:String,
@ColumnInfo(name = "pack_id_map") val packTypeId: String,
@ColumnInfo(name = "unit_is_active") val isActive:Boolean
)
@Entity(
tableName = "AREA_TYPE"
)
data class AreaTypeEntity(
@PrimaryKey @ColumnInfo(name = "area_type_id") val id:String, //<<<<< unique name
@ColumnInfo(name = "area_type_title") val title:String,
/* ???? should an area type have an area as a parent? potential issues if so */
/* commented out
@ColumnInfo(name = "area_type_title") val parentAreaId : String //<<<<< unique name
*/
)
data class AreaPOJO(
@Embedded val areaEntity : AreaEntity,
@Relation(
parentColumn = "area_type_id_map", //<<<<< changed accordingly
entityColumn = "area_type_id" //<<<<< changed accordingly
)
val areaTypeEntity : AreaTypeEntity
)
data class UnitPOJO (
@Embedded val unitEntity : UnitEntity,
@Relation (
entity = AreaEntity::class, //<<<<< ADDED
parentColumn = "area_id_map",
entityColumn = "area_id"
)
val areaWithAreaType : AreaPOJO,
@Relation (
parentColumn = "pack_id_map",
entityColumn = "pack_id"
)
val packEntity : PackEntity,
@Relation (
parentColumn = "sample_id_map",
entityColumn = "sample_id"
)
val sampleEntity : SampleEntity
)
data class AlternativeAreaPOJO (
@Embedded val areaEntity: AreaEntity,
@Embedded val areaTypeEntity: AreaTypeEntity
)
data class AlternativeUnitPOJO (
@Embedded val unitEntity: UnitEntity,
@Embedded val alternativeAreaPOJO: AlternativeAreaPOJO,
@Embedded val packEntity: PackEntity,
@Embedded val sampleEntity: SampleEntity
)
@Dao注解接口AllDao :-
@Dao
interface AllDAO {
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(areaEntity: AreaEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(unitEntity: UnitEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(sampleEntity: SampleEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(packEntity: PackEntity): Long
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(areaTypeEntity: AreaTypeEntity)
@Transaction
@Query("SELECT * FROM UNIT")
fun getUnitsWithRelations(): List<UnitPOJO>
@Query("SELECT * FROM UNIT " +
"INNER JOIN AREA ON UNIT.area_id_map = AREA.area_id " +
"INNER JOIN AREA_TYPE ON AREA.area_type_id_map = AREA_TYPE.area_type_id " +
"INNER JOIN SAMPLE ON UNIT.sample_id_map = SAMPLE.sample_id " +
"INNER JOIN PACK ON UNIT.pack_id_map = PACK.pack_id")
fun getAlternativeUnitsWithRelations(): List<AlternativeUnitPOJO>
}
@Database 注解 class TheDatabase :-
@Database(entities = [
AreaEntity::class,
SampleEntity::class,
PackEntity::class,
UnitEntity::class,
AreaTypeEntity::class
],
version = 1,
exportSchema = false
)
abstract class TheDatabase: RoomDatabase() {
abstract fun getAllDAO(): AllDAO
companion object {
private var instance: TheDatabase? = null
fun getInstance(context: Context): TheDatabase {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase::class.java,
"the_database.db"
)
.allowMainThreadQueries()
.build()
}
return instance as TheDatabase
}
}
}
- 为方便起见,请注意 .allowMainThreadQueries 已被使用。
activity 中的代码(旨在 运行 一次):-
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 p1 = PackEntity("P001","Pack1")
val p2 = PackEntity("P002","Pack2")
val p3 = PackEntity("P003","Pack3")
dao.insert(p1)
dao.insert(p2)
dao.insert(p3)
val s1 = SampleEntity("S001","Sample1")
val s2 = SampleEntity("S002","Sample2")
val s3 = SampleEntity("S003","Sample3")
dao.insert(s1)
dao.insert(s2)
dao.insert(s3)
val at1 = AreaTypeEntity("AT001","AreaType1")
val at2 = AreaTypeEntity("AT002","AreaType2")
val at3 = AreaTypeEntity("AT003","AreaType3",)
dao.insert(at1)
dao.insert(at2)
dao.insert(at3)
val a1 = AreaEntity("A001","Area1",at1.id,true)
val a2 = AreaEntity("A002","Area2",at2.id,false)
val a3 = AreaEntity("A003","Area3",at1.id,true)
dao.insert(a1)
dao.insert(a2)
dao.insert(a3)
dao.insert(UnitEntity("U001",s1.id,a1.id,p1.id,true))
dao.insert(UnitEntity("U002",s2.id,a2.id,p2.id, false))
dao.insert(UnitEntity("U003",s3.id,a3.id,p3.id,true))
dao.insert(UnitEntity("U004",s1.id,a2.id,p3.id,false))
dao.insert(UnitEntity("U005",s3.id,a2.id,p1.id, true))
for(uwr in dao.getUnitsWithRelations()) {
Log.d(TAG,
"Unit is ${uwr.unitEntity.id} " +
"Active = ${uwr.unitEntity.isActive} " +
"Sample is ${uwr.sampleEntity.title} " +
"Area is ${uwr.areaWithAreaType.areaEntity.title} " +
"AreaType is ${uwr.areaWithAreaType.areaTypeEntity.title}"
)
}
for (auwr in dao.getAlternativeUnitsWithRelations()) {
Log.d(TAG,
"Unit is ${auwr.unitEntity.id} " +
"Active is ${auwr.unitEntity.isActive} " +
"Sample is ${auwr.sampleEntity.title} " +
"Area is ${auwr.alternativeAreaPOJO.areaEntity.title} " +
"AreaType is ${auwr.alternativeAreaPOJO.areaTypeEntity.title}"
)
}
}
}
日志的最后结果输出:-
2022-04-05 09:32:40.528 D/DBINFO: Unit is U001 Active = true Sample is Sample1 Area is Area1 AreaType is AreaType1
2022-04-05 09:32:40.528 D/DBINFO: Unit is U002 Active = false Sample is Sample2 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.529 D/DBINFO: Unit is U003 Active = true Sample is Sample3 Area is Area3 AreaType is AreaType1
2022-04-05 09:32:40.529 D/DBINFO: Unit is U004 Active = false Sample is Sample1 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.529 D/DBINFO: Unit is U005 Active = true Sample is Sample3 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.537 D/DBINFO: Unit is U001 Active is true Sample is Sample1 Area is Area1 AreaType is AreaType1
2022-04-05 09:32:40.537 D/DBINFO: Unit is U002 Active is false Sample is Sample2 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.537 D/DBINFO: Unit is U003 Active is true Sample is Sample3 Area is Area3 AreaType is AreaType1
2022-04-05 09:32:40.537 D/DBINFO: Unit is U004 Active is false Sample is Sample1 Area is Area2 AreaType is AreaType2
2022-04-05 09:32:40.537 D/DBINFO: Unit is U005 Active is true Sample is Sample3 Area is Area2 AreaType is AreaType2
- 即两种选择的结果相同,当然关系按预期工作
其实我已经通过这个POJO解决了:
data class UnitPOJO
(
@Embedded val unit: UnitEntity,
@Relation(
parentColumn = "sample_id",
entityColumn = "id"
)
val sampleEntity: SampleEntity,
@Relation(
parentColumn = "pack_type_id",
entityColumn = "id"
)
val pack: PackEntity,
@Relation(
parentColumn = "area_id",
entityColumn = "id",
entity = AreaEntity::class
)
val area: AreaPOJO
)
AreaPOJO 是这样的:
data class AreaPOJO(
@Embedded val areaEntity: AreaEntity,
@Relation(
parentColumn = "area_type_id",
entityColumn = "id"
)
val areaTypeEntity: AreaTypeEntity
)
但我肯定会考虑你的警告@MikeT 关于在使用分层数据时命名 fields/column 名称。如果出现问题或者我得到意想不到的结果,我一定会使用这种方法来解决问题。