Android 房间持久库-如何插入具有 List 对象字段的类

Android 房间持久图书馆中,如何将整个 Model 对象插入到本身就有另一个列表的表中。

让我告诉你我的意思:

@Entity(tableName = TABLE_NAME)
public class CountryModel {


public static final String TABLE_NAME = "Countries";


@PrimaryKey
private int idCountry;


private List<CountryLang> countryLang = null;


public int getIdCountry() {
return idCountry;
}


public void setIdCountry(int idCountry) {
this.idCountry = idCountry;
}


public String getIsoCode() {
return isoCode;
}


public void setIsoCode(String isoCode) {
this.isoCode = isoCode;
}


/**
here i am providing a list of coutry information how to insert
this into db along with CountryModel at same time
**/
public List<CountryLang> getCountryLang() {
return countryLang;
}


public void setCountryLang(List<CountryLang> countryLang) {
this.countryLang = countryLang;
}
}

我的 DAO 是这样的:

@Dao
public interface CountriesDao{


@Query("SELECT * FROM " + CountryModel.TABLE_NAME +" WHERE isoCode =:iso_code LIMIT 1")
LiveData<List<CountryModel>> getCountry(String iso_code);


@Query("SELECT * FROM " + CountryModel.TABLE_NAME )
LiveData<List<CountryModel>> getAllCountriesInfo();


@Insert(onConflict = REPLACE)
Long[] addCountries(List<CountryModel> countryModel);


@Delete
void deleteCountry(CountryModel... countryModel);


@Update(onConflict = REPLACE)
void updateEvent(CountryModel... countryModel);
}

当我调用 database.CountriesDao().addCountries(countryModel);时,我得到下面的房间 db 编译错误: 错误: (58,31)错误: 无法找出如何将此字段保存到数据库中。可以考虑为其添加类型转换器。

应该有另一个桌子叫 CountryLang 吗?如果是这样,如何告诉房间连接上插入语句?

CountryLang 对象本身是这样的:

public class CountryLang {




private int idCountry;


private int idLang;


private String name;


public int getIdCountry() {
return idCountry;
}


public void setIdCountry(int idCountry) {
this.idCountry = idCountry;
}


public int getIdLang() {
return idLang;
}


public void setIdLang(int idLang) {
this.idLang = idLang;
}


public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


}

答案是这样的:

"country_lang": [
{
"id_country": 2,
"id_lang": 1,
"name": "Austria"
}
]

每个国家,所以它不会是一个以上的项目在这里。我舒适的设计它只有一个项目在国家的长长的名单。所以我可以为 country _ lang 创建一个表,然后把它链接到 CountryModel。但是怎么做呢?我可以用外键吗?我希望我不用用平面文件夹。所以你的意思是我必须把它存储为 json?是否建议暂时不要使用房间?用什么代替呢?

85190 次浏览

你不能。

实现这一点的唯一方法是使用 @ ForeignKey约束。如果希望仍然将对象列表保留在父 POJO 中,则必须使用 @ 忽略或提供 @ TypeConverter

更多信息,请关注这篇博文:-

Https://www.bignerdranch.com/blog/room-data-storage-on-android-for-everyone/

及示例代码:-

Https://github.com/googlesamples/android-architecture-components

正如 Omkar 所说,您不能这样做。在这里,我将根据文档描述为什么应该始终使用 @Ignore注释: https://developer.android.com/training/data-storage/room/referencing-data.html#understand-no-object-references

您将处理表中的 Country 对象,以检索其权限的数据; Language 对象将转到另一个表,但您可以保留相同的 Dao:

  • Country 和 Language 对象是独立的,只需在 Language 实体中定义主键和更多字段(country Id,Language ageId)。当活动线程是 Worker 线程时,您可以在 Repository 类中串行地保存它们: 两个插入 Tao 的请求。
  • 要加载 Country 对象,您需要具有 CountryId。
  • 要加载相关的 Language 对象,您已经有了 country Id,但是您需要等待该国家首先加载,然后才能加载语言,这样您就可以在父对象中设置它们,并只返回父对象。
  • 当您加载国家时,您可以在 Repository 类中串行执行此操作,因此您将同步加载国家,然后加载语言,就像您在服务器端所做的那样!(没有 ORM 库)。

您可以使用 类型转换器GSON轻松地插入带有列表对象字段的类,

public class DataConverter {


@TypeConverter
public String fromCountryLangList(List<CountryLang> countryLang) {
if (countryLang == null) {
return (null);
}
Gson gson = new Gson();
Type type = new TypeToken<List<CountryLang>>() {}.getType();
String json = gson.toJson(countryLang, type);
return json;
}


@TypeConverter
public List<CountryLang> toCountryLangList(String countryLangString) {
if (countryLangString == null) {
return (null);
}
Gson gson = new Gson();
Type type = new TypeToken<List<CountryLang>>() {}.getType();
List<CountryLang> countryLangList = gson.fromJson(countryLangString, type);
return countryLangList;
}
}

