Android Room多表关联查询报错,请求正确实现方案指导
问题描述
尝试用Android Room从数据库获取格式为{registration_number, List<String>, List<String>}的数据时,报错:
Cannot find the parent entity column
area_namein ...
当前实现使用了数据类LastConfiscats和DAO查询,但方式有误,需要修正。
当前代码
LastConfiscats数据类
data class LastConfiscats( @ColumnInfo(name = "registration_number") var slaugh_num: String, // @ColumnInfo(name = "area_name", @Relation(entity = Area::class, parentColumn = "area_name", entityColumn = "name") var areaName: List<String>, // @ColumnInfo(name = "confiscation_name") @Relation(entity = Confiscation::class, parentColumn = "confiscation_name", entityColumn = "name") var confiscationName: List<String> )
DAO查询方法
@Query("SELECT registration_number, area.[name] AS area_name, confiscations.[name] AS confiscation_name " + "FROM car_body, car_body_confiscations" + "INNER JOIN area ON car_body_confiscations.area_id == area.id " + "INNER JOIN confiscations ON car_body_confiscations.confiscation_id == confiscations.id " + "WHERE car_body.id == car_body_confiscations.car_body_id ORDER BY car_body.id DESC LIMIT :row_count") fun getLastConfiscats(row_count: Int): LiveData<List<LastConfiscats>>
关联实体类
@Entity(tableName = "area") data class Area( @PrimaryKey(autoGenerate = true) var id: Int?, var name: String? )
@Entity(tableName = "confiscations") data class Confiscation( @PrimaryKey(autoGenerate = true) var id: Int?, var name: String? )
表关联逻辑:car_body与car_body_confiscations是一对多关系,car_body_confiscations分别与area、confiscations是多对一关系。
错误原因
- @Relation注解用法错误:@Relation是用于实体类之间的关联查询,依赖实体的主键/外键关系,但
LastConfiscats是普通数据类而非Room实体,Room无法识别其中的area_name作为父实体列。 - 查询返回的是扁平结构(每条记录对应一个
registration_number+area_name+confiscation_name),但你期望的是聚合后的分组结构,直接用@Relation无法实现这种内存分组。
正确实现方案
方案1:先查扁平数据,内存分组转换(推荐)
这是最直接的方式,先获取所有扁平记录,再通过Kotlin集合操作分组得到目标格式。
步骤1:定义扁平数据类
data class LastConfiscatsFlat( @ColumnInfo(name = "registration_number") val slaughNum: String, @ColumnInfo(name = "area_name") val areaName: String, @ColumnInfo(name = "confiscation_name") val confiscationName: String )
步骤2:修改DAO查询
@Query("SELECT car_body.registration_number, area.name AS area_name, confiscations.name AS confiscation_name " + "FROM car_body " + "INNER JOIN car_body_confiscations ON car_body.id = car_body_confiscations.car_body_id " + "INNER JOIN area ON car_body_confiscations.area_id = area.id " + "INNER JOIN confiscations ON car_body_confiscations.confiscation_id = confiscations.id " + "ORDER BY car_body.id DESC LIMIT :row_count") fun getLastConfiscatsFlat(row_count: Int): LiveData<List<LastConfiscatsFlat>>
步骤3:转换为目标格式
在Repository或ViewModel中,对LiveData数据进行转换:
fun getLastConfiscats(rowCount: Int): LiveData<List<LastConfiscats>> { return dao.getLastConfiscatsFlat(rowCount).map { flatList -> flatList.groupBy { it.slaughNum }.map { (regNum, items) -> LastConfiscats( slaugh_num = regNum, areaName = items.map { it.areaName }.distinct(), // 去重避免重复数据 confiscationName = items.map { it.confiscationName }.distinct() ) } } }
方案2:基于Room实体关联实现
如果需要基于Room的实体关联设计实现,可按以下步骤操作:
步骤1:完善CarBody实体
@Entity(tableName = "car_body") data class CarBody( @PrimaryKey(autoGenerate = true) val id: Int?, @ColumnInfo(name = "registration_number") val registrationNumber: String )
步骤2:定义交叉引用实体
@Entity( tableName = "car_body_confiscations", primaryKeys = ["car_body_id", "area_id", "confiscation_id"], foreignKeys = [ ForeignKey(entity = CarBody::class, parentColumns = ["id"], childColumns = ["car_body_id"]), ForeignKey(entity = Area::class, parentColumns = ["id"], childColumns = ["area_id"]), ForeignKey(entity = Confiscation::class, parentColumns = ["id"], childColumns = ["confiscation_id"]) ] ) data class CarBodyConfiscationCrossRef( val car_body_id: Int, val area_id: Int, val confiscation_id: Int )
步骤3:定义带关联的数据类
data class CarBodyWithConfiscations( @Embedded val carBody: CarBody, @Relation( parentColumn = "id", entityColumn = "car_body_id", associateBy = Junction(CarBodyConfiscationCrossRef::class) ) val areaList: List<Area>, @Relation( parentColumn = "id", entityColumn = "car_body_id", associateBy = Junction(CarBodyConfiscationCrossRef::class) ) val confiscationList: List<Confiscation> )
步骤4:DAO查询
@Query("SELECT * FROM car_body ORDER BY id DESC LIMIT :row_count") fun getLastCarBodiesWithConfiscations(row_count: Int): LiveData<List<CarBodyWithConfiscations>>
步骤5:转换为目标格式
在Repository或ViewModel中转换数据:
fun getLastConfiscats(rowCount: Int): LiveData<List<LastConfiscats>> { return dao.getLastCarBodiesWithConfiscations(rowCount).map { carBodyList -> carBodyList.map { carBodyWith -> LastConfiscats( slaugh_num = carBodyWith.carBody.registrationNumber, areaName = carBodyWith.areaList.mapNotNull { it.name }, confiscationName = carBodyWith.confiscationList.mapNotNull { it.name } ) } } }
方案选择
- 若仅需聚合后的字符串列表,方案1更简洁高效,避免复杂的实体关联配置。
- 若后续需要操作完整的
Area/Confiscation实体数据,方案2更符合Room的实体关联设计。
内容的提问来源于stack exchange,提问作者Андрій Х
相关产品推荐
相关产品推荐

