为房间数据库创建类型转换器的问题

problem with creating type converter for room database

大家好,我正在尝试创建一个有空间的数据库,我有一个数据 class 作为实体,如下所示:

@Entity(tableName = "forecast")
data class WeatherForecastEntity(
    @PrimaryKey(autoGenerate = false)
    val id:Int?,
    val city:String?,
    val country:String?,
    val timeZone:Int?,
    val sunrise: Int?,
    val sunset: Int?,
    val detailList:List<Detail>?
)

如您所见,我有一个名为 detailList 的值,它是一个名为 Detail 的数据 class 的列表,如下所示:

data class Detail(
    val clouds: Clouds?,
    val dt: Int?,
    val dt_txt: String?,
    val main: Main?,
    val pop: Int?,
    val sys: Sys?,
    val visibility: Int?,
    val weather: List<Weather>?,
    val wind: Wind?
)

并且在这个 class 中我有一些其他数据的实例 class 例如天气值是天气数据列表 class 是这样的:

data class Weather(
    val description: String?,
    val icon: String?,
    val id: Int?,
    val main: String?
)

或者 main 是另一个数据的实例 class 调用 Main 是这样的:

data class Main(
    val feels_like: Double?,
    val grnd_level: Int?,
    val humidity: Int?,
    val pressure: Int?,
    val sea_level: Int?,
    val temp: Double?
)

当我 运行 我的应用程序收到一个错误,提示我必须创建一个类型转换器,我真的不知道我应该如何处理这么多数据 classes互为实例。如果您能帮助我,我将不胜感激。顺便说一句,我在我的应用程序中使用了 RxJava 和 Gson。

首先。你不能(我相信)有一个 val detailList:List<Detail>?,你需要一个(不是列表)项目。

因此 (1) 添加一个包含列表的新 Class 例如:-

data class DetailList(
    val detailList: List<Detail>
)

(2) 在 WeatherForecastEntity 中使用 :-

/* val detailList:List<Detail>? */
val detailList: DetailList?
  • 原行已被注释掉

所以现在你有一个 item/variable/field 需要由 TypeConverter 转换。

然后 (3) 创建一个 class 例如(参见注释 placement/scope):-

class DetailListTypeConverter {

    @TypeConverter
    fun toDetailList(value: String): DetailList {
        Log.d("DBINFO_FROMJSON","Extracted>>${value}") /* just for demonstration */
        return Gson().fromJson(value,DetailList::class.java)
    }
    @TypeConverter
    fun fromDetailList(value: DetailList): String {
        return  Gson().toJson(value)
    }
}
  • fromDetailList 函数将 DetailList (a List<Detail>) 转换为 String (DetailList对象的一个​​json字符串,字符串中包含了Clouds、Sys、Main等所有底层对象)。插入数据库时​​使用。
  • toDetailList 函数执行相反的操作并构建 DetailList 对象 (以及底层对象)来自存储的 String。这在从数据库中提取数据时使用。
  • 在这两种情况下,Room 都知道使用相应的 TypeConverter(Room 知道是因为函数使用的类型(即传递的类型和返回的类型))。您总是会有一对函数 (或 class 中针对不同类型转换器的许多对)

然后(4)@Databaseclass注解后@Database添加如下内容:-

@TypeConverters(DetailListTypeConverter::class)

演示

例如(用于演示):-

@Database(entities = [WeatherForecastEntity::class],version = 1)
@TypeConverters(DetailListTypeConverter::class) /*<<<<<<<<<< ADDED >>>>>>>>>>*/
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDao(): AllDao

    companion object {
        private var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(
                    context,
                    TheDatabase::class.java,
                    "weather.db"
                )
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}
  • 即已添加 @TypeConverters 注释
  • 注意因为convenience/brevity已经使用了.allowMainThreadQueries(),所以demo运行在主线程(不推荐用于即将分发的App )

现在使用您的 classes (Classes Sys, CloudsWind 是因为它们未包含在问题中而创建的,因此它们很可能不会反映您的代码) 和以下 @Dao class AllDao (在演示中允许插入和提取):-

@Dao
abstract class AllDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract fun insert(weatherForecastEntity: WeatherForecastEntity): Long
    @Query("SELECT * FROM forecast")
    abstract fun getAllForecasts(): List<WeatherForecastEntity>
}

