проблема с созданием преобразователя типов для базы данных комнат

#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 в своем приложении.

Ответ №1:

Первый. У вас не может (я полагаю) быть val detailList:List<Detail>? , вам нужен один (не список) элемент.

Итак (1) добавьте новый класс, который содержит список, например :-

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

и (2) при использовании в погодных условиях :-

 /* val detailList:List<Detail>? */
val detailList: DetailList?
 
  • исходная строка была закомментирована

Итак, теперь у вас есть один элемент/переменная/поле, которое должно быть преобразовано преобразователем типов.

затем (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 Функция преобразует a DetailList (a List<Detail> ) в a String (строку json объекта DetailList, которая содержит все базовые объекты, такие как Облака, система, Main в строке). Это используется при вставке в базу данных.
  • toDetailList Функция делает обратное и создает DetailList объект (и базовые объекты) из сохраненного String . Это используется при извлечении данных из базы данных.
  • В обоих случаях Комната знает, что нужно использовать соответствующий преобразователь типов (Комната знает из-за типов, используемых функциями (т. Е. переданный тип и возвращенный тип)). У вас всегда будет пара функций (или много пар в классе для разных преобразователей типов).

Затем (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() была использована демонстрация, поэтому демонстрация выполняется в основном потоке (не рекомендуется для приложения, которое будет распространяться).

Теперь используйте свои классы (классы Sys, Облака и Ветер были созданы, поскольку они не включены в вопрос, поэтому они, скорее всего, НЕ будут отражать ваш код) и следующий @Dao класс AllDao (чтобы разрешить вставку и извлечение в демо):-

 @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 строки из преобразователя типов показывают, что json был извлечен.
  • Последующие строки показывают, что данные были успешно извлечены и что в detailList них содержится подробная информация (по 2 в строке) (хотя и идентичные данные).

Сама база данных, согласно инспектору приложений Android Studio :-

введите описание изображения здесь