android - 为房间数据库创建类型转换器时出现问题

标签 android android-sqlite android-room typeconverter

嘿伙计们,我正在尝试创建一个有空间的数据库,并且我有一个像这样的实体数据类:

@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。

最佳答案

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

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

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

(2)在WeatherForecastEntity中使用:-

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

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

然后(3)创建一个类,例如(请参阅有关放置/范围的注释):-

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 是根据函数使用的类型(即传递的类型和返回的类型)而知道的)。您总是会拥有一对函数(或者类中针对不同 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 已创建,因为它们未包含在问题中,因此它们很可能不会反射(reflect)您的代码) 以及以下 @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 次后,日志包括:-

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

enter image description here

关于android - 为房间数据库创建类型转换器时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69210605/

相关文章:

java - 无法使用自定义 sqlite 绑定(bind)创建数据库

android - 如何将 setTag 和 getTag 与自定义适配器一起使用

android - Sqlite 不同查询 From -To、To - From

android - 如何检测按钮背景资源(可绘制)android?

java - 如何将数据插入到 SQLite 数据库表的开头?

java - 无法创建 com.example.architectureexample.NoteViewModel 类的实例

kotlin - 无法在Room dao界面中使用Completable

java - 限制房间数据库中的行数

java - Android:使默认全息按钮不透明

Android应用内购买,取消订单