Android Room library error: Cannot find setter for field. (Kotlin) - android

I am using room library and I have below mentioned entity:
#Parcelize
#Entity(tableName = "tb_option")
data class OptionsTable(
var question_id: Int? = null,
var option_id: Int? = null,
var option: String? = null,
var is_selected: Int? = null,
#PrimaryKey(autoGenerate = true)
var sr_no: Int = 0) : Parcelable
as you can see I have all the field declared as var but it is still showing error as:
error: Cannot find setter for field.
e:
e: private java.lang.Integer is_selected;
e:
^
please suggest some fix for this.
Thanks

Most of the time issue is occurring because of the following:
Problem 1:
Final field: Fields are marked with val, they are effectively final and don't have setter fields.
Solution: Replace the fields val with var. You might also need to initialize the fields.
Problem 2:
is keyword: We cannot use sqllite reserved keywords line for fields naming source e.g.
The following will cause error
#ColumnInfo(name = "IS_ACTIVE") var isActive
Solution: The solution is:
#ColumnInfo(name = "IS_ACTIVE") var active

I removed the initialization of sr_no from
#PrimaryKey(autoGenerate = true)
var sr_no: Int = 0
and the final code is:
#PrimaryKey(autoGenerate = true)
var sr_no: Int
worked for me because it was an auto-generated field.

There is an issue in room orm library with kotlin java code generation.
My optional field isFavorite and compile time same error then I change my field name to favorite then compiled.
before
var isFavorite: Int? = 0,
working fine:
var favorite: Int? = 0,
Thanks

Related

How to retrieve a parcelable object stored in Firebase (Kotlin) [duplicate]

https://github.com/neuberfran/JThings/blob/main/app/src/main/java/neuberfran/com/jfran/model/FireFran.kt
I have this POJO above with the error mentioned in the topic. I know it is a mistake already mentioned here, but I have tried several classes (besides this one) and I have not been successful, since my model/POJO class (and Code implementation) is different from several that I saw:(Every help is welcome)
Could not deserialize object. Class does not define a no-argument
constructor. If you are using ProGuard, make sure these constructors
are not stripped (found in field 'value')
Change made to the garagem document, exchanged value for valorb, etc...
The error is very clear, your class "FireFran" doesn't have a no-argument constructor. When you try to deserialize an object from Cloud Firestore, the Android SDKs require that the class must have a default no-arg constructor and also setters that map to each database property.
In Kotlin, the data classes don't provide a default no-arg constructor. So you need somehow ensure the compiler that all the properties have an initial value. You can provide to all of the properties an initial value of null or any other value you find more appropriate.
So your "FireFran" might look like this:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var value: FireFranValue? = null //Newly added
) {
//var value: FireFranValue = FireFranValue(false, 0)
companion object Factory {
fun create() :FireViewModel = FireViewModel()
var COLLECTION = "device-configs"
var DOCUMENT = "alarme"
var FIELD_userId = "userId"
}
}
Now adding the properties in the constructor, Kotlin will automatically generate a default no-arg constructor. In this way, the Firebase Android SDK will be able to use. It will also generate setters for each property. Please see that each property is var and not a val, and provides a default null value in case of "id" and "userId".
If don't make this change, you won't be able to use automatic deserialization. You'll have to read the value for each property out of the DocumentSnapshot object and pass them all to Kotlin's constructor.
Edit:
In your screenshot, the "value" property is on an object of type "FireFranValue", which has only two properties, "brightness" and "on". To be able to read the data under "value", your "FireFran" class should contain a new property of type "FireFranValue". Please check above the class.
If you don't want to use automatic deserialization, you can get the value of each property individually. For example, you can get the value of the "userId" property using DocumentSnapshot's getString(String field) method:
val userId = snapshot.getString("userId")
Edit2:
Your three classes should look like this:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var owner: String? = null,
var value: FireFranValue = FireFranValue(false),
var valorb: FireFranValueB = FireFranValueB(openPercent = 0)
)
data class FireFranValue(
var on: Boolean // = false
)
data class FireFranValueB(
var openPercent: Number // = 0
)
Edit3:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var owner: String? = null,
var value: FireFranValue? = null,
var valorb: FireFranValueB? = null
)
data class FireFranValue(
var on: Boolean? = null
)
data class FireFranValueB(
var openPercent: Number? = null
)
Now, "on" and "openPercent" will get the values from the database.
Please check the following class declaration:
You can define a no-arg constructor in kotlin like this:
data class SampleClass(
val field1: String,
val field2: String
) {
constructor(): this("", "")
}
First thing: It was not necessary to create the field (map type) called valorb. It was resolved with value.openPercent
As I have two documents (alarme and garagem) I created two POJO classes (FireFran and FireFranB)
https://github.com/neuberfran/JThingsFinal/blob/main/app/src/main/java/neuberfran/com/jfran/model/FireFranB.kt
The secret was to treat the value.on and value.openPercent fields as maps (as facts are):

