Android Studio 文件室数据库中的类型转换器出现问题

2skhul33  于 2022-11-16  发布在  Android
关注(0)|答案(3)|浏览(116)

我尝试在Android(Kotlin)中使用类型转换器,所以我使用类型转换器类,但我越来越困惑,就像在云中一样,我有一个单一的变量,所以我已经返回了它,但

@Entity(tableName = "WeatherDb")
data class WeatherDTO(
    val base: String,
    val clouds: Clouds,
    val cod: Int,
    val coord: Coord,
    val dt: Int,
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val main: Main,
    val name: String,
    val sys: Sys,
    val timezone: Int,
    val visibility: Int,
    val weather: List<Weather>,
    val wind: Wind
)

class TypeConverters {
    @TypeConverter
    fun fromCloudsToDouble(clouds: Clouds): Int {
        return clouds.all
    }

    fun fromCoordToDouble(coord: Coord): Double {

    }
}

在coord类中有多个不同的数据类型,如何转换?

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

Clouds.kt

data class Clouds(
    val all: Int
)

Coord.kt

data class Coord(
    val lat: Double,
    val lon: Double
)

Main.kt

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

Sys.kt

data class Sys(
    val country: String,
    val id: Int,
    val sunrise: Int,
    val sunset: Int,
    val type: Int
)

Weather.kt

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

Wind.kt

data class Wind(
    val deg: Int,
    val gust: Double,
    val speed: Double
)

WeatherViewModel.kt

@HiltViewModel
class WeatherViewModel @Inject constructor(
    private val repo:WeatherRepository,
    private val application: Application,
    private val WeatherDb:WeatherDB,
    private val fusedLocationProviderClient: FusedLocationProviderClient
) :ViewModel(){

    private val _resp = MutableLiveData<WeatherDTO>()
    val weatherResp:LiveData<WeatherDTO>
    get() = _resp


    private val _cord = MutableLiveData<Coord>()
    val cord:LiveData<Coord>
        get() = _cord

    var locality:String = ""

   fun getWeather(latitude:Double,longitude:Double) =
        viewModelScope.launch {
            repo.getWeather(latitude,longitude).let { response->
                if(response.isSuccessful){
                    Log.d("response","${response.body()}")
                    WeatherDb.WeatherDao().insertWeather(response.body()!!)
                    _resp.postValue(response.body())
                }else{
                    Log.d("Weather Error","getWeather Error Response: ${response.message()}")
                }
            }
        }

    fun fetchLocation():Boolean{
        val task = fusedLocationProviderClient.lastLocation
        if(ActivityCompat.checkSelfPermission(application,android.Manifest.permission.ACCESS_FINE_LOCATION)
            !=PackageManager.PERMISSION_GRANTED &&
            ActivityCompat.checkSelfPermission(application,android.Manifest.permission.ACCESS_COARSE_LOCATION)
            !=PackageManager.PERMISSION_GRANTED
        ){
            return true
        }
        task.addOnSuccessListener {
            if(it!=null){
                getWeather(it.latitude,it.longitude)
                getAddressName(it.latitude,it.longitude)
                Log.d("localityname", locality)
            }
        }
        return true

    }

    private fun fetchLocationDetails(){

    }
    private fun getAddressName(lat:Double,long:Double):String{

        var addressName = " "
        val geoCoder = Geocoder(application, Locale.getDefault())
        val address = geoCoder.getFromLocation(lat,long,1)
        if (address != null) {
            addressName = address[0].adminArea
        }
        locality = addressName

        Log.d("subadmin",addressName.toString())
        Log.d("Address", addressName)
        return addressName

    }

    fun getCoordinates(cord:String){
        val geocoder = Geocoder(application,Locale.getDefault())
        val address = geocoder.getFromLocationName(cord,2)
        val result = address?.get(0)
        if (result != null) {


            getWeather(result.latitude,result.longitude)
            getAddressName(result.latitude,result.longitude)
        }

    }




}
jchrr9hc