接下来,将@TypeConverters 注释添加到 AppDatabase 类中

    @Database(entities = {CountryModel.class}, version = 1)
@TypeConverters({DataConverter.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract CountriesDao countriesDao();
}

关于 室内型号转换器的更多信息,请查看我们的博客 给你官方文件

以下是阿曼•古普塔(Aman Gupta)在 Kotlin 为那些喜欢复制粘贴的懒惰谷歌员工设计的转换器:

class DataConverter {


@TypeConverter
fun fromCountryLangList(value: List<CountryLang>): String {
val gson = Gson()
val type = object : TypeToken<List<CountryLang>>() {}.type
return gson.toJson(value, type)
}


@TypeConverter
fun toCountryLangList(value: String): List<CountryLang> {
val gson = Gson()
val type = object : TypeToken<List<CountryLang>>() {}.type
return gson.fromJson(value, type)
}
}

此外,将@TypeConverters 注释添加到 AppDatabase 类中

@Database(entities = arrayOf(CountryModel::class), version = 1)
@TypeConverters(DataConverter::class)
abstract class AppDatabase : RoomDatabase(){
abstract fun countriesDao(): CountriesDao
}


为自定义对象字段添加@insert (参考以下例子)

//this class refers to pojo which need to be stored
@Entity(tableName = "event_listing")
public class EventListingEntity implements Parcelable {


@Embedded  // <<<< This is very Important in case of custom obj
@TypeConverters(Converters.class)
@SerializedName("mapped")
public ArrayList<MappedItem> mapped;


//provide getter and setters
//there should not the duplicate field names
}


//add converter so that we can store the custom object in ROOM database
public class Converters {
//room will automatically convert custom obj into string and store in DB
@TypeConverter
public static String
convertMapArr(ArrayList<EventListingEntity.MappedItem> list) {
Gson gson = new Gson();
String json = gson.toJson(list);
return json;
}


//At the time of fetching records room will automatically convert string to
// respective obj
@TypeConverter
public static ArrayList<EventsListingResponse.MappedItem>
toMappedItem(String value) {
Type listType = new
TypeToken<ArrayList<EventsListingResponse.MappedItem>>() {
}.getType();
return new Gson().fromJson(value, listType);
}


}


//Final db class
@Database(entities = {EventsListingResponse.class}, version = 2)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
....
}

为了解决这个问题,我使用 类型转换莫希将列表解析为字符串。

按照以下步骤:

创建一个带有转换器的类。

class Converters {


private val moshi = Moshi.Builder().build()
private val listMyData : ParameterizedType = Types.newParameterizedType(List::class.java, MyModel::class.java)
private val jsonAdapter: JsonAdapter<List<MyModel>> = moshi.adapter(listMyData)


@TypeConverter
fun listMyModelToJsonStr(listMyModel: List<MyModel>?): String? {
return jsonAdapter.toJson(listMyModel)
}


@TypeConverter
fun jsonStrToListMyModel(jsonStr: String?): List<MyModel>? {
return jsonStr?.let { jsonAdapter.fromJson(jsonStr) }
}
}

在 RoomDatabase 类中定义带有 Converters 的类。

@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {...}

... 您将@TypeConverters 注释添加到 AppDatabase 类中,这样 Room 就可以使用您为 AppDatabase 中的每个实体和 DAO 定义的转换器..。

... 有时候,你的应用程序需要使用一个定制的数据类型,你想把它的值存储在一个单独的数据库列中。要添加对自定义类型的这种支持,您需要提供一个 类型转换器,它可以将自定义类与 Room 可以持久存储的已知类型进行转换。

参考文献:

Moshi 解析列表(Kotlin)

如何解析一个列表? # 78(由杰克 · 沃顿回答)

使用类型转换器(官方文档)

莫希图书馆

我做了一些类似于@Daniel Wilson 的事情,但是,我使用了 莫希,因为它是建议的库。要了解更多关于莫希和高森之间的区别,我建议你看看 ABc1。

在我的例子中,我必须在 Room数据库中存储 List<LatLng>。如果你不知道 LatLng是用来处理地理坐标的也就是经纬度。 为了达到这个目的,我使用了以下代码:

class Converters {


private val adapter by lazy {
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val listMyData = Types.newParameterizedType(List::class.java, LatLng::class.java)
return@lazy moshi.adapter<List<LatLng>>(listMyData)
}


@TypeConverter
fun toJson(coordinates: List<LatLng>) : String {
val json = adapter.toJson(coordinates)
return json
}


@TypeConverter
fun formJson(json: String) : List<LatLng>? {
return adapter.fromJson(json)
}
}

我来晚了,但我真的建议使用 Kotlin Serialization。

class CountryLangConverter {
@TypeConverter
fun toCountryLang(countryLang: String): CountryLang =
Json.decodeFromString(countryLang)


@TypeConverter
fun fromCountryLang(countryLang: CountryLang): String =
Json.encodeToString(countryLang)
}

不要忘记检查房间和它的类型转换器的 正式文件