116

I'm integrating with the Room persistence library. I have a data class in Kotlin like:

@Entity(tableName = "story")
data class Story (
        @PrimaryKey val id: Long,
        val by: String,
        val descendants: Int,
        val score: Int,
        val time: Long,
        val title: String,
        val type: String,
        val url: String
)

The @Entity and @PrimaryKey annotations are for the Room library. When I try to build, it is failing with error:

Error:Cannot find setter for field.
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.

I also tried providing a default constructor:

@Entity(tableName = "story")
data class Story (
        @PrimaryKey val id: Long,
        val by: String,
        val descendants: Int,
        val score: Int,
        val time: Long,
        val title: String,
        val type: String,
        val url: String
) {
    constructor() : this(0, "", 0, 0, 0, "", "", "")
}

But this doesn't work as well. A thing to note is that it works if I convert this Kotlin class into a Java class with getters and setters. Any help is appreciated!

gsb
  • 5,520
  • 8
  • 49
  • 76
  • 3
    In https://github.com/googlesamples/android-architecture-components/blob/master/BasicRxJavaSampleKotlin/app/src/main/java/com/example/android/observability/persistence/User.kt from google's example, immutable properties works without any problem. Can someone analyze the cause? Could it be a bug? – Samuel Robert Nov 16 '17 at 08:54

20 Answers20

235

Since your fields are marked with val, they are effectively final and don't have setter fields.

Try switching out the val with var. You might also need to initialize the fields.

@Entity(tableName = "story")
data class Story (
        @PrimaryKey var id: Long? = null,
        var by: String = "",
        var descendants: Int = 0,
        var score: Int = 0,
        var time: Long = 0L,
        var title: String = "",
        var type: String = "",
        var url: String = ""
)

EDIT

The above solution is a general fix for this error in Kotlin when using Kotlin with other Java libraries like Hibernate where i've seen this as well. If you want to keep immutability with Room, see some of the other answers which may be more specific to your case.

In some cases immutability with Java libraries is simply not working at all and while making sad developer noises, you have to switch that val for a var unfortunately.

