使用Builder模式的Filter实体类在Room中存储失败的问题解决
Room Filter实体类编译错误的原因与修复方案
问题描述
为在Room数据库中创建存储Filter的表,编写了如下实体类:
@Entity class Filter private constructor( val name: String?, @TypeConverters(Converters::class) val sortBy: Sort?, val withGenres: List<Int>?, val withCast: List<Int>?, ) : Serializable { @PrimaryKey(autoGenerate = true) var id: Int? = null data class Builder( var name: String? = null, var sortBy: Sort? = null, var withGenres: List<Int> = listOf(), var withCast: List<Int> = listOf(), ) { fun name(name: String) = apply { this.name = name } fun sortBy(sort: Sort?) = apply { this.sortBy = sort } fun withGenres(genresId: List<Int>) = apply { this.withGenres = genresId } fun withCast(castIds: List<Int>) = apply { this.withCast = castIds } fun build() = Filter(name, sortBy, withGenres, withCast) } /** * Sorting options, default order is Descendant (desc) */ enum class Sort { POPULARITY, RELEASE_DATE, REVENUE, VOTE_AVERAGE; private var order: Order = Order.DESC override fun toString(): String { return this.name.lowercase() + "." + this.order.name.lowercase() } fun setOrder(order: Order) { this.order = order } enum class Order { ASC, DESC } } override fun toString(): String { return "sortBy: ${sortBy.toString()}, withGenres: $withGenres" } }
编译时出现以下错误:
- 无法配置withGenres字段的数据库存储方式
- 无法配置withCast字段的数据库存储方式
- 实体类和POJO必须拥有公共构造函数
- 找不到所有字段的setter方法
错误原因分析
- List
字段无类型转换器 :Room默认不支持直接存储集合类型(如List<Int>),需要自定义TypeConverter实现集合与数据库支持类型(如String)的互转;且@TypeConverters注解放在字段上无效,需放在类或数据库级别。 - 构造函数私有:Room需要通过公共构造函数实例化实体对象,当前
Filter的构造函数是private,导致Room无法创建实例。 - 字段无setter且不可访问:实体类中的
name、sortBy等字段是val(只读),且构造函数私有,Room无法为这些字段赋值,因此提示找不到setter方法。
修复方案与代码
1. 实现List的TypeConverter
创建转换器类,完成集合与字符串的互转,同时为枚举类型添加基础转换逻辑:
class Converters { @TypeConverter fun fromIntList(list: List<Int>?): String? { return list?.joinToString(",") } @TypeConverter fun toIntList(value: String?): List<Int>? { return value?.split(",")?.mapNotNull { it.toIntOrNull() } } @TypeConverter fun fromSort(sort: Filter.Sort?): String? { return sort?.name } @TypeConverter fun toSort(value: String?): Filter.Sort? { return value?.let { Filter.Sort.valueOf(it) } } }
2. 修改Filter实体类
- 将构造函数改为公共,保留Builder模式
- 在类级别添加
@TypeConverters注解,指定使用上述转换器 - 调整字段为
var,确保Room可以赋值
修改后的Filter类:
@Entity @TypeConverters(Converters::class) class Filter( var name: String?, var sortBy: Sort?, var withGenres: List<Int>?, var withCast: List<Int>?, ) : Serializable { @PrimaryKey(autoGenerate = true) var id: Int? = null data class Builder( var name: String? = null, var sortBy: Sort? = null, var withGenres: List<Int> = listOf(), var withCast: List<Int> = listOf(), ) { fun name(name: String) = apply { this.name = name } fun sortBy(sort: Sort?) = apply { this.sortBy = sort } fun withGenres(genresId: List<Int>) = apply { this.withGenres = genresId } fun withCast(castIds: List<Int>) = apply { this.withCast = castIds } fun build() = Filter(name, sortBy, withGenres, withCast) } /** * Sorting options, default order is Descendant (desc) */ enum class Sort { POPULARITY, RELEASE_DATE, REVENUE, VOTE_AVERAGE; private var order: Order = Order.DESC override fun toString(): String { return this.name.lowercase() + "." + this.order.name.lowercase() } fun setOrder(order: Order) { this.order = order } enum class Order { ASC, DESC } } override fun toString(): String { return "sortBy: ${sortBy.toString()}, withGenres: $withGenres" } }
额外说明
如果希望字段保持只读,可以将构造函数参数改为val,但需要添加包含id的全参公共构造函数,或者用@Ignore标记Builder的非Room初始化逻辑。上述方案将字段改为var是更简单直接的实现方式。
内容的提问来源于stack exchange,提问作者Antonio
相关产品推荐
相关产品推荐

