Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Room Database error with Kotlin Data Class

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.
like image 383
WoogieNoogie Avatar asked Oct 16 '17 15:10

WoogieNoogie


People also ask

What are the requirements for a data class in Kotlin?

There are following conditions for a Kotlin class to be defined as a Data Class: The primary constructor needs to have at least one parameter. All primary constructor parameters need to be marked as val or var. Data classes cannot be abstract, open, sealed, or inner.

Can we inherit data class in Kotlin?

The inheritance of data classes in Kotlin doesn't execute well. Hence, it is advised not to use inheritance by extending the data class in Kotlin. But we can use abstract class and interface .

Can data class be open Kotlin?

Kotlin Data Class Requirements The parameters of the primary constructor must be marked as either val (read-only) or var (read-write). The class cannot be open, abstract, inner or sealed. The class may extend other classes or implement interfaces.

How do I create a room database in Kotlin?

Room Database with Kotlin 1 Create a new project in Android Studio with empty activity. 2 Add dependencies. 3 Now let’s create an Entity. It must to declare one field as primary key. 4 Create a Dao ( Data access object ) using an interface. ... 5 Create a database class an NoteDatabase extends RoomDatabase. 6 Managing data. ...

Can I use Kotlin in Android app?

Basic coroutines (If you're not familiar with coroutines, you can start with Using Kotlin Coroutines in your Android App .) It also helps to be familiar with software architectural patterns that separate data from the user interface, such as Model-View-Presenter (MVP) or Model-View-Controller (MVC).

Why is my Kotlin Kapt class not working in Gradle?

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.

What are the components of a room database?

There are 3 main components are there in Room database. Represents a table within the database. Room creates a table for each class that has entity annotation, the fields in the class correspond to columns in the table. Therefore, the entity classes tend to be small model classes that don’t contain any logic.


2 Answers

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

like image 142
Emanuel S Avatar answered Sep 20 '22 17:09

Emanuel S


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.

like image 26
WoogieNoogie Avatar answered Sep 21 '22 17:09

WoogieNoogie