Android Room查询返回父类及Report子类转父类适配PagedList问题
嘿,针对你提出的两个关于Android Room和多态适配PagedList的问题,我来给你梳理下可行的解决方案,都是实战中验证过的思路哦!
Room本身对继承实体的支持需要一点小配置,毕竟它得知道怎么区分不同子类并正确映射数据。假设你的抽象父类是Report,子类比如DailyReport、WeeklyReport都是带@Entity注解的实体类,咱们可以这么搞:
第一步:给父类加个类型标识字段
在抽象的Report里加个reportType字段,用来标记当前实例属于哪个子类。每个子类在构造时要给这个字段赋值,比如DailyReport设为"DAILY",WeeklyReport设为"WEEKLY",这样后续Room就能靠它区分类型了。abstract class Report { var id: Int = 0 var title: String = "" lateinit var reportType: String // 子类必须赋值的类型标识 // 其他通用属性和方法 } @Entity(tableName = "daily_reports") data class DailyReport( @PrimaryKey(autoGenerate = true) override var id: Int = 0, override var title: String = "", override var reportType: String = "DAILY", // 固定标识 // 日报表特有字段 val targetDate: String ) : Report() @Entity(tableName = "weekly_reports") data class WeeklyReport( @PrimaryKey(autoGenerate = true) override var id: Int = 0, override var title: String = "", override var reportType: String = "WEEKLY", // 固定标识 // 周报表特有字段 val weekNumber: Int ) : Report()第二步:写个类型转换器(TypeConverter)
Room本身不知道怎么把数据库行转换成Report的具体子类,所以咱们得写个转换器,根据reportType来实例化对应的子类。如果是跨表查询(比如要同时查日、周报表),可以用SQL的UNION合并结果,再用转换器处理:class ReportTypeConverter { private val gson = Gson() @TypeConverter fun jsonToReport(json: String): Report { val jsonObj = JSONObject(json) return when(jsonObj.getString("reportType")) { "DAILY" -> gson.fromJson(json, DailyReport::class.java) "WEEKLY" -> gson.fromJson(json, WeeklyReport::class.java) else -> throw IllegalArgumentException("未知的报表类型:${jsonObj.getString("reportType")}") } } @TypeConverter fun reportToJson(report: Report): String { return gson.toJson(report) } }第三步:在Database类里注册转换器
给你的@Database注解类加上@TypeConverters(ReportTypeConverter::class),这样Room就会用这个转换器来处理Report类型的映射了。第四步:编写Dao查询
如果是跨表查询返回父类列表,用@RawQuery最灵活:@Dao interface ReportDao { @RawQuery(observedEntities = [DailyReport::class, WeeklyReport::class]) fun getAllReports(): LiveData<List<Report>> }调用的时候传入包含
UNION的SQL语句,比如:SELECT id, title, reportType, targetDate as period FROM daily_reports UNION SELECT id, title, reportType, CAST(weekNumber as TEXT) as period FROM weekly_reports这样就能拿到统一的
List<Report>结果啦。
你已经把Report定义成抽象类来统一属性和方法,现在要适配PagedList,核心就是把不同子类的数据源转换成DataSource.Factory<Int, Report>,这样就能生成PagedList<Report>了,完美利用多态简化代码:
方案1:单个子类数据源转父类
假设你的Dao已经有了各个子类的分页查询方法,比如getDailyReportsPaged()返回DataSource.Factory<Int, DailyReport>,那直接用map操作把它转成父类数据源就行:fun getDailyReportsAsParentPaged(): DataSource.Factory<Int, Report> { return reportDao.getDailyReportsPaged().map { dailyReport -> dailyReport as Report } } fun getWeeklyReportsAsParentPaged(): DataSource.Factory<Int, Report> { return reportDao.getWeeklyReportsPaged().map { weeklyReport -> weeklyReport as Report } }这样不管是日还是周报表,都能得到统一的
DataSource.Factory<Int, Report>,直接用来创建PagedList就好。方案2:合并多子类数据源(如果要混排展示)
如果需要在同一个PagedList里展示日、周、月报表,可以用ConcatDataSource把多个数据源合并起来:fun getAllReportsPaged(): DataSource.Factory<Int, Report> { val dailySource = getDailyReportsAsParentPaged().create() val weeklySource = getWeeklyReportsAsParentPaged().create() val monthlySource = getMonthlyReportsAsParentPaged().create() return ConcatDataSource.Factory(dailySource, weeklySource, monthlySource) }合并后的数据源就能生成包含所有类型报表的PagedList,而且都是
Report父类类型,完全符合你想要的多态优化结构。小提醒
- 要确保各个子类的分页查询有统一的排序规则,不然合并后的PagedList数据顺序会混乱。
- 分页大小(pageSize)的配置要适合所有子类的数据源,避免出现某类数据加载异常的情况。
内容的提问来源于stack exchange,提问作者tomwassing

