找不到字段的 setter - 将 Kotlin 与 Room 数据库一起使用

Cannot find setter for field - using Kotlin with Room database

我正在与 Room 持久性库集成。我在 Kotlin 中有一个数据 class,例如:

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

@Entity@PrimaryKey 注释用于 Room 库。当我尝试构建时,它失败并出现错误:

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

我也试过提供默认构造函数:

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

但这也行不通。需要注意的是,如果我将此 Kotlin class 转换为带有 getter 和 setter 的 Java class,它就会起作用。感谢您的帮助!

由于您的字段标有 val,它们实际上是最终的,没有 setter 个字段。

尝试将 val 换成 var。 您可能还需要初始化字段。

@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 = ""
)

编辑

上面的解决方案是在将 Kotlin 与其他 Java 库(如 Hibernate)一起使用时 Kotlin 中此错误的一般修复方法,我也曾在其中看到过此问题。如果您想保持 Room 的不变性,请参阅其他一些可能更适合您的情况的答案。

在某些情况下,Java 库的不可变性根本无法正常工作,同时让开发人员发出悲伤的声音,不幸的是,您必须将 val 切换为 var

另一个原因可能是字段的命名。如果您使用任何预定义的关键字,您将得到相同的错误。 例如,您不能将列命名为 "is_active".

参考:http://www.sqlite.org/lang_keywords.html

在 Java 中出现此错误。

Java 中不能有以 isis_ 开头的列。

尝试重命名列。

另一个解决方案:

您要么必须在构造函数中传递该字段并使用构造函数参数对其进行初始化,要么为其创建一个 setter。

示例:

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

public void setName(String name) {
    this.name = name;
}

嘿,不知道大家知不知道,但是不能有从is开始到Room的列。 例如你不能这样

   @Entity(tableName = "user")
   data class User (
        @PrimaryKey var id: Long? = null,
        var userName: String = "",
        var isConnectedToFB: Boolean = false,
)

room db 库 java 代码生成有问题。

我使用的是可选字段 isFavorite。它给了我同样的错误然后我将我的字段名称更改为 favorite 然后编译。

之前 var isFavorite: Int? = 0, 更改后工作正常 var favorite: Int? = 0, 谢谢

根据 如果你有一个自动生成的主键,你应该这样写:

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

请注意 @PrimaryKey 写在 class 正文中并包含修饰符 var.

如果您以后想用不同的参数更新数据库中的一行,请使用这些行:

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

更新

我还是不用AndroidX,Room是'android.arch.persistence.room:runtime:1.1.1'。

您可以从 Serializable 扩展此 class。但是如果你想从 Parcelable 扩展它,你会得到一个警告(超过 id 变量):Property would not be serialized inro a 'Parcel'. Add '@IgnoredOnParcel' annotation to remove this warning:

然后我将一个 id 从正文移动到构造函数。在 Kotlin 中,我使用 @Parcelize 创建 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

这是一个错误,已在 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

如果有人在 2019 年遇到这个话题,我只是更新一下,在网上花了几个小时研究为什么它应该起作用,但它不起作用。

如果您使用的是 AndroidX 版本 (androidx.room:room-<any>:2.*),使用 val 会按预期工作,但在使用旧的 android.arch.persistence.room:<any>:1.1.1 时却不会,似乎版本 2.* 没有在后一个回购中发布。

编辑:打字错误

如果您希望 val 不变性可用于您的实体,这是可能的。

  1. 你应该更新到 AndroidX 房间 current version
  2. 检查相关的 issue here 它被标记为 Won't Fix
  3. 现在他们发布了与 version 2.0.0-beta01
  4. 问题相关的修复程序
  5. 现在您可以将不可变 valdefault value 一起使用,例如:
@Entity("tbl_abc")
data class Abc(
    @PrimaryKey
    val id: Int = 0, 
    val isFavourite: Boolean = false
)

以前,上面的代码片段会抛出 Cannot find setter for field 的错误。更改为 var 是一个很好的解决方法,但我更喜欢实体 class 从外部调用不可变

您现在可以使用 is 开始您的字段,但是 is 旁边不能有数字,例如:is2FooSelected,您必须重命名为 isTwoFooSelected ].

我发现导致此编译错误的另一个原因可能是在您的实体数据字段上使用了 Room 的 @Ignore 注释 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
)

使用 @Transient 注释时似乎也会发生同样的错误。

我在使用 Room 的 2.2.2 版本时注意到了这个问题:

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

希望对某人有所帮助!

只需使用 var 而不是 val,如果您使用 private 关键字,请将其设为 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
)

如果您的列以 Is:

开头,则会抛出此错误
@ColumnInfo(name = "IsHandicapLeague")
    @NonNull
    var isHandicapLeague: String = "Y"

添加一个默认的 set() 函数来消除

fun setIsHandicapLeague(flag:String) {
    isHandicapLeague = flag
}

如果数据 class 构造函数中有 @Ignore 字段,则需要将其移动到 class 主体,如下所示:

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

所有荣誉归于marianperca on GitHub: https://github.com/android/architecture-components-samples/issues/421#issuecomment-442763610

您可以尝试将 id 变量重命名为其他名称。它对我有用;

var id: Long? = null

var workerId: Long? = null

如果你必须命名为 id 并且你正在使用改造,那么你可能需要添加 SerializedName("id")

只是让变量可变,对于 Kotlin 将 val 更改为 var,或者对于 Java

将 private 更改为 public

看来Room和Kotlin版本需要匹配。我对 Room 2.3.0Kotlin 1.6.10 有同样的问题,但对 Kotlin 1.5.20 没问题。我更新 Room2.4.2 后看起来没问题。 https://youtrack.jetbrains.com/issue/KT-45883

还有 there is 一个可能的解决方案,使用 @JvmOverloads constructor 以获得更好的 Java 兼容性。

将 Room 库更新到最新版本2.4.2解决问题