房间持久性:错误:实体和Pojos必须具有可用的公共构造函数


78

我正在将一个项目转换为Kotlin,并且试图将我的模型(也是我的实体)变成一个数据类,我打算使用Moshi来转换来自API的JSON响应

@Entity(tableName = "movies")
data class MovieKt(
    @PrimaryKey
    var id : Int,
    var title: String,
    var overview: String,
    var poster_path: String,
    var backdrop_path: String,
    var release_date: String,
    var vote_average: Double,
    var isFavorite: Int
)

我无法构建导致以下错误的应用

实体和Pojos必须具有可用的公共构造函数。您可以有一个空的构造函数或一个其参数与字段匹配的构造函数(按名称和类型)。找不到字段的设置器。

我发现的例子离这个不远

关于如何解决的想法?


1
这是哪个kotlin版本?您还可以打印完整的错误消息吗?Room应该能够正确处理该数据类,因为它只有1个构造函数来接收所有字段。
yigit

2
确保构造函数参数的名称和类型与Room期望的一致。与Java的关键字冲突也可能发生这种情况。例如,static在Kotlin中允许使用Java关键字,但是当将其压缩后,看起来它们被替换为另一个名称,从而导致Room注释处理器无法与该字段进行匹配。
chRyNaN

您还可以发布您的DAO吗?
Vicky '18年

您可以解决此问题,添加空白构造函数
Webserveis

Answers:


42

之前有过类似的问题。

首先,我已经更新/添加apply plugin: 'kotlin-kapt'到gradle。

接下来,我用它代替了annotationProcessorgradle:

kapt "android.arch.persistence.room:compiler:1.0.0-alpha4"

最后一件事是创建一个不变的数据类:

@Entity(tableName = "movies")
data class MovieKt(
    @PrimaryKey
    val id : Int,
    val title: String,
    val overview: String,
    val poster_path: String,
    val backdrop_path: String,
    val release_date: String,
    val vote_average: Double,
    val isFavorite: Int
)

更新:

当您在同一Android模块中具有模型的类和数据库的类时,此解决方案有效。如果您在Android库模块中有模型类,而在主模块中有其余代码,Room将无法识别它们。


19
添加apply plugin: 'kotlin-kapt'到模块的build.gradle文件为我解决了这个问题,谢谢!
路易CAD

19
无法使用androidx并使用“应用插件”:“ kotlin-kapt”
ArdenDev

1
[Kotlin]我在选择查询时遇到了这个问题,因为我忘了在DAO中添加返回类型
Sachin

38

在您的情况下这不是问题,但对于其他情况,如果您在主构造函数中具有@Ignore参数,则可能会发生此错误,即Room期望具有以下任一情况:

  • 无参数构造函数或
  • 构造函数,其中所有字段均未标记@Ignore

例如:

@Entity(tableName = "movies")
data class MovieKt(
    @PrimaryKey
    var id : Int,
    var title: String,
    @Ignore var overview: String) 

不管用。这将:

@Entity(tableName = "movies")
data class MovieKt(
    @PrimaryKey
    var id : Int,
    var title: String) 

2
这是我的情况,但没有意义,因为@Ignore的官方示例使用相同的方式:developer.android.com/training/data-storage/room/defining-data
Aspiring Dev

@AspiringDev我同意,听起来更像是房间的bug,而不是故意的设计。
daneejela

3
因此,唯一的选择是不使用@Ignore字段?
FutureShocked

3
@daneejela您也可以@Ignore在主构造函数中包含字段,您只需要一些构造函数而无需Room可以利用的字段。有关详细信息,请参见我的答案。
FutureShocked

1
@Ignore单个构造函数中允许使用默认值和@JvmOverloads注释
-gmk57

22

您需要像这样指定一个辅助构造函数:

@Entity(tableName = "movies")
data class MovieKt(
    @PrimaryKey
    var id : Int,
    var title: String,
    var overview: String,
    var poster_path: String,
    var backdrop_path: String,
    var release_date: String,
    var vote_average: Double,
    var isFavorite: Int
) {
    constructor() : this(0, "", "", "", "", "", 0.0, 0)
}    

14

对我有用的是:

@Entity(tableName = "movies")
data class MovieKt(
    @PrimaryKey
    var id : Int? = 0,
    var title: String? = "",
    var overview: String? = "",
    var poster_path: String? = "",
    var backdrop_path: String? = "",
    var release_date: String? = "",
    var vote_average: Double? = 0.0,
    var isFavorite: Int? = 0
)

10

对我而言,我要做的就是向数据类添加一个构造函数,并向其发送空参数,如下所示:

    @Entity(tableName = "posts")
