首页 > 解决方案 > 为房间数据库创建类型转换器的问题

问题描述

嘿伙计们,我正在尝试创建一个有空间的数据库,并且我有一个数据类作为实体,如下所示:

@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 的数据类的列表,如下所示:

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?
)

在这个类中,我有一些其他数据类的实例,例如天气值,它是天气数据类的列表,如下所示:

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

或 main 是另一个名为 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?
)

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

标签: androidandroid-sqliteandroid-roomtypeconverter

解决方案


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

所以(1)添加一个体现列表的新类,例如:-

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

( 2)在 WeatherForecastEntity 中使用:-

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

所以现在你有一个需要由 TypeConverter 转换的项目/变量/字段。

然后(3)创建一个类,例如(参见注释replacement/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函数将(a )转换为( DetailList 对象的 json 字符串,该字符串包含所有底层对象,例如字符串中的 Clouds、Sys、Main)。这在插入数据库时​​使用。DetailList List<Detail>String
  • toDetailList函数执行相反的操作并DetailList存储String. 这在从数据库中提取数据时使用。
  • 在这两种情况下,Room 都知道使用各自的TypeConverter(Room 知道由于函数使用的类型(即传递的类型和返回的类型))。您将始终拥有一对函数(或类中的许多对用于不同的 TypeConverters)

然后(4)在注释后将以下内容添加到@Database类中:-@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 注解
  • 请注意,为了方便/简洁.allowMainThreadQueries()已经使用,所以演示在主线程上运行(不推荐用于将要分发的应用程序)

现在使用您的类(创建类SysCloudsWind,因为它们不包含在问题中,因此它们很可能不会反映您的代码)和以下@DaoAllDao (允许在演示中插入和提取):-

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

然后在活动中使用以下代码来证明上述工作:-

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 次运行后,日志包括:-

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 :-

在此处输入图像描述


推荐阅读