【问题标题】:Android Architecture Components: Using EnumsAndroid 架构组件:使用枚举
【发布时间】:2017-11-13 21:09:09
【问题描述】:

是否可以通过新的 Android 架构组件和 Room Persistence 库将 Enum 类型用作实体类中的嵌入字段?

我的实体(带有嵌入式枚举):

@Entity(tableName = "tasks")
public class Task extends SyncEntity {

    @PrimaryKey(autoGenerate = true)
    String taskId;

    String title;

    /** Status of the given task.
     * Enumerated Values: 0 (Active), 1 (Inactive), 2 (Completed)
     */
    @Embedded
    Status status;

    @TypeConverters(DateConverter.class)
    Date startDate;

    @TypeConverters(StatusConverter.class)
    public enum Status {
        ACTIVE(0),
        INACTIVE(1),
        COMPLETED(2);

        private int code;

        Status(int code) {
            this.code = code;
        }

        public int getCode() {
            return code;
        }
    }
}

我的类型转换器:

public class StatusConverter {

    @TypeConverter
    public static Task.Status toStatus(int status) {
        if (status == ACTIVE.getCode()) {
            return ACTIVE;
        } else if (status == INACTIVE.getCode()) {
            return INACTIVE;
        } else if (status == COMPLETED.getCode()) {
            return COMPLETED;
        } else {
            throw new IllegalArgumentException("Could not recognize status");
        }
    }

    @TypeConverter
    public static Integer toInteger(Task.Status status) {
        return status.getCode();
    }
}

当我编译这个时,我收到一条错误消息Error:(52, 12) 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).

更新 1 我的 SyncEntity 类:

/**
 * Base class for all Room entities that are synchronized.
 */
@Entity
public class SyncEntity {

    @ColumnInfo(name = "created_at")
    Long createdAt;

    @ColumnInfo(name = "updated_at")
    Long updatedAt;
}

【问题讨论】:

  • 我认为您需要将字段设置为public,提供public 设置器,或者提供与@Query 列匹配的public 构造函数。否则,Room 无法为您提供数据。您唯一的构造函数是零参数的构造函数。
  • Getters 和 Setters 不能解决问题,并且 Enum 不允许公共构造函数。我认为 TypeConverter 可以解决问题并将 Enum 转换为 int,但事实并非如此。可能 Room 还不够成熟,无法用于这种用途。可能应该提出功能请求。

标签: android android-room android-architecture-components


【解决方案1】:

我可以在RoomTypeConverters 中使用枚举值。您的代码有一些部分需要更改:

1) 您必须将实体的字段声明为公开,或者它们必须具有公开的 getter/setter。否则你会得到以下错误:

yourFieldYourEntity 中不公开;无法从 外包装

2) 您的status 字段不需要@Embedded 注释。它用于嵌套对象。 More from docs.

3) 您没有在正确的位置使用@TypeConverters 注释。在您的情况下,它可以设置在 status 字段上方。 More from docs.

4) 你必须为你的实体定义一个构造函数,否则你会得到以下错误:

实体和 Pojos 必须有一个可用的公共构造函数。你可以有一个空的构造函数或一个参数匹配字段(按名称和类型)的构造函数。

你可以定义一个空的构造函数来跳过这个错误。

5) 在 TypeConverter 中使用 int 而不是 Integer。

总和;下面按预期工作:

@Entity(tableName = "tasks")
public class Task extends SyncEntity {

    @PrimaryKey(autoGenerate = true)
    public String taskId;

    public String title;

    /** Status of the given task.
     * Enumerated Values: 0 (Active), 1 (Inactive), 2 (Completed)
     */
    @TypeConverters(StatusConverter.class)
    public Status status;

    @TypeConverters(DateConverter.class)
    public Date startDate;

    // empty constructor 
    public Task() {
    }

    public enum Status {
        ACTIVE(0),
        INACTIVE(1),
        COMPLETED(2);

        private int code;

        Status(int code) {
            this.code = code;
        }

        public int getCode() {
            return code;
        }
    }
}

【讨论】:

  • 感谢@Devrim,因为它是“在您的 TypeConverter 中使用 int 而不是 Integer”
  • 如果你必须为你的枚举使用整数值,这是正确的做法。永远不要使用序数。
  • 不使用序数的原因是什么?
  • 如果常用转换器,我更愿意在数据库级别调用转换器。
  • 你不应该使用序数的原因是你可以选择重新排列你的枚举的顺序(例如,如果你添加一个新的) - 所以当它从数据库中拉出来时,你可能会将其映射到错误的值。你可能最好使用字符串。 @CheokYanCheng​​span>
【解决方案2】:

我遇到了类似的问题。 Devrim 对这个主题的回答非常有帮助。但是我的实体有一个可以为空的。

在这种情况下,插入操作会返回错误。这是由枚举类型中的原始“代码”成员引起的。

我开发了以下实现来解决此错误。

首先我将枚举类型固定如下。

package com.example.models.entities.enums;


import androidx.room.TypeConverter;

public enum Status {
    ACTIVE(0),
    INACTIVE(1),
    COMPLETED(2);

    private final Integer code;

    Status(Integer value) {
        this.code = value;
    }
    public Integer getCode() {
        return code;
    }

    @TypeConverter
    public static Status getStatus(Integer numeral){
        for(Status ds : values()){
            if(ds.code == numeral){
                return ds;
            }
        }
        return null;
    }

    @TypeConverter
    public static Integer getStatusInt(Status status){

        if(status != null)
            return status.code;

        return  null;
    }

}

然后我将实体更新如下。

package com.example.models.entities;


import com.example.models.TypeConverters.DateConverter;
import com.example.models.entities.enums.Status;

import java.util.Date;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.TypeConverters;

@Entity(tableName = "tasks")
@TypeConverters({Status.class, DateConverter.class})

public class Task extends SyncEntity {

    @PrimaryKey(autoGenerate = true)
    String taskId;

    @NonNull
    String title;

    /** Status of the given task.
     * Enumerated Values: 0 (Active), 1 (Inactive), 2 (Completed)
     */
    @Nullable
    Status status;

    @NonNull
    Date startDate;


    public String getTaskId() {
        return taskId;
    }

    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }

    @NonNull
    public String getTitle() {
        return title;
    }

    public void setTitle(@NonNull String title) {
        this.title = title;
    }

    @Nullable
    public Status getStatus() {
        return status;
    }

    public void setStatus(@Nullable Status status) {
        this.status = status;
    }

    @NonNull
    public Date getStartDate() {
        return startDate;
    }

    public void setStartDate(@NonNull Date startDate) {
        this.startDate = startDate;
    }
}

所以,我可以在 Room 数据库中管理可为空的枚举属性。

我希望我能够帮助解决类似的情况。

编码愉快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 2022-09-23
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 2019-10-24
    相关资源
    最近更新 更多