data class JobPost(
    @Ignore
    @SerializedName("companyLogo")
    var companyLogo: String,
    @Ignore
    @SerializedName("companyName")
    var companyName: String,
    @Ignore
    @SerializedName("isAggregated")
    var isAggregated: String,
    @PrimaryKey(autoGenerate = false)
    @SerializedName("jobID")
    var jobID: String,
    @Ignore
    @SerializedName("jobTitle")
    var jobTitle: String,
    @Ignore
    @SerializedName("postedOn")
    var postedOn: String,
    @Ignore
    @SerializedName("region")
    var region: String
) {
    constructor() : this("","","","","","","")
}

谢谢,这应该是公认的答案。@Leonardo Deleon有时​​会出现问题,因为我们没有在属性上分配默认值。 e.g: var postedOn: String = "" // this should fix the error as well
mochadwi

仅使用@Ignore参数对我有用的答案。
error1337

10

要扩展@evanchooly和@daneejela提供的答案,您需要一个辅助构造函数,以便能够@Ignore在主要构造函数中使用参数。这样,Room仍然具有可在实例化对象时使用的构造函数。根据您的示例,如果我们忽略以下字段之一:

@Entity(tableName = "movies")
data class MovieKt(
        @PrimaryKey
        var id : Int,
        var title: String,
        var overview: String,
        var poster_path: String,
        var backdrop_path: String,
        @Ignore var release_date: String,
        @Ignore var vote_average: Double,
        @Ignore var isFavorite: Int
) {
    constructor(id: Int, title: String, overview: String, poster_path: String, backdrop_path: String) {
        this(id, title, overview, poster_path, backdrop_path, "", 0.0, 0)
    }
}

1
您的语法不应该是constructor(params) : this(params from primary constructor) { }
Sudhir Khanger

10

我遇到过同样的问题。您可以将@Ignore字段移至类主体。例如 :

@Entity(tableName = "movies")
data class MovieKt(
    @PrimaryKey
    var id : Int,
    var title: String
){
    //here
    @Ignore var overview: String
 }

7

Kotlin允许使用长名称作为参数名称,但是当room生成Java代码时,此名称将无效。


1
这是我的问题。将经度var从“ long”更改为“ lng”可修复该错误。
汤姆(Tom),

同样,来自api的响应长时间用于经度,并且花费了数小时才能解决此问题。
Thracian

4

我认为解决此问题的一个不错的选择是:

@Entity(tableName = "movies")
data class MovieKt(
    @PrimaryKey
    var id : Int = 0,
    var title: String = "",
    var overview: String = "",
    var poster_path: String = "",
    var backdrop_path: String = "",
    var release_date: String = "",
    var vote_average: Double = 0.0,
    var isFavorite: Int = 0
)

4

我也遇到了这个问题,但是我意识到问题是我在已经具有类型转换器的属性中添加了@Embedded批注,因此任何有相同问题的人都应仔细检查模型类的属性声明,并确保@嵌入式注释不在具有与其关联的类型转换器的属性上。


3

今天我遇到了这个问题。我使用@Ignore,这就是为什么我得到错误。为了解决这个问题,我创建了一个辅助构造函数。所以我的代码看起来像这样:

@Entity(tableName = "profile")
data class Profile(
  @field:SerializedName("id") @PrimaryKey @ColumnInfo(name = "id") var id:Long,
  @field:SerializedName("foo") @ColumnInfo(name = "foo") var foo:String?,
  @field:SerializedName("bar") @Ignore var Bar:String?
){
   constructor(id:Long, foo:String) : this(id, foo, null)
}

这对我有用。


2

我遇到了一个实体问题(所有字段均已正确初始化,var就像这里的许多答案所暗示的那样),该实体包括相关的非原始项目(如此SO问题中的OP)的列表。例如:

@Entity(tableName = "fruits")
data class CachedFruitEntity(
        @PrimaryKey var id: Long = 0L,
        @Embedded(prefix = "buyer_") var buyerEntity: CachedBuyerEntity? = null
        @TypeConverters(VendorsConverter::class)
        var vendorEntities: List<CachedVendorEntity?> = listOf()))

也就是说,它具有一个嵌入式字段,花了我一段时间才意识到我真正需要的是供卖方实体列表使用的类型转换器(编译器没有抛出通常的代码,Error:(58, 31) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.因此我的解决方案与该答案非常相似)。

这个google架构组件github线程具有有关此误导性错误的更多信息,但不确定该问题是否已解决。


2

就像Room文档中所说的那样,您需要创建一个空的public构造函数。同时,如果要声明其他自定义构造函数,则必须添加@Ignore注释。

@Entity
public class CartItem {
    @PrimaryKey
    public int product_id;
    public int qty;

    public CartItem() {
    }

