android 将复杂的类数据保存在Room中-“无法确定如何将此字段保存到数据库中,您可以考虑为其添加类型转换器”

7eumitmz  于 2023-01-15  发布在  Android
关注(0)|答案(2)|浏览(131)

我开始学习房间,我面临着一个问题:
给定两个类,一个是Car,另一个是Car中的Engine

@Entity
class Car{

    @PrimaryKey
    var id = 0
    var name: String? = null
    var engine: Engine? = null
}

...

@Entity
class Engine{

    @PrimaryKey
    var id = 0
    var manufacturer: String? = null
}

我还将这些类初始化为AppDatabase类中的表。

@Database(entities = [Car::class, Engine::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
//...
}

问题是每当我只想运行项目时,我会收到以下错误消息,指向Car的引擎字段:

Cannot figure out how to save this field into database. You can consider adding a type converter for it.

没有简单的方法吗?我正在寻找用最少的代码来保存数据的工具,比如Firestore,它可以用简单的注解来完成所有的工作。
先谢了。

hm2xizp9

hm2xizp91#

因为一辆车只有一个发动机,你有一个发动机表和一个汽车表,那么你有一个一对多的关系,也就是说一辆车可以有一个发动机,但是同一个发动机可以被很多汽车使用。
因此,与其试图将引擎嵌入到汽车中,不如建立一个关系,汽车(子)引用引擎(父)。
这就像将汽车更改为:-

@Entity
class Car{

    @PrimaryKey
    var id = 0
    var name: String? = null
    var engine: Int? = null
}

另一种不需要关系也不需要TypeConverter的方法是不将Engine作为表,而是在Engine之前使用@Embedded注解。

@Entity
class Car{

    @PrimaryKey
    var id = 0
    var name: String? = null
    @Embedded
    var engine: Engine? = null
}

...

class Engine{
    @PrimaryKey
    @ColumnInfo(name = "engineId")
    var id = 0
    var manufacturer: String? = null
}
  • 用于存储引擎ID的列的名称已更改,否则将有2列具有相同的名称。
  • 请注意,使用这种方法时,不需要@Entity注解,因为您将Engine值存储在Car中。
  • 这不被认为是良好的做法,因为如果同一个引擎是由许多汽车使用,那么你是重复的数据,因此,它是不正常的。

从数据库的Angular 来看,第三种也是最不可取的方法是将引擎对象的表示形式存储在单列中。也就是说,将对象转换为单一的可存储表示形式。通常是JSON字符串。因此,您需要代码(一个函数)将对象转换为单个值(JSON字符串),并需要代码(另一个函数)将JSON字符串转换为对象。
使用这种方法,不仅不能规范化数据,而且最终存储了表示对象所需的膨胀,从数据库的Angular 来看,这种膨胀在某种程度上混淆了实际有用的存储数据。
此外,没有一个集合/标准库提供将对象转换为JSON或从JSON转换对象的功能,因此您必须选择一种风格,然后将该库包含在项目中。
下面是一个类,其中包含可以使用的类型转换器(请参阅库的注解):-

class CarAndEngineTypeConverters{
    /* Using Library as per dependency implementation 'com.google.code.gson:gson:2.10.1' */
    @TypeConverter
    fun convertEngineToJSONString(engine: Engine): String = Gson().toJson(engine)
    @TypeConverter
    fun convertJSONStringToEngine(jsonString: String): Engine = Gson().fromJson(jsonString,Engine::class.java)
}

这将适合您原来的类。
需要通过@TypeConverters注解(注意是复数而不是单数)告知Room使用这些类(它会计算出何时使用),这是在@Database注解具有最高级别作用域之前或之后立即使用的。
为了一起演示所有这三个方面,请考虑顶级Car类:-

@Entity
class Car{

    @PrimaryKey
    var id = 0
    var name: String? = null
    var engine: Int? = null
    @Embedded
    var alternativeEngine: Engine? = null
    var jsonConvertedEngine: Engine? = null
}
  • 超过顶部,因为发动机存放了3次(可能是不同的发动机)
    **引擎 * 类
@Entity
class Engine{
    @PrimaryKey
    @ColumnInfo(name = "engineId")
    var id = 0
    var manufacturer: String? = null
}

类型转换器如上所述。
准备好上述内容并在活动中使用后(请注意,为了简洁/方便起见,使用了.allowMainThreadQueries):-

db = TheDatabase.getInstance(this)
    carAndEngineDAO = db.getCarAndEngineDAO()

    var engine1 = Engine()
    engine1.manufacturer = "Ford"
    engine1.id = carAndEngineDAO.insert(engine1).toInt()

    var car1 = Car()
    car1.name = "Escort"
    car1.engine = engine1.id /* id of the engine */
    car1.alternativeEngine = engine1
    car1.jsonConvertedEngine = engine1
    carAndEngineDAO.insert(car1)

使用Android Studios应用程序检查查看数据库,然后

  • 列ID和名称,显然符合预期
  • 引擎列包含值0,这是引擎表中相应引擎的ID(最多8个字节存储ID)
  • JsonConvertedEngine列存储引擎的JSON表示形式(31字节)
  • engineId列和manufacturer列存储各自的值(12字节)。

引擎表(仅关系需要)为:-

cxfofazt

cxfofazt2#

您应该使用类型转换器:
1.首先,将此依赖项添加到项目中以将Engine转换为Json,反之亦然
实现“com.squareup.retrofit2:转换器-gson:2.5.0”
1.现在,您应该创建一个Object类,将Engine转换为Json。该类使Engine对于Room来说是可理解的:

object CommonTypeConverters {

@TypeConverter
@JvmStatic
fun stringToEngine(value: String): Engine = fromJson(value)

@TypeConverter
@JvmStatic
fun engineToString(items: Engine?): String = toJson(items)

inline fun <reified T> toJson(value: T): String {
    return if (value == null) "" else Gson().toJson(value)
}

inline fun <reified T> fromJson(value: String): T {
    return Gson().fromJson(value, object : TypeToken<T>() {}.type)
}

最后,Engine不是一个实体,您应该在数据库类中添加@Typeconverter注解:

@Database(entities = [Car::class], version = 1)
@TypeConverters(CommonTypeConverters::class)
abstract class AppDatabase : RoomDatabase() {
//...
}

相关问题