Room Database error with Kotlin Data Class - android

I've been moving into using Room, and I've run into a blocking issue. I've gone through and fixed all of the compile-time checks from the Room library, but am now encountering the following 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).
This appears twice at compile time with no evidence of which class this comes from, but I was able to figure out (by removing classes from the Database) that this was one of the files. I'm assuming it has something to do with the Primary Key being a string instead of an Int (this is one of two classes that uses this), but nothing in the documentation indicates what the issue would be, and in fact the documentation shows that strings are valid Primary Keys.
#Entity(tableName = "inspections")
data class Inspection(
#SerializedName("id")
var id: Int = 0,
...
// Rest of code left off for brevity, found to not be related to the issue.
I've tried a few things to try and get around this.
Remove the data attribute of this class to make it a normal POKO
Remove the variables from the default constructor, and place them into the class
Remove the Ignore from the empty constructor (note, this causes a different issue, Room cannot pick a constructor since multiple constructors are suitable - the Ignore annotation on a default constructor gets around this.) This is the part which perplexes me the most - removing this says "multiple constructors are valid", keeping it says "no constructors are valid".
Updated: Adding a few more relevant code snippets from my project.
build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
.....
implementation 'android.arch.persistence.room:runtime:1.0.0-alpha9-1'
implementation 'android.arch.persistence.room:rxjava2:1.0.0-alpha9-1'
kapt 'android.arch.persistence.room:compiler:1.0.0-alpha9-1'
Database class
#Database(entities =
arrayOf(Account::class, Category::class,
Inspection::class, InspectionForm::class,
InspectionFormItem::class, InspectionFormsStructure::class,
InspectionItemPhoto::class,
InspectionItem::class, LineItem::class,
LocalPhoto::class, Rating::class,
Structure::class, SupervisoryZone::class,
Upload::class, User::class),
version = 16)
#TypeConverters(Converters::class)
abstract class OrangeDatabase : RoomDatabase() {
abstract fun inspectionDao(): InspectionDao
abstract fun localDao(): LocalDao
abstract fun ratingsDao(): RatingsDao
abstract fun structureZoneDao(): StructureZoneDao
abstract fun userAccountDao(): UserAccountDao
}
Converters
class Converters {
#TypeConverter
fun fromTimestamp(value: Long?): Date? {
return if (value == null) Date() else Date(value)
}
#TypeConverter
fun dateToTimestamp(date: Date?): Long? {
return date?.time ?: 0
}
#TypeConverter
fun fromStringToArray(value: String?): Array<String>? {
return value?.split(",")?.toTypedArray() ?: arrayOf()
}
#TypeConverter
fun stringToStringArray(strings: Array<String>?): String? {
return strings?.joinToString(",") ?: ""
}
}
Another data class
#Entity(tableName = "users")
data class User(
#PrimaryKey
#SerializedName("id")
var id: Int = 0,
...
// Rest of code left off for brevity, found to not be related to the issue.
UserPermissions class:
data class UserPermissions(
#SerializedName("id")
var pid: Int = 0,
...
// Rest of code left off for brevity, found to not be related to the issue.

The problem in your case is, that if you have nullable values Kotlin will generate several constructors for each possible constructor.
That means that you have to define a default constructor and fill it with default values.
If you want to have another one which should be ignored you should make sure to use the parent constructor with all those parameters.
Example:
#Entity(tableName = "inspections")
data class Inspection(
#SerializedName("id")
var id: Int = 0,
#PrimaryKey
#SerializedName("guid")
var guid: String = "",
#SerializedName("score")
var score: Double = 0.0,
#SerializedName("notification_sent_at")
var notificationSentAt: Date = Date(),
var wasUploaded: Boolean = false) {
#Ignore
constructor() : this(0, "", 0.0, Date(), false)
}
In this case only two constructors will be generated "under the hood". If you have nullable values you will have all possible constructors available.
Example:
data class Test(var id: Int = 0, var testString: String? = null, var testBool : Boolean? = null) {
constructor(0)
}
generates
constructor(var id:Int)
constructor() : this(0)
constructor(var id:Int, var testString: String)
constructor(var id:Int, var testBool: Boolean)
constructor(var id:Int, var testString: String, var testBool : Boolean)
// .. and so on
Since you'r looking for an official documentation, you may want to look at Overloads Generation.
After testing your class which works flawlessly i found in another post that you have to check if you used apply plugin: 'kotlin-kapt' in your Gradle.
Double check that you've valid type converters for your Date class. I wrote that issue longer time ago.
After recoding your stuff above it worked just fine by adding a UserPermissions class like that:
data class UserPermissions(var permissionid: String)
Edit: After using your UserPermission class everything worked just fine. Please take care if you use the proper import (util.Date instead of sql.Date for example).
Another problem is that your using an old very buggy library of room.
The current version (while writing this) is
implementation "android.arch.persistence.room:runtime:1.0.0-beta2"
kapt "android.arch.persistence.room:compiler:1.0.0-beta2"
implementation "android.arch.persistence.room:rxjava2:1.0.0-beta2"
I wrote an issue long time ago

The issue was extremely difficult to debug and harder to reproduce, but I found the issue. I was using an #Embedded object, but the result that was going in was actually a List of that object. This was giving trouble to the automatic Embed task, and there wasn't a perfect Converter that could be written for it.
#SerializedName("range_choices")
#Embedded
var rangeChoices: List<RangeChoice>? = null,
I had to annotate that with #Ignore and instead, I'll be saving the results of this list to its own table, now the new table range_choices.

Your Primary keys should be like given below using Annotation Use-site Targets
#field:PrimaryKey #field:SerializedName("guid") var guid: String = ""
and
#field:PrimaryKey #field:SerializedName("id") var id: Int = 0

Try to avoid using nullable values and make everything have some kind of default value. That's the simpliest way to solve this issue.
If you really want to use them, then you may create a constructor, containing all of them.

Related

Saving complex Class data in Room - "Cannot figure out how to save this field into database. You can consider adding a type converter for it"

I started to learn Room and I'm facing an issue:
Given two classes, one is a Car, and the other one is an Engine iside a Car.
#Entity
class Car{
#PrimaryKey
var id = 0
var name: String? = null
var engine: Engine? = null
}
...
#Entity
class Engine{
#PrimaryKey
var id = 0
var manufacturer: String? = null
}
I also have these classes initalized to tables in my AppDatabase class.
#Database(entities = [Car::class, Engine::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
//...
}
The problem is whenever I simply want to run the project I get the following error message which points to the Car's engine field:
Cannot figure out how to save this field into database. You can consider adding a type converter for it.
Is there no simple way for this? I'm looking for something which saves my data with the least amount of code, like Firestore which do all the work with simple annotations.
Thanks in advance.
AS a car would only have a single engine and that you have a table for the engine as well as a table for the car. Then you have a 1 to many relationship. That is a car can have an engine but the same engine can be used by many cars.
So instead of trying to embed the engine within the car you make a relationship, the car (the child) referencing the engine (the parent).
This is as simple as changing the Car to be:-
#Entity
class Car{
#PrimaryKey
var id = 0
var name: String? = null
var engine: Int? = null
}
An alternative, that would not need the relationship nor a TypeConverter would be to not have the Engine as a table but to use the #Embedded annotation prior to the engine. e.g.
#Entity
class Car{
#PrimaryKey
var id = 0
var name: String? = null
#Embedded
var engine: Engine? = null
}
...
class Engine{
#PrimaryKey
#ColumnInfo(name = "engineId")
var id = 0
var manufacturer: String? = null
}
the name of the column used to store the Engine's id changed as otherwise there would be 2 columns with the same name.
Note that with this way there is no need for the #Entity annotation as you are storing the Engine values within the Car.
This is not considered good practice as if the same engine is used by many cars then you are duplicating data and thus that it is not normalised.
The third and least desirable way from a database perspective is to store a representation of the engine object in a single column. That is to convert the object into a singular storable representation. Typically a JSON string. Thus you need code (a function) to convert from the object to the single value (JSON string) and (another function) to convert from the JSON String to the Object.
With this method not only are you not normalising the data but additionally you end up storing the bloat required to enable the object to be represented. That bloat, from a database, perspective, obfuscating the actual useful stored data to some extent.
In addition there is not a single set/standard library providing the functionality of converting objects to/from JSON, so you have to select a flavour and then include that library in the project.
Here is a class that contains Type Converters that could be used (see comment re library):-
class CarAndEngineTypeConverters{
/* Using Library as per dependency implementation 'com.google.code.gson:gson:2.10.1' */
#TypeConverter
fun convertEngineToJSONString(engine: Engine): String = Gson().toJson(engine)
#TypeConverter
fun convertJSONStringToEngine(jsonString: String): Engine = Gson().fromJson(jsonString,Engine::class.java)
}
This would suit your original classes.
Room needs to be told to use these classes (it works out when) via a #TypeConverters annotation (note the plural and not singular) this it immediately before or after the #Database annotation has the highest level of scope. The annotation itself could be #TypeConverters(value = [CarAndEngineTypeConverters::class])
To demonstrate all three together consider this over the top Car class:-
#Entity
class Car{
#PrimaryKey
var id = 0
var name: String? = null
var engine: Int? = null
#Embedded
var alternativeEngine: Engine? = null
var jsonConvertedEngine: Engine? = null
}
Over the top as the engine is stored 3 times (could be different engines)
The *Engine class
#Entity
class Engine{
#PrimaryKey
#ColumnInfo(name = "engineId")
var id = 0
var manufacturer: String? = null
}
The Type Converters as above.
With the above in place and using within an activity (noting that for brevity/convenience .allowMainThreadQueries has been used):-
db = TheDatabase.getInstance(this)
carAndEngineDAO = db.getCarAndEngineDAO()
var engine1 = Engine()
engine1.manufacturer = "Ford"
engine1.id = carAndEngineDAO.insert(engine1).toInt()
var car1 = Car()
car1.name = "Escort"
car1.engine = engine1.id /* id of the engine */
car1.alternativeEngine = engine1
car1.jsonConvertedEngine = engine1
carAndEngineDAO.insert(car1)
Using Android Studios App inspection the view the database then
The Columns id and name and obviously as expected
The engine column contains the value 0, this is the id of the respective engine in the engine table (maximum 8 bytes to store the id)
The JsonConvertedEngine column stores the JSON representation of the Engine (31 bytes)
The engineId column and manufacturer column stores the respective values (12 bytes).
The Engine Table (only needed for the relationship) is :-
You should use TypeConverters:
At first add this dependency to your project to convert Engine to Json and vice versa
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
Now you should create an Object class that convert Engine to Json. This class make Engine understandable for Room :
object CommonTypeConverters {
#TypeConverter
#JvmStatic
fun stringToEngine(value: String): Engine = fromJson(value)
#TypeConverter
#JvmStatic
fun engineToString(items: Engine?): String = toJson(items)
inline fun <reified T> toJson(value: T): String {
return if (value == null) "" else Gson().toJson(value)
}
inline fun <reified T> fromJson(value: String): T {
return Gson().fromJson(value, object : TypeToken<T>() {}.type)
}
In the end Engine is not a entity and you should add #Typeconverter annotation to your database class :
#Database(entities = [Car::class], version = 1)
#TypeConverters(CommonTypeConverters::class)
abstract class AppDatabase : RoomDatabase() {
//...
}

Fixing One Error Leads to Another Error - Endless Error Loop in Room

I am stuck in a sticky situation here. I'm looking to add Room to my app, but I keep getting the following error whenever I try and run my code:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.realtomjoney.pyxlmoose/com.realtomjoney.pyxlmoose.activities.main.MainActivity}: java.lang.RuntimeException: cannot find implementation for com.realtomjoney.pyxlmoose.database.PixelArtDatabase. PixelArtDatabase_Impl does not exist
To fix this, a person suggested to change the following line in the build.gradle file:
(Old.)
annotationProcessor "androidx.room:room-compiler:2.3.0"
(New.)
kapt "androidx.room:room-compiler:2.3.0"
After looking through all the suggestions, this was the only one that could fix the error.
This fixed my problem! Hooray!
Except, whenever I run this code I get a different error:
C:\Users\TO\AndroidStudioProjects\PyxlMoose\app\build\tmp\kapt3\stubs\debug\com\realtomjoney\pyxlmoose\dao\PixelArtCreationsDao.java:13: error: Type of the parameter must be a class annotated with #Entity or a collection/array of it.
kotlin.coroutines.Continuation<? super kotlin.Unit> continuation);
^
Okay, so to fix this someone recommended I change the following line in the build.gradle file:
(Old.)
kapt "androidx.room:room-compiler:2.3.0"
(New.)
annotationProcessor "androidx.room:room-compiler:2.3.0"
After looking through all the suggestions, this was the only one that could fix the error.
And, whenever I run this code I get stuck to the error which I had before. I am unfortunately stuck in this neverending error loop, and I'm not sure which error to solve or what to do about it as solving one error leads to the same error and solving that one brings us to the exact place we were in from the very beginning.
The only possible way I thought to get out of this loop is to find a different solution for one of the two errors (which has been hard to find so far) which won't cause another error whose solution will bring us back to the same error as before, if anyone could assist that would be great.
PixelArts Model:
#Entity
data class PixelArts(
#ColumnInfo(name = "item_bitmap") var bitmap: String,
#ColumnInfo(name = "item_title") var title: String,
#ColumnInfo(name = "item_pixel_data") var pixelData: String,
#ColumnInfo(name = "item_favourited") var favourited: Boolean,
#ColumnInfo(name = "item_date_created") var dateCreated: String = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now())) {
#PrimaryKey(autoGenerate = true) var objId = 0
}
Dao:
#Dao
interface PixelArtCreationsDao {
#Insert
suspend fun insertPixelArt(pixelArt: PixelArts)
#Query("SELECT * FROM PixelArts ")
fun getAllPixelArtCreations(): List<PixelArts>
#Query("DELETE FROM PixelArts WHERE objId=:pixelArtId")
fun deletePixelArtCreation(pixelArtId: Int)
#Query("UPDATE PixelArts SET item_pixel_data=:pixelData AND item_bitmap=:bitmap WHERE objId=:pixelArtId")
fun updatePixelArtCreation(pixelArtId: Int, bitmap: String, pixelData: String)
}
Database:
#Database(entities = [PixelArts::class], version = 1)
abstract class PixelArtDatabase: RoomDatabase() {
abstract fun pixelArtCreationsDao(): PixelArtCreationsDao
companion object {
var instance: PixelArtDatabase? = null
fun getDatabase(context: Context): PixelArtDatabase? {
if (instance == null) {
synchronized(PixelArtDatabase::class) {
instance = Room.databaseBuilder(
context.applicationContext,
PixelArtDatabase::class.java,
AppData.dbFileName).build()
}
}
return instance
}
}
}
AppData:
package com.realtomjoney.pyxlmoose.database
class AppData {
companion object {
var dbFileName = "pixel_art_db"
lateinit var db: PixelArtDatabase
}
}
If you find an issue with this question, or you think for some reason it's not well written then tell me in the comments the exact problem. Don't be an elitist.

Kotlin : implenting an immutable class through the data class method but making sure the input values are clean

I'm new to coding in kotlin and want to implement an immutable class that represents a project with various fields inside.
The easiest way to do this is by using a data class and using the copy() method so that anytime one of the app user modifies a field it results in the backend in a call to the copy method with the modified field producing the new project.
My problem is that this way does not allow for prior checking of parameters (eg : limit string size of the owner, making sure the number of people added to the project is reasonable etc).
If this was java, I'd use a builder pattern but this seems to defeat the purpose of kotlin, and i've read articles that are positive to using builders in kotlin (https://www.baeldung.com/kotlin/builder-pattern)
and others that are completely against (https://code-held.com/2021/01/23/dont-use-builder-in-kotlin/).
I haven't found any way to "modify" the copy method and to add the parameter sanitization checks that are needed for each parameter. I would appreciate any "smooth" idea to implement this, if anybody has found it. The goal would also be to throw exeptions/sealed classes variables so that the app UI can tell the user what went wrong instead of a generic error message just mentioning that the project was not modified.
I agree with the second link. If you look at the comments on the Baeldung article, you'll see even they were convinced and pledged to revise the article.
You can throw exceptions in an init block but if these are exceptions that are not caused by programmer error, it would be more Kotlin-idiomatic to expose a single constructor-like function that returns a wrapper or just null for invalid input.
Examples:
data class Person(val name: String, val age: Int = 0) {
init {
if (age < 0) {
throw IllegalArgumentException("Age $age is less than 0.")
}
}
}
If you want to return a wrapper or nullable, a data class isn't suitable for preventing invalid input because the generated copy() function will always return a fully constructed object. Sadly, Kotlin does not support overriding the generated copy() function.
sealed class Result<T>
data class Success<T>(val value: T): Result<T>()
data class Failure<T>(val reason: String): Result<T>()
class Person private constructor(val name: String, val age: Int = 0) {
companion object {
fun build(name: String, age: Int = 0): Result<Person> {
return when {
age < 0 -> Failure("Age $age is less than 0.")
else -> Success(Person(name, age))
}
}
}
fun buildCopy(name: String = this.name, age: Int = this.age) = build(name, age)
}

Firestore - how to exclude fields of data class objects in Kotlin

Firestore here explains, how I can use simple classes to directly use them with firestore: https://firebase.google.com/docs/firestore/manage-data/add-data
How can I mark a field as excluded?
data class Parent(var name: String? = null) {
// don't save this field directly
var questions: ArrayList<String> = ArrayList()
}
I realize this is super late, but I just stumbled upon this and thought I could provide an alternative syntax, hoping someone will find it helpful.
data class Parent(var name: String? = null) {
#get:Exclude
var questions: ArrayList<Child> = ArrayList()
}
One benefit to this is that, in my opinion, it reads a little clearer, but the main benefit is that it would allow excluding properties defined in the data class constructor as well:
data class Parent(
var name: String? = null,
#get:Exclude
var questions: ArrayList<Child> = ArrayList()
)
Since Kotlin creates implicit getters and setters for fields, you need to annotate the setter with #Exclude to tell Firestore not to use them. Kotlin's syntax for this is as follows:
data class Parent(var name: String? = null) {
// questions will not be serialized in either direction.
var questions: ArrayList<Child> = ArrayList()
#Exclude get
}

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