1
votes

I have a problem with room.

I'm using retrofit with Gson converter for the rest api, and I'd like to share the pojos with room. In general it works, but in some cases I need to ignore some fields, because I have list of objects. I tried to use the @Ignore annotation, but using it the build process fails with the following errors:

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). public final class Service {

^ error: Cannot find setter for field. private final java.lang.String id = null;

^ error: Cannot find setter for field. private final java.lang.String name = null;

^ error: Cannot find setter for field. private final java.lang.String description = null;

So, using this class, everything works:

@Entity(tableName = "services")
data class Service(

    @PrimaryKey val id: String,
    val name: String,
    val description: String,
    val parentId: String?
)

With this, fails:

@Entity(tableName = "services")
data class Service(

    @PrimaryKey val id: String,
    val name: String,
    val description: String,
    val parentId: String?,
    @Ignore val test: String
)

I'm using this version of room:

implementation 'androidx.room:room-runtime:2.1.0-alpha06'
kapt 'androidx.room:room-compiler:2.1.0-alpha06'

I know that the problem could be fixed using var instead of val and adding a secondary constructor, but I don't want to do that, I prefer to preserve the immutable state of my fields.

Is it a bug of the ignore annotation? Why without it everything works? Any help is appreciated :)

2

2 Answers

2
votes

In your second example Service will be translated to a Java class which has a single constructor with four parameters. Room sees the @Ignore annotations and knows that it needs to bind 3 fields and so it needs a constructor with 3 parameters matching those fields types. As it doesn't find such a constructor(or the default one) it fails.

Try to make the last property optional and use the @JvmOverloads annotation on the constructor:

@Entity(tableName = "services")
data class Service @JvmOverloads constructor(
    @PrimaryKey val id: String,
    val name: String,
    val description: String,
    val parentId: String?,
    @Ignore val test: String? = null
)

This will make the Kotlin compiler to generate a 3 parameter constructor and make Room happy again.

I'm using retrofit with Gson converter for the rest api, and I'd like to share the pojos with room.

You should probably avoid doing this and use two sets of classes to model the API response and the database data. Even in this initial moment you need to make some "hacks" to make everything work, if in the future the API or the database changes you'll need to make more complex changes in more places.

1
votes

In fact, you can retain immutability with a single class and without the @JvmOverloads trick. You simply need the only visible (to Room) constructor to match the fields of the table. For your example, it would be:

@Entity(tableName = "services")
data class Service @Ignore constructor(
    @PrimaryKey val id: String,
    val name: String,
    val description: String,
    val parentId: String?,
    @Ignore val test: String
) {
    constructor(id: String, name: String, description: String, parentId: String?) : 
        this(id, name, description, parentId, /* some default value for test */)
}