Kotlin 1.5.20 build issue

Since I updated from Kotlin 1.5.10 to 1.5.20 I am having a build issue due to models.
I am getting : Note: PrimaryKey[childId] is overridden by PrimaryKey[parentId]
My objects are :
#Entity
class Parent : Child() {
#ColumnInfo(name = "parent_id")
#PrimaryKey(autoGenerate = true)
var parentId: Int? = null
#SerializedName("name")
var name: String? = null
...
}
and
#Entity
open class Child(
#ColumnInfo(name = "child_id")
#PrimaryKey(autoGenerate = true)
var childId: Int? = null,
#SerializedName("description")
var description: String? = null
...
) : Serializable {...}
Note that with Kotlin 1.5.10 I have no build issue.
I tried to remove the #PrimaryKey of the child but I get another error saying #Entity must have a PrimaryKey
Am I missing something? Thanks for your help
Because in the same build I also had issues about expected #androidentrypoint to have a value I have done some research about it and found this:
Expected #AndroidEntryPoint to have a value. Did you forget to apply the Gradle Plugin?
Leading to https://stackoverflow.com/a/68141101/9606199
and to https://github.com/google/dagger/issues/2684#issuecomment-860250964
So I tried it and it worked (still having this error but the build is now ok).
It looks like it is a bug in Kotlin 1.5.20

Could not deserialize object. Class does not define a no-argument constructor.If you are using ProGuard, make sure these constructors are not stripped

https://github.com/neuberfran/JThings/blob/main/app/src/main/java/neuberfran/com/jfran/model/FireFran.kt
I have this POJO above with the error mentioned in the topic. I know it is a mistake already mentioned here, but I have tried several classes (besides this one) and I have not been successful, since my model/POJO class (and Code implementation) is different from several that I saw:(Every help is welcome)
Could not deserialize object. Class does not define a no-argument
constructor. If you are using ProGuard, make sure these constructors
are not stripped (found in field 'value')
Change made to the garagem document, exchanged value for valorb, etc...
The error is very clear, your class "FireFran" doesn't have a no-argument constructor. When you try to deserialize an object from Cloud Firestore, the Android SDKs require that the class must have a default no-arg constructor and also setters that map to each database property.
In Kotlin, the data classes don't provide a default no-arg constructor. So you need somehow ensure the compiler that all the properties have an initial value. You can provide to all of the properties an initial value of null or any other value you find more appropriate.
So your "FireFran" might look like this:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var value: FireFranValue? = null //Newly added
) {
//var value: FireFranValue = FireFranValue(false, 0)
companion object Factory {
fun create() :FireViewModel = FireViewModel()
var COLLECTION = "device-configs"
var DOCUMENT = "alarme"
var FIELD_userId = "userId"
}
}
Now adding the properties in the constructor, Kotlin will automatically generate a default no-arg constructor. In this way, the Firebase Android SDK will be able to use. It will also generate setters for each property. Please see that each property is var and not a val, and provides a default null value in case of "id" and "userId".
If don't make this change, you won't be able to use automatic deserialization. You'll have to read the value for each property out of the DocumentSnapshot object and pass them all to Kotlin's constructor.
Edit:
In your screenshot, the "value" property is on an object of type "FireFranValue", which has only two properties, "brightness" and "on". To be able to read the data under "value", your "FireFran" class should contain a new property of type "FireFranValue". Please check above the class.
If you don't want to use automatic deserialization, you can get the value of each property individually. For example, you can get the value of the "userId" property using DocumentSnapshot's getString(String field) method:
val userId = snapshot.getString("userId")
Edit2:
Your three classes should look like this:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var owner: String? = null,
var value: FireFranValue = FireFranValue(false),
var valorb: FireFranValueB = FireFranValueB(openPercent = 0)
)
data class FireFranValue(
var on: Boolean // = false
)
data class FireFranValueB(
var openPercent: Number // = 0
)
Edit3:
class FireFran(
var alarmstate: Boolean = false,
var garagestate: Boolean = false,
var id: String? = null,
var userId: String? = null,
var owner: String? = null,
var value: FireFranValue? = null,
var valorb: FireFranValueB? = null
)
data class FireFranValue(
var on: Boolean? = null
)
data class FireFranValueB(
var openPercent: Number? = null
)
Now, "on" and "openPercent" will get the values from the database.
Please check the following class declaration:
You can define a no-arg constructor in kotlin like this:
data class SampleClass(
val field1: String,
val field2: String
) {
constructor(): this("", "")
}
First thing: It was not necessary to create the field (map type) called valorb. It was resolved with value.openPercent
As I have two documents (alarme and garagem) I created two POJO classes (FireFran and FireFranB)
https://github.com/neuberfran/JThingsFinal/blob/main/app/src/main/java/neuberfran/com/jfran/model/FireFranB.kt
The secret was to treat the value.on and value.openPercent fields as maps (as facts are):

