Android Room自定义构造器报错:实体与POJO需可用公共构造器
问题背景
在Android Kotlin项目中使用Room创建数据库表实体,编写了Bike实体类并嵌入Sensor类,但编译时出现以下错误:
- Bike类:
Entities and POJOs must have a usable public constructor - Sensor类:同样报构造器不可用的错误
移除Sensor类的@Ignore注解可正常编译,但需要保留这些注解来忽略不需要持久化的字段和方法。尝试将Bike类字段初始化移至次级构造器或init{}块后仍无法解决问题。
相关代码
Bike实体类
@Entity(tableName = "bikes") class Bike(name: String, appearance: Int, id: Int, sensorFid: Int, sensorRid: Int, lowPressThreshF: Int, lowPressThreshR: Int) { var appearance: Int = appearance var name: String = name @Embedded(prefix = "senF_") val sensorFront: Sensor = Sensor(sensorFid, lowPressThreshF) @Embedded(prefix = "senR_") val sensorRear: Sensor = Sensor(sensorRid, lowPressThreshR) @PrimaryKey(autoGenerate = true) val id: Int = id }
Sensor类
class Sensor (_id: Int, _lowPressureTh: Int) { var id: Int var lowPressureTh: Int @Ignore var pressureBar: Int? @Ignore var temperatureC: Int? @Ignore var battery: Byte? init { id = _id lowPressureTh = _lowPressureTh pressureBar = null temperatureC = null battery = null } constructor(id: Int, lowPressureTh: Int, pressureBar: Int?, temperatureC: Int?, battery: Byte?) : this(id, lowPressureTh) @Ignore public fun updateMeasurementFromAdvData(advData: List<Long>) { pressureBar = decodeInt(advData[0].toInt()) temperatureC = decodeInt(advData[1].toInt()) battery = advData[2].toByte() Log.d("Moje", "pressure:" + pressureBar.toString()) Log.d("Moje", "temp:" +temperatureC.toString()) Log.d("Moje", "bat:" +battery.toString()) } @Ignore private fun decodeInt(codedValue: Int) : Int { val buffer = ByteBuffer.allocate(4) buffer.putInt(codedValue) buffer.order(ByteOrder.LITTLE_ENDIAN) buffer.flip() return buffer.int } @Ignore public fun equalId(id3: Int, id2: Int, id1: Int): Boolean { if (id3 <= 0xFF && id2 <= 0xFF && id1 <= 0xFF) { return (id == id3.shl(16).or(id2.shl(8).or(id1))) } else { return false } } @Ignore fun setToNullData() { pressureBar = null temperatureC = null battery = null } }
Dao接口
@Dao interface BikeDao { @Insert fun insertBike(bike: Bike) @Delete fun deleteBike(bike: Bike) @Query("SELECT * FROM bikes") fun selectBikes() : LiveData<List<Bike>> }
编译错误信息
Bike类错误
Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
Tried the following constructors but they failed to match:
Bike(java.lang.String,int,int,int,int,int,int) -> [param:name -> matched field:name, param:appearance -> matched field:appearance, param:id -> matched field:id, param:sensorFid -> matched field:unmatched, param:sensorRid -> matched field:unmatched, param:lowPressThreshF -> matched field:unmatched, param:lowPressThreshR -> matched field:unmatched]
Sensor类错误
Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
Tried the following constructors but they failed to match:
Sensor(int,int) -> [param:_id -> matched field:unmatched, param:_lowPressureTh -> matched field:unmatched]
Sensor(int,int,java.lang.Integer,java.lang.Integer,java.lang.Byte) -> [param:id -> matched field:sensorRear > id, param:lowPressureTh -> matched field:sensorRear > lowPressureTh, param:pressureBar -> matched field:unmatched, param:temperatureC -> matched field:unmatched, param:battery -> matched field:unmatched]
错误原因
- Sensor类问题:主构造器参数
_id、_lowPressureTh与类内字段id、lowPressureTh名称不匹配,Room按名称匹配构造器参数和字段,下划线导致无法识别对应关系。 - Bike类问题:主构造器中的
sensorFid、sensorRid等参数是用来初始化嵌入的Sensor对象的,并非Bike类的直接持久化字段,Room无法识别这种间接初始化的参数,找不到匹配的字段。
修复方案
1. 修复Sensor类
调整主构造器参数名称与字段一致,去掉下划线,让Room能正确匹配参数和字段,同时保留@Ignore注解忽略不需要持久化的内容:
class Sensor(id: Int, lowPressureTh: Int) { var id: Int = id var lowPressureTh: Int = lowPressureTh @Ignore var pressureBar: Int? = null @Ignore var temperatureC: Int? = null @Ignore var battery: Byte? = null // 带测量数据的次级构造器 constructor(id: Int, lowPressureTh: Int, pressureBar: Int?, temperatureC: Int?, battery: Byte?) : this(id, lowPressureTh) { this.pressureBar = pressureBar this.temperatureC = temperatureC this.battery = battery } @Ignore fun updateMeasurementFromAdvData(advData: List<Long>) { pressureBar = decodeInt(advData[0].toInt()) temperatureC = decodeInt(advData[1].toInt()) battery = advData[2].toByte() Log.d("Moje", "pressure:" + pressureBar.toString()) Log.d("Moje", "temp:" + temperatureC.toString()) Log.d("Moje", "bat:" + battery.toString()) } @Ignore private fun decodeInt(codedValue: Int): Int { val buffer = ByteBuffer.allocate(4) buffer.putInt(codedValue) buffer.order(ByteOrder.LITTLE_ENDIAN) buffer.flip() return buffer.int } @Ignore fun equalId(id3: Int, id2: Int, id1: Int): Boolean { if (id3 <= 0xFF && id2 <= 0xFF && id1 <= 0xFF) { return (id == id3.shl(16).or(id2.shl(8).or(id1))) } else { return false } } @Ignore fun setToNullData() { pressureBar = null temperatureC = null battery = null } }
2. 修复Bike类
提供Room可识别的构造器,参数对应所有需要持久化的字段(包括嵌入Sensor的字段),或者直接用主构造器定义所有持久化字段:
@Entity(tableName = "bikes") class Bike( var name: String, var appearance: Int, @Embedded(prefix = "senF_") val sensorFront: Sensor, @Embedded(prefix = "senR_") val sensorRear: Sensor, @PrimaryKey(autoGenerate = true) val id: Int = 0 // autoGenerate=true时,默认0即可,Room会自动生成主键值 ) { // 保留原业务逻辑的次级构造器 constructor( name: String, appearance: Int, id: Int, sensorFid: Int, sensorRid: Int, lowPressThreshF: Int, lowPressThreshR: Int ) : this( name, appearance, Sensor(sensorFid, lowPressThreshF), Sensor(sensorRid, lowPressThreshR), id ) }
修复原理
- Sensor类:主构造器参数与字段名称一致后,Room能正确映射参数到字段,
@Ignore标记的字段和方法会被Room跳过,不参与数据库持久化逻辑。 - Bike类:主构造器直接定义了所有需要持久化的字段(包括嵌入的Sensor对象),Room可以直接识别这些字段并生成对应的数据库表结构;次级构造器保留了原有的业务初始化方式,不影响Room的映射逻辑。
内容的提问来源于stack exchange,提问作者Michal