然后在 activity 中使用以下代码来证明上面的工作:-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: AllDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this)
        dao = db.getAllDao()
        dao.insert(createSomeData())
        for(wf: WeatherForecastEntity in dao.getAllForecasts()) {
            Log.d("DBINFO","City is ${wf.city}")
            for (dl: Detail in wf.detailList!!.detailList) {
                Log.d("DBINFO_DETAIL","Detail is ${dl.dt} Clouds is ${dl.clouds!!.name}")
            }
        }
    }

    fun createSomeData(): WeatherForecastEntity {
        val dtlList: ArrayList<Detail> = arrayListOf()
        val dtl1 = Detail(
            clouds = Clouds(name = "Cirrus", value = 10.2),
            dt = 10,
            dt_txt = "The dt for dt1",
            main = Main(feels_like = 1.1,grnd_level = 15, humidity = 65,sea_level = 100,pressure = 14,temp = 30.24),pop = 23000,visibility = 300,
            weather = listOf(Weather(description = "wet","weticon",0,"wet")),
            sys = Sys("sysname",10.333),
            wind = Wind(22.5,10.23)
        )
        val dtl2 = dtl1
        dtlList.add(dtl1)
        dtlList.add(dtl2)
        val detailList = DetailList(dtlList)
        return WeatherForecastEntity(null,"London","England",0,100,100,detailList)
    }
}
  • 上面的代码会在每次 运行 时插入一个新行,然后提取所有输出一些提取数据的行。

所以在 3 运行s 之后,日志包括:-

2021-09-18 08:19:33.825 D/DBINFO_FROMJSON: Extracted>>{"detailList":[{"clouds":{"name":"Cirrus","value":10.2},"dt":10,"dt_txt":"The dt for dt1","main":{"feels_like":1.1,"grnd_level":15,"humidity":65,"pressure":14,"sea_level":100,"temp":30.24},"pop":23000,"sys":{"name":"sysname","value":10.333},"visibility":300,"weather":[{"description":"wet","icon":"weticon","id":0,"main":"wet"}],"wind":{"direction":22.5,"speed":10.23}},{"clouds":{"name":"Cirrus","value":10.2},"dt":10,"dt_txt":"The dt for dt1","main":{"feels_like":1.1,"grnd_level":15,"humidity":65,"pressure":14,"sea_level":100,"temp":30.24},"pop":23000,"sys":{"name":"sysname","value":10.333},"visibility":300,"weather":[{"description":"wet","icon":"weticon","id":0,"main":"wet"}],"wind":{"direction":22.5,"speed":10.23}}]}
2021-09-18 08:19:33.858 I/chatty: uid=10200(a.a.so69210605kotlinroomgsontypeconverter) identical 4 lines
2021-09-18 08:19:33.864 D/DBINFO_FROMJSON: Extracted>>{"detailList":[{"clouds":{"name":"Cirrus","value":10.2},"dt":10,"dt_txt":"The dt for dt1","main":{"feels_like":1.1,"grnd_level":15,"humidity":65,"pressure":14,"sea_level":100,"temp":30.24},"pop":23000,"sys":{"name":"sysname","value":10.333},"visibility":300,"weather":[{"description":"wet","icon":"weticon","id":0,"main":"wet"}],"wind":{"direction":22.5,"speed":10.23}},{"clouds":{"name":"Cirrus","value":10.2},"dt":10,"dt_txt":"The dt for dt1","main":{"feels_like":1.1,"grnd_level":15,"humidity":65,"pressure":14,"sea_level":100,"temp":30.24},"pop":23000,"sys":{"name":"sysname","value":10.333},"visibility":300,"weather":[{"description":"wet","icon":"weticon","id":0,"main":"wet"}],"wind":{"direction":22.5,"speed":10.23}}]}
2021-09-18 08:19:33.871 D/DBINFO: City is London
2021-09-18 08:19:33.871 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.871 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.871 D/DBINFO: City is London
2021-09-18 08:19:33.871 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.871 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.872 D/DBINFO: City is London
2021-09-18 08:19:33.872 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
2021-09-18 08:19:33.872 D/DBINFO_DETAIL: Detail is 10 Clouds is Cirrus
  • 前 3 行来自 TypeConverter,显示 json 已被提取。
  • 后续行显示数据已成功提取,detailList 包含详细信息(每行 2 个)(尽管数据相同)。

数据库本身,根据 Android Studio 的 App Inspector :-