Kotlin Room Error "Cannot figure out how to save this field into database" With TypeConverters

I have the following Kotlin class that I'm using as a Room entity
#Entity(tableName = "subscriptions")
data class Subscription(#ColumnInfo(name = "title")
var name: String,
#ColumnInfo(name = "description")
var description: String,
#ColumnInfo(name = "price")
var price: Float,
#ColumnInfo(name = "payment_method")
var paymentMethod: String,
#ColumnInfo(name = "frequency")
var frequencyNum: Int,
#ColumnInfo(name = "interval")
#TypeConverters(FrequencyTypeConverter::class)
var frequencyType: FrequencyType,
#ColumnInfo(name = "start_date")
#TypeConverters(DateTypeConverter::class)
var startDate: Date,
#PrimaryKey(autoGenerate = true)
val id: Int = 0) : Comparable<Subscription>
however, when I build I get the following error:
private com.owenlejeune.subscriptions.model.FrequencyType frequencyType;
^/Volumes/Data/Code/HonorsProject/ModernSubscriptions/app/build/tmp/kapt3/stubs/debug/com/owenlejeune/subscriptions/model/Subscription.java:29: error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
My FrequencyType converter class looks like
class FrequencyTypeConverter {
#TypeConverter
fun fromFrequencyType(value: FrequencyType): Int = value.ordinal
#TypeConverter
fun toFrequencyType(value: Int): FrequencyType = FrequencyType.values()[value]
}
Any ideas why I'm still receiving this error?
I'm going to have to disagree with the answer from #DivijGupta, you can put the #TypeConverters annotation on entity fields and it will limit the scope of the converter to that particular field as I am sure you intended. The documentation here actually lists the usage as:
If you put it on an Entity field, only that field will be able to use it.
The error is actually to do with how Kotlin translates the annotations into Java bytecode. If you have a look at what adding an annotation to a field actually does in the decompiled code, you can see that Kotlin places it on the constructor parameter, not the field itself. Therefore as Room doesn't see an annotation on the field it gives you the error.
To fix this you have to use an extra field qualifier when annotating, in this instance:
#field:TypeConverters(FrequencyTypeConverter::class)
and you can read more about this in the Kotlin documentation on Annotation use-site targets.
Just as a side note, the answer from #DivijGupta would fix the error as the problem that I have outlined wouldn't exist, but it is just a workaround.
You need to add #TypeConverters annotation to your database class, not in the entity class.

Cannot find setter for field - using Kotlin with Room database

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!
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.
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,
)
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
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
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:
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
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;
}
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
}
Just make the variables mutable, change val into var for Kotlin, Or private into public for Java
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
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!
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")
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
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.
Updating Room library to the latest version 2.4.2 solve the issue
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
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
If you want the val immutability available for your entity, it is possible.
You should update to AndroidX room current version.
Check for the related issue here it is marked as Won't Fix
Now they have release a fix related to the issue with version 2.0.0-beta01
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
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.
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
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
)

Categories

Resources