#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
Функция преобразует aDetailList
(aList<Detail>
) в aString
(строку json объекта DetailList, которая содержит все базовые объекты, такие как Облака, система, Main в строке). Это используется при вставке в базу данных.toDetailList
Функция делает обратное и создаетDetailList
объект (и базовые объекты) из сохраненногоString
. Это используется при извлечении данных из базы данных.- В обоих случаях Комната знает, что нужно использовать соответствующий преобразователь типов (Комната знает из-за типов, используемых функциями (т. Е. переданный тип и возвращенный тип)). У вас всегда будет пара функций (или много пар в классе для разных преобразователей типов).
Затем (4) добавьте следующее в @Database
класс после @Database
аннотации :-
@TypeConverters(DetailListTypeConverter::class)
- примечание смотрите/читайте https://developer.android.com/reference/androidx/room/TypeConverters, в отношении размещения/области применения преобразователей типов
ДЕМОНСТРАЦИЯ
Например (как в демо-версии) :-
@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 :-