Architecture-samples: Error: Entities and Pojos must have a usable public constructor error occurs with Relations.

Created on 13 Oct 2017  路  5Comments  路  Source: android/architecture-samples

I am encountering the issue stated in the title. I have the following entities:

@Entity(tableName = "Person")
data class Person(@PrimaryKey var id: Int,
                  var firstName: String,
                  var surname: String,
                  var age: Int,
                  var numberOfHobbies: Int) {
    @Ignore
    constructor() : this(0, "", "", 0, 0)
}

@Entity(tableName = "Skill")
data class Skill(@PrimaryKey var id: Int,
                 var skillName: String) {
    @Ignore
    constructor() : this(0, "")
}

@Entity(tableName = "PersonSkill")
data class PersonSkill(var personId: Int,
                       var skillId: Int) {
    @Ignore
    constructor() : this(0, 0)

    @field:PrimaryKey(autoGenerate = true)
    var id: Int = 0
}

And the following relationships:

data class SkillWithPersons(
        @Embedded var skill: Skill = Skill(0, "UNKNOWN"),
        @Relation(
                parentColumn = "id",
                entityColumn = "skillId",
                entity = PersonSkill::class,
                projection = arrayOf("personId")
        ) var personIds: List<Int> = emptyList()
) {
        constructor() : this(Skill(0, "UNKNOWN"), emptyList())
}

data class PersonWithSkills(
        @Embedded var person: Person = Person(0, "UNKNOWN", "UNKNOWN", 0, 0),
        @Relation(
                parentColumn = "id",
                entityColumn = "personId",
                entity = PersonSkill::class,
                projection = arrayOf("skillId")
        ) var skillIds: List<Int> = emptyList()
) {
        constructor(): this(Person(0, "UNKNOWN", "UNKNOWN", 0, 0), emptyList())
}

And I have tried everything, and yet it does not work. I keep getting the following error with kotlin-kapt:

e: error: 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).
e: 

e:   Tried the following constructors but they failed to match:
e:   Integer(int) : [value : null]
e:   Integer(java.lang.String) : [s : null]
e: error: 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).
e: 

e: java.lang.IllegalStateException:

I am using the following versions:

Android Studio 3.0 with Gradle 4,
Room: 1.0.0-alpha9-1,
Build tools: 26.0.2,
Kotlin: 1.1.51

It seems there's a bug with the use of @Relation as kotin-kapt seems to no handle it.

Most helpful comment

Thanks @rs146 for reporting this. The problem here is that Room can't find the right constructor for the elements in the relation, when the element is Int (or Integer in Java). I created an issue on our internal tracker for this but chances are this won't be fixed for the 1.0 release, unfortunately.
For now, the workaround is to return List<PersonSkill> instead of List<Int> for the element annotated with @Relation.

All 5 comments

Thanks @rs146 for reporting this. The problem here is that Room can't find the right constructor for the elements in the relation, when the element is Int (or Integer in Java). I created an issue on our internal tracker for this but chances are this won't be fixed for the 1.0 release, unfortunately.
For now, the workaround is to return List<PersonSkill> instead of List<Int> for the element annotated with @Relation.

Hi @florina-muntenescu, I tried that but I'm now getting the following error:

/Users/xyz/git-android/android-sample-sdk/app/build/generated/source/kapt/debug/uk/co/xyz/persistence/dao/PeopleDao_Impl.java:209: error: incompatible types: <null> cannot be converted to int
            _item_1 = new PersonSkill(null,_tmpSkillId);
                                      ^

My PersonWithSkills relation class now has been modified to look like the following:

data class PersonWithSkills(
        @Embedded var person: Person = Person(0, "UNKNOWN", "UNKNOWN", 0, 0),
        @Relation(
                parentColumn = "id",
                entityColumn = "personId",
                entity = PersonSkill::class,
                projection = arrayOf("skillId")
        ) var skillIds: List<PersonSkill> = emptyList()
)

It seems to break in the generated Dao_Impl. My DAO looks like the following:

@Dao
interface PeopleDao {

    @Query("SELECT * FROM Person")
    fun findAllPeople(): Flowable<List<Person>>

    @Insert
    fun savePerson(person: Person)

    @Query("SELECT * FROM Person")
    fun findAllPeopleWithSkills(): Flowable<List<PersonWithSkills>>
}

"Note that the @Relation annotated field cannot be a constructor parameter, it must be public or have a public setter." - from docs
Room first constructs the object and then sets the data.

Got the same error, fixed by upgrading arch lifecycle version from 1.0.0 to 1.1.1, thanks

Moving the @Ignore and @Relation fields to body class solved the issue for me

Was this page helpful?
0 / 5 - 0 ratings