jchrr9hc1#

这是我在Kotlin的转换器:

class Converters {

    @TypeConverter
    fun valueFromDomainToStorage(value: Value): String {
        return value.convertToJson()
    }

    @TypeConverter
    fun valueFromStorageToDomain(str: String): Value {
        // we can not create an empty instance of value as TypeDecoder.java should call non-empty constructor
        return Value(
            "just a stub",
            BigInteger.valueOf(0),
            BigInteger.valueOf(0),
            false,
            BigInteger.valueOf(0)
        )
            .fromJson(str)
    }
}

其中.convertToJson().fromJson(str)作为Value类中的扩展实现:

fun Value.convertToJson(): String {
    val result = JSONObject()
    result.put(ValueConst.OFFER_FIELD, offer)
    result.put(ValueConst.AVAILABLE_SINCE, availableSince.toLong())
    result.put(ValueConst.AVAILABLE_END, availabilityEnd.toLong())
    result.put(ValueConst.IS_CONSUMED, isConsumed)
    result.put(ValueConst.LOCKED_UNTIL, lockedUntil)
    return result.toString()
}

fun Value.fromJson(json: String): Value {
    val subj = JSONObject(json)
    return Value(
        subj.optString(ValueConst.OFFER_FIELD),
        BigInteger.valueOf(subj.optLong(ValueConst.AVAILABLE_SINCE)),
        BigInteger.valueOf(subj.optLong(ValueConst.AVAILABLE_END)),
        subj.optBoolean(ValueConst.IS_CONSUMED),
        BigInteger.valueOf(subj.optLong(ValueConst.LOCKED_UNTIL))
    )
}

您应该为每个非本机类类型实现Converter类。不要忘记在数据库上注册转换器:

@Database(entities = [ChainTransaction::class], version = 1, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase: RoomDatabase() {

当您编译完代码并在以后引入新的更改时,您还必须增加version参数以使更改生效:

@Database(entities = [ChainTransaction::class], version = 2, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase: RoomDatabase() {

以下是有关此主题的官方文档和培训:https://developer.android.com/training/data-storage/room

vlurs2pr

vlurs2pr2#

所以我使用了类型转换器类,但是我感到很困惑
SQLite(Room是一个面向对象的 Package 器)不是一个面向对象(或感知)的数据库。它是一个可以存储基本类型数据的数据库,这些数据是

  • 整数(如整数或长整数)、真实的
  • 真实的(如浮点型或双精度型)
  • TEXT(如字符串)
  • BLOB(例如ByteArray)
  • 空值

因此,要存储坐标、云或天气类型......您有三个选项:
1.嵌入类,在这种情况下,将从嵌入类复制字段(如果嵌入类包含不支持的类型,则会很复杂)。答案中未涉及
1.将类作为一个表,在它自己的权限内,在它和父表(WeatherDTO)之间有一个关系。答案中未涉及
1.以将类转换为SQLite类型之一(其中TEXT或BLOB可能仅适用)。
考虑到选项3(TyepConverters)转换数据的作用很小(如果有),只使用存储数据,因为您将无法检索数据。
因此,类型转换器应始终成对使用。

  • 其中一个会从类别转换成可储存的型别。
  • 另一个方法是从存储类型转换为类。

因此,您需要相当多的类型转换器,即每个字段2个:-

  • clouds(云类)
  • 类坐标
  • 主(主类)
  • sys(系统类)
  • 天气(类列表)
  • 风(风类)

它是Room查找相应类型转换器的字段的类。
转换对象(也称为类)最简单的方法之一是将对象转换为JSON表示形式。尽管这一方法的复杂性在于有许多JSON库,它们通常会有差异。
在下面的例子中,我们使用了Google的JSON库。但是,在Room中使用这个库似乎并不直接支持List的使用,<the_class>例如List。

  • 其相关性为(例如)implementation 'com.google.code.gson:gson:2.10'

作为一个新的类**WeatherList**已经按照如下使用:-

data class WeatherList(
    val weatherList: List<Weather>
)

WeatherDTO类已更改为按以下方式使用它:-

....
//val weather: List<Weather>,
val weather: WeatherList,
....

因此,TypeConverters类可以是:-

class TypeConverters {
    @TypeConverter
    fun fromCloudsToJSONString(clouds: Clouds): String = Gson().toJson(clouds)
    @TypeConverter
    fun toCloudsFromJSONString(jsonString: String): Clouds = Gson().fromJson(jsonString,Clouds::class.java)
    @TypeConverter
    fun fromCoordToJSONString(coord: Coord): String = Gson().toJson(coord)
    @TypeConverter
    fun toCoordFromJSONString(jsonString: String): Coord = Gson().fromJson(jsonString,Coord::class.java)
    @TypeConverter
    fun fromMaintoJSONString(main: Main): String = Gson().toJson(main)
    @TypeConverter
    fun toMainFromJSONString(jsonString: String): Main = Gson().fromJson(jsonString,Main::class.java)
    @TypeConverter
    fun fromSysToJSONString(sys: Sys): String = Gson().toJson(sys)
    @TypeConverter
    fun toSysFromJSONString(jsonString: String): Sys = Gson().fromJson(jsonString,Sys::class.java)
    @TypeConverter
    fun fromWeatherListFromJSONString(weatherList: WeatherList): String = Gson().toJson(weatherList)
    @TypeConverter
    fun toWeatherListFromJSOnString(jsonString: String): WeatherList = Gson().fromJson(jsonString,WeatherList::class.java)
    @TypeConverter
    fun fromWindToJSONString(wind: Wind): String = Gson().toJson(wind)
    @TypeConverter
    fun toWindFromJSONString(jsonString: String): Wind = Gson().fromJson(jsonString,Wind::class.java)
}

因此,所有不直接支持的类型/类/对象都将转换为类型/类/对象的JSON字符串表示,或从JSON字符串表示转换而来。
请注意,您需要添加@TypeConverters(@TypeConverters( value = [<????>.TypeConverters::class])。其中必须区分您的项目TypeConverters类和Room的 (TypeConverters可能不是该类的最佳名称,重命名它将克服区分的需要)

工作示例

下面将把上述内容付诸实施。
由于该问题不包括基本类别,因此使用了以下内容:

data class Coord(
    val longitude: Double,
    val latitude: Double
)
data class Clouds(
    val cover: Double,
    val type: String
)
data class Main(
    val main: Double
)
data class Sys(
    val sys: Double
)
data class WeatherList(
    val weatherList: List<Weather>
)
data class Weather(
    val weather: Double
)
data class Wind(
    val wind: Double
)

@道注解界面也是由简单的:-

@Dao
interface AllDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(weatherDTO: WeatherDTO)
    @Query("SELECT * FROM weatherdb")
    fun getAllFromWeatherDB(): List<WeatherDTO>
}

此外,@Database注解抽象类是由以下内容组成的:

@TypeConverters( value = [a.a.so74384736typeconverterconfusion.TypeConverters::class])
@Database(entities = [WeatherDTO::class], exportSchema = false, version = 1)
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,"the_database.db")
                    .allowMainThreadQueries()
                    .build()
            }
            return instance as TheDatabase
        }
    }
}
  • 请注意用于区分TypeConverters类和Room的TypeConverters类的包名称
  • 套件名称无法在其他地方使用,因此如果复制上述名称,就必须加以变更。我们不希望复制并使用完整的程式码。程式码的设计目的只是为了示范TypeConverters。

最后一些活动代码实际做一些事情(存储和检索一些数据):-

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(
            WeatherDTO(
            "base001",
            Clouds(25.5,"cumulus"),10,
                Coord(10.567,30.345),
                11,
                12,
                Main(12345.67890),
                "thename",
                Sys(9.87654321),
                14,
                1000,
                WeatherList(listOf(Weather(5.1234),Weather(6.5432), Weather(7.6543))),
                Wind(23.12)
            )
        )
        for (wdto in dao.getAllFromWeatherDB()) {
            Log.d("DBINFO","base = ${wdto.base} longitude = ${wdto.coord.longitude} latitude = ${wdto.coord.latitude} etc ....")
        }
    }
}