Jan Vladimir Mostert
  • 12,380
  • 15
  • 80
  • 137
  • 1
    Hey I figured something weird about this, Could you have a look at it? https://stackoverflow.com/q/47323883/4620609 – Samuel Robert Nov 16 '17 at 07:29
  • Thanks for the pointer, this issue killed around 2hrs for me.....I used "private var" and it was throwing error, had no clue – anoop4real Apr 26 '18 at 12:37
  • 12
    The answer means 'don't use Room, it forces you to write bad code', right? – Miha_x64 May 15 '18 at 06:55
  • 10
    Please beware that var usage in data class leads to effective immutability loss. – yaroslav May 18 '18 at 17:55
  • This solution worked for me, but for anyone else having the same issue. I also had to remove my private visibility modifier. – The Tokenizer Jun 02 '18 at 10:34
  • No need to make all the fields read/write. Just changing the primary key fixed the issue – juancamilo87 Oct 03 '18 at 18:04
  • Sometimes we design for our entity to be `val` immutable instead of using `var`. Please check my answer [here](https://stackoverflow.com/a/56126801/3763032) for this fix, when you prefer to use `val` over `var` – mochadwi May 14 '19 at 09:17
  • This is not necessary. In my case the error was caused by using the @Ignore annotation on one of the fields. Of course Room doesn't know what to insert there when creating the object from the database. – Miloš Černilovský Jan 09 '20 at 07:56
  • The why isn't clear – ericn Feb 23 '22 at 14:33
60

Hey I don't know if everyone know or not, but you can not have column which is starting from is into Room. For example you can't have like this

   @Entity(tableName = "user")
   data class User (
        @PrimaryKey var id: Long? = null,
        var userName: String = "",
        var isConnectedToFB: Boolean = false,
)
AJay
  • 1,233
  • 10
  • 19
12

If you have @Ignore field in the data class constructor you need to move it to class body like this:

@Entity(primaryKeys = ["id"])
data class User(
        @field:SerializedName("id")
        val id: Int,

        @field:SerializedName("name")
        val name: String,
    
        @field:SerializedName("age")
        val age: Int
) {
    @Ignore
    val testme: String?
}

All kudos go to marianperca on GitHub: https://github.com/android/architecture-components-samples/issues/421#issuecomment-442763610

Milan
  • 900
  • 2
  • 14
  • 25
10

There is an issue in room db library java code generation.

I was using optional field isFavorite. It gives me same error then I change my field name to favorite then compiled.

before var isFavorite: Int? = 0, after changing working fine var favorite: Int? = 0, Thanks

Qamar
  • 4,959
  • 1
  • 30
  • 49
  • Change your data type. I have same field named isFavourite in my db I changed my field type to boolean and now it's worked for me. @ColumnInfo(name = "is_favourite") var isFavourite: Boolean = false – Gevaria Purva Oct 18 '18 at 06:39
10

Just make the variables mutable, change val into var for Kotlin, Or private into public for Java

Yazdan Ilyas
  • 374
  • 4
  • 8
8

Had this error in Java.

You cannot have a column starting with is or is_ in Java.

Try renaming the column.

Another solution:

You either have to pass the field in the constructor and initialize it with the constructor argument, or create a setter for it.

Example:

public MyEntity(String name, ...) {
   this.name = name;
   ...
}

public void setName(String name) {
    this.name = name;
}
live-love
  • 48,840
  • 22
  • 240
  • 204
8

According to https://stackoverflow.com/a/46753804/2914140 if you have an autogenerated primary key, you should write so:

@Entity(tableName = "story")
data class Story (
        val by: String,
        val descendants: Int,
        val score: Int,
        val time: Long,
        val title: String,
        val type: String,
        val url: String
)  {
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0
}

Note that @PrimaryKey is written inside the class body and contains modifier var.

If you later want to update a row in a database with different parameters, use these lines:

val newStory = story.copy(by = "new author", title = "new title") // Cannot use "id" in object cloning
newStory.id = story.id
dao.update(newStory)

UPDATE

I still don't use AndroidX, and Room is 'android.arch.persistence.room:runtime:1.1.1'.

You can extend this class from Serializable. But if you want to extend it from Parcelable, you will get a warning (over id variable): Property would not be serialized inro a 'Parcel'. Add '@IgnoredOnParcel' annotation to remove this warning:

enter image description here

Then I moved an id from the body to the constructor. In Kotlin I use @Parcelize to create Parcelable classes:

@Parcelize
@Entity(tableName = "story")
data class Story (
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0,

    val by: String,
    val descendants: Int,
    val score: Int,
    val time: Long,
    val title: String,
    val type: String,
    val url: String
) : Parcelable
CoolMind
  • 26,736
  • 15
  • 188
  • 224
  • If you do this, you will likely want to override `equals` and `hashcode`. – Tenfour04 Dec 03 '21 at 14:06
  • @Tenfour04, but why? They are generated in `data class` automatically. But I didn't compare two objects. – CoolMind Dec 03 '21 at 15:16
  • 1
    They are only generated for constructor properties. Since you moved the primary key outside the constructor, the `id` is no longer included in the auto-generated `equals` and `hashcode` which would be surprising behavior. – Tenfour04 Dec 03 '21 at 15:26
  • @Tenfour04, agree with you, thanks for the comment. – CoolMind Dec 03 '21 at 15:28
6

This error will be thrown if your column starts with Is:

@ColumnInfo(name = "IsHandicapLeague")
    @NonNull
    var isHandicapLeague: String = "Y"

Add a default set() function to eliminate

fun setIsHandicapLeague(flag:String) {
    isHandicapLeague = flag
}
Sneg
  • 276
  • 2
  • 5
5

The correct way to fix this issue would be simply updating to Room v2.4.3 or higher.

Workaround

If you're running on an older version of Room, one that uses an old version of the kotlinx-metadata-jvm library which doesn't understand 1.5.x metadata, a simple workaround would be adding the following line to your build.gradle:

kapt "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0"

Source: https://youtrack.jetbrains.com/issue/KT-45883/KAPT-Cannot-find-setter-for-field-compiling-projects-with-Room-db-breaks-using-150-M2

Toufic Batache
  • 762
  • 10
  • 24
4

This is a bug and is fixed in Room 2.1.0-alpha01

https://developer.android.com/jetpack/docs/release-notes#october_8_2018

Bug Fixes

  • Room will now properly use Kotlin’s primary constructor in data classes avoiding the need to declare the fields as vars. b/105769985
triad
  • 20,407
  • 13
  • 45
  • 50
4

I've found that another cause of this compilation error can be due to the use of the Room's @Ignore annotation on fields of your entity data class:

@Entity(tableName = "foo")
data class Foo(

    // Okay
    @PrimaryKey
    val id: String,

    // Okay
    val bar: String,

    // Annotation causes compilation error, all fields of data class report
    // the "Cannot find setter for field" error when Ignore is present
    @Ignore
    val causeserror: String
)

The same error also seems to happens when using the @Transient annotation.

I've noticed this issue using version 2.2.2 of Room:

// build.gradle file
dependencies {
   ...
   kapt "androidx.room:room-compiler:2.2.2"
   ...
}

Hope that helps someone!

Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
  • Did you find any solution for this? Stuck with same case. Thanks – Abhishek Batra Apr 18 '20 at 17:53
  • In my case , I needed to remove `@Ignore` from fields on my entity class. Perhaps you have `@Ignore` included on fields on one or more of your entity classes? – Dacre Denny Apr 18 '20 at 23:14
  • Obviously, Room library cannot instantiate an object of your data class, because data class must have a field that does not exist in the database (because of the `@Ignore` annotation). – soshial Jun 19 '20 at 06:58
4

Updating Room library to the latest version 2.4.2 solve the issue

Rami El-bouhi
  • 431
  • 4
  • 9
3

You can try to rename id variable to another name. It worked for me ;

var id: Long? = null

to

var workerId: Long? = null

If you have to name as id and you are using retrofit, then you may need to add SerializedName("id")

mgkhnyldz
  • 51
  • 3
3

It seems like Room and Kotlin versions need to be matched. I have same issue with Room 2.3.0 and Kotlin 1.6.10 but it's ok with Kotlin 1.5.20. It looks ok after I updated Room to 2.4.2. https://youtrack.jetbrains.com/issue/KT-45883

Also there is a possible solution to use @JvmOverloads constructor for better Java compability.

Psijic
  • 743
  • 7
  • 20
2

Another cause of this may be the naming of the field. If you use any of the pre-defined keywords, you will get the same error. For instance, you can not name your column "is_active".

Reference: http://www.sqlite.org/lang_keywords.html

dgngulcan
  • 3,101
  • 1
  • 24
  • 26
1

Just an update if somebody comes across this thread in 2019, after spending hours digging online on why this should work, but it doesn't.

Using val works as expected if you are using the AndroidX version ( androidx.room:room-<any>:2.*) but it doesn't when using the old android.arch.persistence.room:<any>:1.1.1 and it seems that version 2.* wasn't released on this latter repo.

Edit: typos

Jibbo
  • 442
  • 3
  • 13
1

If you want the val immutability available for your entity, it is possible.

  1. You should update to AndroidX room current version.
  2. Check for the related issue here it is marked as Won't Fix
  3. Now they have release a fix related to the issue with version 2.0.0-beta01
  4. Now you can use immutable val with default value e.g:
@Entity("tbl_abc")
data class Abc(
    @PrimaryKey
    val id: Int = 0, 
    val isFavourite: Boolean = false
)

Previously, the above snippet will throw an error of Cannot find setter for field. Changing into var is a great workaround, but I prefer for the entity class to be immutable from outside invocation

mochadwi
  • 1,190
  • 9
  • 32
  • 87
0

You can now start your field with is but you can't have a number next to the is like : is2FooSelected, you have to rename to isTwoFooSelected.

Bubu
  • 1,533
  • 14
  • 14
0

I think that the variable we wrote as id is getting mixed up with the id in the system. Therefore, when I define it as uuid, my error is resolved. I think it will be solved too. Also, try using var instead of val.

@PrimaryKey(autoGenerate = true) var uuid:Int=0

-2

Just use var instead of val and if you are using private keyword, make it public.

@Entity(tableName = "story")
data class Story (
        @PrimaryKey val id: Long,
        var by: String,
        var descendants: Int,
        var score: Int,
        var time: Long,
        var title: String,
        var type: String,
        var url: String
)
Vijay Singh
  • 320
  • 1
  • 3
  • 10