    @Ignore
    public CartItem(int product_id, int count) {
        this.product_id = product_id;
        this.qty = count;
    }
}


1

https://issuetracker.google.com/issues/62851733

我发现这是@Relation的投影错误!不是科特林语言的问题。基于Google的GithubBrowserSample java也发生了错误,但消息错误不同。

以下是我的Kotlin代码:

data class UserWithCommunities(
        @Embedded
        var user: User = User(0, null),

        @Relation(parentColumn = "id",
                entityColumn = "users_id",
                entity = CommunityUsers::class,
                projection = arrayOf("communities_id")) // delete this line.
        var communityIds: List<Int> = emptyList()
)

对:

data class UserWithCommunities(
        @Embedded
        var user: User = User(0, null),

        @Relation(parentColumn = "id",
                entityColumn = "users_id",
                entity = CommunityUsers::class)
        var communityList: List<CommunityUsers> = emptyList()
)

1

相同的错误,更陌生的解决方案:不要Maybe<Cursor>在Dao上使用reactx返回光标。FlowableSingle以及Observable没有任何工作。

只需咬一下子弹,然后在Dao请求之外进行响应式调用。之前:

@Dao
interface MyDao{
    @Query("SELECT * FROM mydao")
    fun getCursorAll(): Flowable<Cursor>
}

后:

@Dao
interface MyDao{
    @Query("SELECT * FROM mydao")
    fun getCursorAll(): Cursor
}

元:

Android Studio 3.2
Build #AI-181.5540.7.32.5014246, built on September 17, 2018
JRE: 1.8.0_152-release-1136-b06 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
macOS 10.12.6

1

只需将以下注释添加到任何会导致错误的构造函数中,然后添加一个新的空白构造函数。

@忽视


1

不要用于数据类,而应使用普通类。这种方法可以解决问题


1

在2.1.0-alpha6中,它在Dao中被证明是无效的返回类型。固定返回类型按预期进行修复。


1

对我来说,我在kotlin的data(Entity)类中使用了'lat'和'long'作为变量名,因此重命名为经度和纬度可以正常工作。

无法运作:

@Entity(tableName = "table_User")
data class User(@PrimaryKey var userId : Int, @ColumnInfo(name = "first_name") 
var firstName: String
            , @ColumnInfo(name = "last_name") var lastName: String
            , @ColumnInfo(name = "password") var password: String
            , @ColumnInfo(name = "dob") var dob: Long
            , @ColumnInfo(name = "address") var address: String
            , @ColumnInfo(name = "lat") var latitude: Double
            , @ColumnInfo(name = "long") var longitude: Double) {

}

加工:

@Entity(tableName = "table_User")
data class User(@PrimaryKey var userId : Int, @ColumnInfo(name = "first_name") 
var firstName: String
            , @ColumnInfo(name = "last_name") var lastName: String
            , @ColumnInfo(name = "password") var password: String
            , @ColumnInfo(name = "dob") var dob: Long
            , @ColumnInfo(name = "address") var address: String
            , @ColumnInfo(name = "latitude") var latitude: Double
            , @ColumnInfo(name = "longitude") var longitude: Double) {

}

这是相同的情况下与我,感谢您的回答👍
Muazzam A.

1

Kotlin插件不会获取注释处理器的依赖关系,因此请使用最新版本的Kotlin注释处理器-将这一行放在模块级别 build.gradle文件的顶部

apply plugin: 'kotlin-kapt'

喜欢

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'  // add this line

android {
    compileSdkVersion 28
    defaultConfig {
      ........
    }
}

不要忘记相应地更改compileSdkVersion。


0

房间数据库中所述Entity

每个实体必须具有无参数构造函数,或者其参数匹配字段(基于类型和名称)的构造函数。

因此,添加一个空的构造函数并使用注释您的参数化构造函数@Ignore将解决您的问题。一个例子:

public class POJO {

    long id;

    String firstName;

    @Ignore
    String lastName;

    public POJO() {
    }

    @Ignore
    public POJO(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    // getters and setters
    // ...

}

-1

以我为例,我没有在Dao查询之一中返回类型,希望它对其他人有帮助


-2

对于实现autoGenerate的FutureShocked答案的变体:

@Entity(tableName = "movies")
data class MovieKt(
        var title: String,
        var overview: String,
        var poster_path: String,
        var backdrop_path: String,
        @Ignore var release_date: String,
        @Ignore var vote_average: Double,
        @Ignore var isFavorite: Int
) {
@PrimaryKey(autoGenerate = true) var id : Int = 0

    constructor(title: String, overview: String, poster_path: String, backdrop_path: String) {
        this(id, title, overview, poster_path, backdrop_path, "", 0.0, 0)
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.