Kotlin数据类的房间数据库错误

时间:2017-10-16 15:48:21

标签: android kotlin android-room

我一直在使用Room,而且我遇到了阻塞问题。我已经完成并修复了Room库中的所有编译时检查,但现在遇到以下错误:

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).

这在编译时出现两次,没有证据表明它来自哪个类,但我能够弄清楚(通过从数据库中删除类)这是其中一个文件。我假设它与主键有关,而不是Int(这是使用它的两个类中的一个),但文档中没有任何内容表明问题是什么,事实上文档显示字符串是有效的主键。

@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.

我尝试了一些尝试来解决这个问题。

  • 删除此类的数据属性以使其成为普通的POKO
  • 从默认构造函数中删除变量,并将它们放入类
  • 从空构造函数中删除Ignore(注意,这会导致另一个问题,Room cannot pick a constructor since multiple constructors are suitable - 默认构造函数上的忽略注释会解决这个问题。)这是困扰我的部分 - 删除这个说"多个构造函数是有效的",保持它说"没有构造函数是有效的"。

更新:从我的项目中添加一些更相关的代码片段。

的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(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
}

转换器

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(",") ?: ""
}
}

另一个数据类

@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类:

data class UserPermissions(
@SerializedName("id")
var pid: Int = 0,

...
// Rest of code left off for brevity, found to not be related to the issue.

4 个答案:

答案 0 :(得分:15)

你的问题是,如果你有可空值,Kotlin会为每个可能的构造函数生成几个构造函数。

这意味着您必须定义默认构造函数并使用默认值填充它。

如果你想要另一个应该被忽略的,你应该确保使用带有所有这些参数的父构造函数。

示例:

@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)
}

在这种情况下,只会生成两个构造函数&#34;引擎盖&#34;。如果您有可空值,那么您将拥有所有可能的构造函数。

示例:

data class Test(var id: Int = 0, var testString: String? = null, var testBool : Boolean? = null) {
   constructor(0)
} 

生成

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

由于您正在寻找官方文档,因此您可能需要查看Overloads Generation

在测试了您的课程后,我在另一篇文章中找到了完美无瑕的作品,您必须检查您是否在Gradle中使用了apply plugin: 'kotlin-kapt'

仔细检查您的Date类的有效类型转换器。我很久以前写过that issue

重新编码上面的内容之后,通过添加类似的UserPermissions类来正常工作:

data class UserPermissions(var permissionid: String) 

编辑:使用UserPermission课程后,一切正常。如果使用正确的导入(例如util.Date而不是sql.Date),请注意。

另一个问题是你使用一个旧的非常多的房间库。

当前版本(写这篇文章时)是

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"

我很久以前写过an issue

答案 1 :(得分:3)

这个问题极难调试,难以重现,但我发现了这个问题。我使用的是@Embedded对象,但进入的结果实际上是该对象的List。这给自动嵌入任务带来了麻烦,而且没有一个完美的转换器可以为它编写。

@SerializedName("range_choices")
@Embedded
var rangeChoices: List<RangeChoice>? = null,

我必须使用@Ignore注释,我会将此列表的结果保存到自己的表中,现在是新表range_choices

答案 2 :(得分:1)

您的主键应如下所示,使用Annotation Use-site Targets

@field:PrimaryKey @field:SerializedName("guid") var guid: String = ""

@field:PrimaryKey @field:SerializedName("id") var id: Int = 0

答案 3 :(得分:0)

尽量避免使用可空值并使所有内容都具有某种默认值。这是解决这个问题的最简单方法。

如果你真的想使用它们,那么你可以创建一个包含所有这些的构造函数。