如何在数据库房间中保存枚举字段?

53

我必须将enum枚举类型的值写入数据库。在编译过程中出现错误。我做错了什么?

无法确定如何将此字段保存到数据库中。您可以考虑为其添加类型转换器。

@ColumnInfo(name = "state_of_health")
@TypeConverters(HealthConverter::class)
var health: Health

enum class Health(val value: Int){
    NONE(-1),
    VERY_BAD(0),
    ...
}

class HealthConverter{

    @TypeConverter
    fun fromHealth(value: Health): Int{
        return value.ordinal
    }

    @TypeConverter
    fun toHealth(value: Int): Health{
        return when(value){
            -1 -> Health.NONE
            0 -> Health.VERY_BAD
            ...
            else -> Health.EXCELLENT
        }
    }

}

1
看起来你应该在 Database 类上使用 @TypeConverters 注解(而不是在枚举类上)。请参考 https://developer.android.com/training/data-storage/room/referencing-data。 - Demigod
@Demigod 是的,这是有效的。你可以编写答案。 - Viewed
6个回答

68

你可以像这样将每个枚举类型转换:

class Converters {

     @TypeConverter
     fun toHealth(value: String) = enumValueOf<Health>(value)

     @TypeConverter
     fun fromHealth(value: Health) = value.name
}

或者,如果您更喜欢将其存储为 SQL integer 类型,您也可以使用序数:

class Converters {

    @TypeConverter
    fun toHealth(value: Int) = enumValues<Health>()[value]

    @TypeConverter
    fun fromHealth(value: Health) = value.ordinal
}

不幸的是,无法使用泛型 Enum<T> 来完成此操作,因为未绑定的泛型会引发错误 Cannot use unbound generics in Type Converters

Android Room团队可以在他们的kapt编译器中严肃地添加一个注释和生成器来支持Enums。

最后,在数据库类、实体类、dao类、dao方法、dao方法参数或实体字段类上注释如下:

@TypeConverters(Converters::class)

如何在数据类中使用枚举?使用值还是枚举? - Rafael
@Rafael 在使用类型转换器后,你可以直接在实体类中使用枚举。 - Sarthak_ssg5

33

版本2.3.0-alpha4中,这已不再是一个问题:“如果没有提供类型转换器,则Room现在将默认使用枚举值作为字符串和反之亦然的类型转换器。如果枚举的类型转换器已经存在,则Room将优先使用它而不是默认的转换器。”

“如果已经存在用于读取的单向类型转换器,则Room可能会意外地使用内置的字符串到枚举的转换器,这可能不是所需的。这是一个已知的问题,并且可以通过将其变为双向转换器来修复。”


15
为了解决这个问题,请在你的 Database 类上使用 @TypeConverters 注释(而不是你的 enum class)。
例如:
@Database(entities = arrayOf(User::class), version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

请查看https://developer.android.com/training/data-storage/room/referencing-data


还有其他方法吗?在JPA中更容易。 - Allan Veloso
@AllanVeloso,你希望采用哪种“另一种方式”?或者,在你的模型中可以持有基本类型数据(如枚举序数作为Int,或枚举名称作为String等),但这样做会更好吗?另外,我不知道JPA是什么... - Demigod
JPA是Java Persistence API。它通过在字段上使用简单的注释@Enumerated(EnumType.STRING)或@Enumerated(EnumType.ORDINAL)来处理枚举类的持久性。这样,您就不需要为每个枚举创建转换器。 - Allan Veloso
翻译:也不好,每次定义枚举选项时都必须覆盖getId()方法。我尝试使用泛型、内联修复和泛型,但room也不支持它,报错Cannot use unbound generics in Type Converters。最好手动进行转换。 - Allan Veloso
@AllanVeloso,你说得对。这不能以一种简单的方式100%地概括。 - Demigod
显示剩余3条评论

11

枚举类;

enum class Priority {
HIGH,
MEDIUM,
LOW
}

转换器类;

class Converter {

@TypeConverter
fun fromPriority(priority: Priority): String {
    return priority.name
}

@TypeConverter
fun toPriority(priority: String): Priority {
    return Priority.valueOf(priority)
}

}

使用方法;

@Database(entities = [MyData::class], version = 1, exportSchema = false)
@TypeConverters(Converter::class)
abstract class MyDatabase : RoomDatabase() {

  // todo

}

“Converter”类是否可以包含您想要的所有转换器? - android developer
2
@androiddeveloper 是的 - Milan
@Milan 很好。谢谢你。 - android developer

5

针对Java开发人员

枚举类型(Enum)

public enum Health {
    NONE(-1),
    VERY_BAD(0);

    public final int value;

    Health(int newValue) {
        value = newValue;
    }

    public int getValue() {
        return value;
    }
}

类型转换器

public class HealthConverter {

    /**
     * Convert Health to an integer
     */
    @TypeConverter
    public static int fromHealthToInt(Health value) {
        return value.ordinal();
    }

    /**
     * Convert an integer to Health
     */
    @TypeConverter
    public static Health fromIntToHealth(int value) {
        return (Health.values()[value]);
    }

}

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接