结果

运行时,日志包含预期内容:-

D/DBINFO: base = base001 longitude = 10.567 latitude = 30.345 etc ....

使用App Inspection,则数据库如下所示:-

  • 转换为JSON字符串的字段已突出显示。
  • 显然,由于组成的类,数据很可能不会完全如您所期望的那样。
qgelzfjb

qgelzfjb3#

接上一个答案**@嵌入式与类型转换器**
从前面的回答中可以看出,使用TypeConverters存在一些问题。从数据库的Angular 来看,TypeConverters将不可避免地包含与标准化相反的膨胀/不必要的数据(而不是不必要地存储重复数据)。
举个例子,JSON表示法对于每一行都包含完全相同的字段名,这浪费了存储空间,所有行都将有存储分隔符的额外开销([ s和] s,{ s和} s,(一个月四个月一个月一个月一个月)。此外,由于膨胀并且还由于多个值被存储在单个列中,因此实际使用所存储的数据可能变得复杂,并且因此可能限制性。
如果不存储膨胀数据,效率会更高,而且从数据库的Angular (查询数据以进行检索)来看,如果不在单个列中存储多个值,则可以消除复杂性并增强存储数据的可用性。
使用@Embedded注解可以非常容易地消除膨胀。请考虑以下内容(WeatherDTO类/实体的替代版本):-

@Entity(tableName = "WeatherDbAlternative1")
data class WeatherDTOAlternative1(
    val base: String,
    @Embedded
    val clouds: Clouds,
    val cod: Int,
    @Embedded
    val coord: Coord,
    val dt: Int,
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    @Embedded
    val main: Main,
    val name: String,
    @Embedded
    val sys: Sys,
    val timezone: Int,
    val visibility: Int,
    //val weather: List<Weather>,
    /* Unable to embed directly so not embedding */
    val weather: WeatherList,
    @Embedded
    val wind: Wind
)

除了weather字段,我们所做的就是添加@Embedded注解。注意字段的类都有Room直接支持的字段类型。
将此实体添加到@Database注解中,并在@Dao注解类中添加几个附加函数,如下所示:

@Query("SELECT * FROM weatherdbalternative1")
fun getAllFromWeatherDBAlternative1(): List<WeatherDTOAlternative1>
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(weatherDTOAlternative1: WeatherDTOAlternative1)

然后修改活动代码,以包括:

/*ALTERNATIVE 1 All but WeatherList embedded */
    dao.insert(
        WeatherDTOAlternative1(
            "base001A",
            Clouds(25.5, "cumulus"),
            10,
            Coord(10.567, 30.345),
            11,
            12,
            Main(12345.67890),
            "thenameA1",
            Sys(9.87654321),
            14,
            1000,
            WeatherList(listOf(Weather(5.1234), Weather(6.5432), Weather(7.6543))),
            Wind(23.12)
        )
    )
    for (wdto in dao.getAllFromWeatherDBAlternative1()) {
        Log.d(
            "DBINFO",
            "base = ${wdto.base} longitude = ${wdto.coord.longitude} latitude = ${wdto.coord.latitude} etc ...."
        )
    }

现在日志中的结果包括:-

D/DBINFO: base = base001 longitude = 10.567 latitude = 30.345 etc ....
D/DBINFO: base = base001A longitude = 10.567 latitude = 30.345 etc ....
  • 即有效地存储和检索相同的数据

然而,数据现在存储在数据库中,如下所示(忽略天气字段):-

  • 即,所存储的数据更干净,但是以附加列为代价(这可能是有利的)。
  • 另外,虽然不明显,但是具有@Embedded注解的字段不需要TypeConverter。

相关问题