Android Room Database:如何处理实体中的 Arraylist?
Android Room Database: How to handle Arraylist in an Entity?
我刚刚实现了用于离线数据保存的 Room。但是在实体 class 中,我收到以下错误:
Error:(27, 30) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
而class如下:
@Entity(tableName = "firstPageData")
public class MainActivityData {
@PrimaryKey
private String userId;
@ColumnInfo(name = "item1_id")
private String itemOneId;
@ColumnInfo(name = "item2_id")
private String itemTwoId;
// THIS IS CAUSING THE ERROR... BASICALLY IT ISN'T READING ARRAYS
@ColumnInfo(name = "mylist_array")
private ArrayList<MyListItems> myListItems;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public ArrayList<MyListItems> getMyListItems() {
return myListItems;
}
public void setCheckListItems(ArrayList<MyListItems> myListItems) {
this.myListItems = myListItems;
}
}
所以基本上我想将 ArrayList 保存在数据库中,但我无法找到与之相关的任何内容。你能指导我如何使用 Room 保存数组吗?
注意:MyListItems Pojo class 包含 2 个字符串(截至目前)
提前致谢。
选项 #1:让 MyListItems
成为 @Entity
,就像 MainActivityData
一样。 MyListItems
会设置 @ForeignKey
回到 MainActivityData
。但是,在这种情况下,MainActivityData
不能有 private ArrayList<MyListItems> myListItems
,因为在 Room 中,实体不引用其他实体。不过,视图模型或类似的 POJO 构造可能具有 MainActivityData
及其关联的 ArrayList<MyListItems>
。
选项 #2:设置一对 @TypeConverter
方法来将 ArrayList<MyListItems>
转换为某种基本类型(例如 String
,例如使用 JSON作为存储格式)。现在,MainActivityData
可以直接拥有它的 ArrayList<MyListItems>
。但是,MyListItems
不会有单独的 table,因此您无法很好地查询 MyListItems
。
Type Converter 是专门为此制作的。在您的情况下,您可以使用下面给出的代码片段将数据存储在数据库中。
public class Converters {
@TypeConverter
public static ArrayList<String> fromString(String value) {
Type listType = new TypeToken<ArrayList<String>>() {}.getType();
return new Gson().fromJson(value, listType);
}
@TypeConverter
public static String fromArrayList(ArrayList<String> list) {
Gson gson = new Gson();
String json = gson.toJson(list);
return json;
}
}
并像这样在您的 Room DB 中提及 class
@Database (entities = {MainActivityData.class},version = 1)
@TypeConverters({Converters.class})
更多信息here
收到与上述相同的错误消息。
我想补充一点:如果您在@Query 中收到此错误消息,您应该在@Query 注释上方添加@TypeConverters。
示例:
@TypeConverters(DateConverter.class)
@Query("update myTable set myDate=:myDate where id = :myId")
void updateStats(int myId, Date myDate);
.....
public class DateConverter {
@TypeConverter
public static Date toDate(Long timestamp) {
return timestamp == null ? null : new Date(timestamp);
}
@TypeConverter
public static Long toTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
Kotlin 类型转换器版本:
class Converters {
@TypeConverter
fun listToJson(value: List<JobWorkHistory>?) = Gson().toJson(value)
@TypeConverter
fun jsonToList(value: String) = Gson().fromJson(value, Array<JobWorkHistory>::class.java).toList()
}
我使用了 JobWorkHistory
对象,请使用您自己的对象
@Database(entities = arrayOf(JobDetailFile::class, JobResponse::class), version = 1)
@TypeConverters(Converters::class)
abstract class MyRoomDataBase : RoomDatabase() {
abstract fun attachmentsDao(): AttachmentsDao
}
这就是我处理列表转换的方式
public class GenreConverter {
@TypeConverter
public List<Integer> gettingListFromString(String genreIds) {
List<Integer> list = new ArrayList<>();
String[] array = genreIds.split(",");
for (String s : array) {
if (!s.isEmpty()) {
list.add(Integer.parseInt(s));
}
}
return list;
}
@TypeConverter
public String writingStringFromList(List<Integer> list) {
String genreIds = "";
for (int i : list) {
genreIds += "," + i;
}
return genreIds;
}}
然后在数据库上我做如下所示
@Database(entities = {MovieEntry.class}, version = 1)
@TypeConverters(GenreConverter.class)
下面是相同的 kotlin 实现;
class GenreConverter {
@TypeConverter
fun gettingListFromString(genreIds: String): List<Int> {
val list = mutableListOf<Int>()
val array = genreIds.split(",".toRegex()).dropLastWhile {
it.isEmpty()
}.toTypedArray()
for (s in array) {
if (s.isNotEmpty()) {
list.add(s.toInt())
}
}
return list
}
@TypeConverter
fun writingStringFromList(list: List<Int>): String {
var genreIds=""
for (i in list) genreIds += ",$i"
return genreIds
}}
使用房间的官方解决方案,@Embedded 注释:
@Embedded(prefix = "mylist_array") private ArrayList<MyListItems> myListItems
添加 @TypeConverters
和转换器 class 作为参数
数据库和 Dao class,使我的查询有效
此答案使用Kotin 以逗号分隔并构造以逗号分隔的字符串。逗号需要放在除最后一个元素之外的所有元素的末尾,因此这也将处理单个元素列表。
object StringListConverter {
@TypeConverter
@JvmStatic
fun toList(strings: String): List<String> {
val list = mutableListOf<String>()
val array = strings.split(",")
for (s in array) {
list.add(s)
}
return list
}
@TypeConverter
@JvmStatic
fun toString(strings: List<String>): String {
var result = ""
strings.forEachIndexed { index, element ->
result += element
if(index != (strings.size-1)){
result += ","
}
}
return result
}
}
Json 转换在内存方面的扩展性不佳 allocation.I 宁愿选择与上面的响应类似但具有一些可空性的东西。
class Converters {
@TypeConverter
fun stringAsStringList(strings: String?): List<String> {
val list = mutableListOf<String>()
strings
?.split(",")
?.forEach {
list.add(it)
}
return list
}
@TypeConverter
fun stringListAsString(strings: List<String>?): String {
var result = ""
strings?.forEach { element ->
result += "$element,"
}
return result.removeSuffix(",")
}
}
对于简单的数据类型可以使用上面的,否则对于复杂的数据类型 Room 提供 Embedded
List<String>
转换器的更好版本
class StringListConverter {
@TypeConverter
fun fromString(stringListString: String): List<String> {
return stringListString.split(",").map { it }
}
@TypeConverter
fun toString(stringList: List<String>): String {
return stringList.joinToString(separator = ",")
}
}
这是将 customObject 类型添加到 Room DB 的示例 table。
https://mobikul.com/insert-custom-list-and-get-that-list-in-room-database-using-typeconverter/
添加类型转换器很容易,我只需要一个可以将对象列表转换为字符串的方法,以及一个可以进行相反操作的方法。我为此使用了 gson。
public class Converters {
@TypeConverter
public static String MyListItemListToString(List<MyListitem> list) {
Gson gson = new Gson();
return gson.toJson(list);
}
@TypeConverter
public static List<Integer> stringToMyListItemList(@Nullable String data) {
if (data == null) {
return Collections.emptyList();
}
Type listType = new TypeToken<List<MyListItem>>() {}.getType();
Gson gson = new Gson();
return gson.fromJson(data, listType);
}
}
然后我在实体中的字段中添加了注释:
@TypeConverters(Converters.class)
public final ArrayList<MyListItem> myListItems;
在我的案例中,问题是泛型
基于这个答案
使用 List 而不是 ArrayList
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class IntArrayListConverter {
@TypeConverter
fun fromString(value: String): List<Int> {
val type = object: TypeToken<List<Int>>() {}.type
return Gson().fromJson(value, type)
}
@TypeConverter
fun fromArrayList(list: List<Int>): String {
val type = object: TypeToken<List<Int>>() {}.type
return Gson().toJson(list, type)
}
}
它不需要添加 @TypeConverters(IntArrayListConverter::class) 在 dao 中查询 class 也不需要在 Entity class 中查询
只需将 @TypeConverters(IntArrayListConverter::class) 添加到数据库 class
@Database(entities = [MyEntity::class], version = 1, exportSchema = false)
@TypeConverters(IntArrayListConverter::class)
abstract class MyDatabase : RoomDatabase() {
当我们使用 TypaConverters 时,数据类型应该是 returnTypeConverter 方法的类型
.示例 TypeConverter 方法 Return String 然后添加 Table COloum 应该是 String
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
// Since we didn't alter the table, there's nothing else to do here.
database.execSQL("ALTER TABLE "+ Collection.TABLE_STATUS + " ADD COLUMN deviceType TEXT;");
database.execSQL("ALTER TABLE "+ Collection.TABLE_STATUS + " ADD COLUMN inboxType TEXT;");
}
};
@Query("SELECT * FROM business_table")
abstract List<DatabaseModels.Business> getBusinessInternal();
@Transaction @Query("SELECT * FROM business_table")
public ArrayList<DatabaseModels.Business> getBusiness(){
return new ArrayList<>(getBusinessInternal());
}
以上所有答案都是针对字符串列表的。但下面可以帮助您编写对象列表的转换器。
就在“YourClassName”的位置,添加您的对象class。
@TypeConverter
public String fromValuesToList(ArrayList<**YourClassName**> value) {
if (value== null) {
return (null);
}
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<**YourClassName**>>() {}.getType();
return gson.toJson(value, type);
}
@TypeConverter
public ArrayList<**YourClassName**> toOptionValuesList(String value) {
if (value== null) {
return (null);
}
Gson gson = new Gson();
Type type = new TypeToken<List<**YourClassName**>>() {
}.getType();
return gson.fromJson(value, type);
}
以上答案全部正确。是的,如果你真的需要将一些东西的数组存储到一个 SQLite 字段中,TypeConverter 是一种解决方案。
并且我在我的项目中使用了接受的答案。
但是不要这样做!!!
如果您需要在实体中存储数组,在 90% 的情况下,您需要创建一对多或多对多关系。
否则,您的下一个 SQL 查询 select 在此数组中包含键的内容绝对是地狱...
示例:
对象 foo 作为 json: [{id: 1, name: "abs"}, {id:2, name: "cde"}
对象栏:[{id, 1, foos: [1, 2], {...}]
所以不要让实体像:
@Entity....
data class bar(
...
val foos: ArrayList<Int>)
下一个点赞:
@Entity(tablename="bar_foo", primaryKeys=["fooId", "barId"])
data class barFoo(val barId: Int, val fooId: Int)
让你的 foos 疼痛:[] 记录在这个 table.
我个人反对 @TypeConverters
/序列化,因为它们破坏了数据库的正常表单合规性。
对于这种特殊情况,它可能值得 defining a relationship using the @Relation 注释,它允许将嵌套实体查询到单个对象中,而不会增加声明 @ForeignKey
和编写所有 SQL 手动查询:
@Entity
public class MainActivityData {
@PrimaryKey
private String userId;
private String itemOneId;
private String itemTwoId;
}
@Entity
public class MyListItem {
@PrimaryKey
public int id;
public String ownerUserId;
public String text;
}
/* This is the class we use to define our relationship,
which will also be used to return our query results.
Note that it is not defined as an @Entity */
public class DataWithItems {
@Embedded public MainActivityData data;
@Relation(
parentColumn = "userId"
entityColumn = "ownerUserId"
)
public List<MyListItem> myListItems;
}
/* This is the DAO interface where we define the queries.
Even though it looks like a single SELECT, Room performs
two, therefore the @Transaction annotation is required */
@Dao
public interface ListItemsDao {
@Transaction
@Query("SELECT * FROM MainActivityData")
public List<DataWithItems> getAllData();
}
除了这个 1-N 示例,还可以定义 1-1 和 N-M 关系。
原生 Kotlin 版本 使用 Kotlin 的序列化组件 – kotlinx.serialization.
- 将 Kotlin 序列化 Gradle 插件和依赖项添加到您的
build.gradle
:
apply plugin: 'kotlinx-serialization'
dependencies {
...
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
}
- 将类型转换器添加到您的转换器中class;
class Converters {
@TypeConverter
fun fromList(value : List<String>) = Json.encodeToString(value)
@TypeConverter
fun toList(value: String) = Json.decodeFromString<List<String>>(value)
}
- 将您的转换器 class 添加到您的数据库 class:
@TypeConverters(Converters::class)
abstract class YourDatabase: RoomDatabase() {...}
大功告成!
额外资源:
Kotlin 答案
你需要做三件事:
- 创建转换器class。
- 在数据库上添加转换器 class。
- 只需定义您要在实体中使用的内容 class。
逐步使用示例:
第 1 步:
class Converters {
@TypeConverter
fun listToJsonString(value: List<YourModel>?): String = Gson().toJson(value)
@TypeConverter
fun jsonStringToList(value: String) = Gson().fromJson(value, Array<YourModel>::class.java).toList()
}
第 2 步:
@Database(entities = [YourEntity::class], version = 1)
@TypeConverters(Converters::class)
abstract class YourDatabase : RoomDatabase() {
abstract fun yourDao(): YourDao
}
第 3 步:
Note: You do not need to call functions of Converter which are listToJsonString() and jsonStringToList(). They are using in background by Room.
@Entity(tableName = "example_database_table")
data class YourEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
@ColumnInfo(name = "your_model_list") var yourModelList: List<YourModel>,
)
我刚刚实现了用于离线数据保存的 Room。但是在实体 class 中,我收到以下错误:
Error:(27, 30) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
而class如下:
@Entity(tableName = "firstPageData")
public class MainActivityData {
@PrimaryKey
private String userId;
@ColumnInfo(name = "item1_id")
private String itemOneId;
@ColumnInfo(name = "item2_id")
private String itemTwoId;
// THIS IS CAUSING THE ERROR... BASICALLY IT ISN'T READING ARRAYS
@ColumnInfo(name = "mylist_array")
private ArrayList<MyListItems> myListItems;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public ArrayList<MyListItems> getMyListItems() {
return myListItems;
}
public void setCheckListItems(ArrayList<MyListItems> myListItems) {
this.myListItems = myListItems;
}
}
所以基本上我想将 ArrayList 保存在数据库中,但我无法找到与之相关的任何内容。你能指导我如何使用 Room 保存数组吗?
注意:MyListItems Pojo class 包含 2 个字符串(截至目前)
提前致谢。
选项 #1:让 MyListItems
成为 @Entity
,就像 MainActivityData
一样。 MyListItems
会设置 @ForeignKey
回到 MainActivityData
。但是,在这种情况下,MainActivityData
不能有 private ArrayList<MyListItems> myListItems
,因为在 Room 中,实体不引用其他实体。不过,视图模型或类似的 POJO 构造可能具有 MainActivityData
及其关联的 ArrayList<MyListItems>
。
选项 #2:设置一对 @TypeConverter
方法来将 ArrayList<MyListItems>
转换为某种基本类型(例如 String
,例如使用 JSON作为存储格式)。现在,MainActivityData
可以直接拥有它的 ArrayList<MyListItems>
。但是,MyListItems
不会有单独的 table,因此您无法很好地查询 MyListItems
。
Type Converter 是专门为此制作的。在您的情况下,您可以使用下面给出的代码片段将数据存储在数据库中。
public class Converters {
@TypeConverter
public static ArrayList<String> fromString(String value) {
Type listType = new TypeToken<ArrayList<String>>() {}.getType();
return new Gson().fromJson(value, listType);
}
@TypeConverter
public static String fromArrayList(ArrayList<String> list) {
Gson gson = new Gson();
String json = gson.toJson(list);
return json;
}
}
并像这样在您的 Room DB 中提及 class
@Database (entities = {MainActivityData.class},version = 1)
@TypeConverters({Converters.class})
更多信息here
收到与上述相同的错误消息。 我想补充一点:如果您在@Query 中收到此错误消息,您应该在@Query 注释上方添加@TypeConverters。
示例:
@TypeConverters(DateConverter.class)
@Query("update myTable set myDate=:myDate where id = :myId")
void updateStats(int myId, Date myDate);
.....
public class DateConverter {
@TypeConverter
public static Date toDate(Long timestamp) {
return timestamp == null ? null : new Date(timestamp);
}
@TypeConverter
public static Long toTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}
Kotlin 类型转换器版本:
class Converters {
@TypeConverter
fun listToJson(value: List<JobWorkHistory>?) = Gson().toJson(value)
@TypeConverter
fun jsonToList(value: String) = Gson().fromJson(value, Array<JobWorkHistory>::class.java).toList()
}
我使用了 JobWorkHistory
对象,请使用您自己的对象
@Database(entities = arrayOf(JobDetailFile::class, JobResponse::class), version = 1)
@TypeConverters(Converters::class)
abstract class MyRoomDataBase : RoomDatabase() {
abstract fun attachmentsDao(): AttachmentsDao
}
这就是我处理列表转换的方式
public class GenreConverter {
@TypeConverter
public List<Integer> gettingListFromString(String genreIds) {
List<Integer> list = new ArrayList<>();
String[] array = genreIds.split(",");
for (String s : array) {
if (!s.isEmpty()) {
list.add(Integer.parseInt(s));
}
}
return list;
}
@TypeConverter
public String writingStringFromList(List<Integer> list) {
String genreIds = "";
for (int i : list) {
genreIds += "," + i;
}
return genreIds;
}}
然后在数据库上我做如下所示
@Database(entities = {MovieEntry.class}, version = 1)
@TypeConverters(GenreConverter.class)
下面是相同的 kotlin 实现;
class GenreConverter {
@TypeConverter
fun gettingListFromString(genreIds: String): List<Int> {
val list = mutableListOf<Int>()
val array = genreIds.split(",".toRegex()).dropLastWhile {
it.isEmpty()
}.toTypedArray()
for (s in array) {
if (s.isNotEmpty()) {
list.add(s.toInt())
}
}
return list
}
@TypeConverter
fun writingStringFromList(list: List<Int>): String {
var genreIds=""
for (i in list) genreIds += ",$i"
return genreIds
}}
使用房间的官方解决方案,@Embedded 注释:
@Embedded(prefix = "mylist_array") private ArrayList<MyListItems> myListItems
添加 @TypeConverters
和转换器 class 作为参数
数据库和 Dao class,使我的查询有效
此答案使用Kotin 以逗号分隔并构造以逗号分隔的字符串。逗号需要放在除最后一个元素之外的所有元素的末尾,因此这也将处理单个元素列表。
object StringListConverter {
@TypeConverter
@JvmStatic
fun toList(strings: String): List<String> {
val list = mutableListOf<String>()
val array = strings.split(",")
for (s in array) {
list.add(s)
}
return list
}
@TypeConverter
@JvmStatic
fun toString(strings: List<String>): String {
var result = ""
strings.forEachIndexed { index, element ->
result += element
if(index != (strings.size-1)){
result += ","
}
}
return result
}
}
Json 转换在内存方面的扩展性不佳 allocation.I 宁愿选择与上面的响应类似但具有一些可空性的东西。
class Converters {
@TypeConverter
fun stringAsStringList(strings: String?): List<String> {
val list = mutableListOf<String>()
strings
?.split(",")
?.forEach {
list.add(it)
}
return list
}
@TypeConverter
fun stringListAsString(strings: List<String>?): String {
var result = ""
strings?.forEach { element ->
result += "$element,"
}
return result.removeSuffix(",")
}
}
对于简单的数据类型可以使用上面的,否则对于复杂的数据类型 Room 提供 Embedded
List<String>
转换器的更好版本
class StringListConverter {
@TypeConverter
fun fromString(stringListString: String): List<String> {
return stringListString.split(",").map { it }
}
@TypeConverter
fun toString(stringList: List<String>): String {
return stringList.joinToString(separator = ",")
}
}
这是将 customObject 类型添加到 Room DB 的示例 table。 https://mobikul.com/insert-custom-list-and-get-that-list-in-room-database-using-typeconverter/
添加类型转换器很容易,我只需要一个可以将对象列表转换为字符串的方法,以及一个可以进行相反操作的方法。我为此使用了 gson。
public class Converters {
@TypeConverter
public static String MyListItemListToString(List<MyListitem> list) {
Gson gson = new Gson();
return gson.toJson(list);
}
@TypeConverter
public static List<Integer> stringToMyListItemList(@Nullable String data) {
if (data == null) {
return Collections.emptyList();
}
Type listType = new TypeToken<List<MyListItem>>() {}.getType();
Gson gson = new Gson();
return gson.fromJson(data, listType);
}
}
然后我在实体中的字段中添加了注释:
@TypeConverters(Converters.class)
public final ArrayList<MyListItem> myListItems;
在我的案例中,问题是泛型 基于这个答案
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class IntArrayListConverter {
@TypeConverter
fun fromString(value: String): List<Int> {
val type = object: TypeToken<List<Int>>() {}.type
return Gson().fromJson(value, type)
}
@TypeConverter
fun fromArrayList(list: List<Int>): String {
val type = object: TypeToken<List<Int>>() {}.type
return Gson().toJson(list, type)
}
}
它不需要添加 @TypeConverters(IntArrayListConverter::class) 在 dao 中查询 class 也不需要在 Entity class 中查询 只需将 @TypeConverters(IntArrayListConverter::class) 添加到数据库 class
@Database(entities = [MyEntity::class], version = 1, exportSchema = false)
@TypeConverters(IntArrayListConverter::class)
abstract class MyDatabase : RoomDatabase() {
当我们使用 TypaConverters 时,数据类型应该是 returnTypeConverter 方法的类型 .示例 TypeConverter 方法 Return String 然后添加 Table COloum 应该是 String
private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
// Since we didn't alter the table, there's nothing else to do here.
database.execSQL("ALTER TABLE "+ Collection.TABLE_STATUS + " ADD COLUMN deviceType TEXT;");
database.execSQL("ALTER TABLE "+ Collection.TABLE_STATUS + " ADD COLUMN inboxType TEXT;");
}
};
@Query("SELECT * FROM business_table")
abstract List<DatabaseModels.Business> getBusinessInternal();
@Transaction @Query("SELECT * FROM business_table")
public ArrayList<DatabaseModels.Business> getBusiness(){
return new ArrayList<>(getBusinessInternal());
}
以上所有答案都是针对字符串列表的。但下面可以帮助您编写对象列表的转换器。
就在“YourClassName”的位置,添加您的对象class。
@TypeConverter
public String fromValuesToList(ArrayList<**YourClassName**> value) {
if (value== null) {
return (null);
}
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<**YourClassName**>>() {}.getType();
return gson.toJson(value, type);
}
@TypeConverter
public ArrayList<**YourClassName**> toOptionValuesList(String value) {
if (value== null) {
return (null);
}
Gson gson = new Gson();
Type type = new TypeToken<List<**YourClassName**>>() {
}.getType();
return gson.fromJson(value, type);
}
以上答案全部正确。是的,如果你真的需要将一些东西的数组存储到一个 SQLite 字段中,TypeConverter 是一种解决方案。
并且我在我的项目中使用了接受的答案。
但是不要这样做!!!
如果您需要在实体中存储数组,在 90% 的情况下,您需要创建一对多或多对多关系。
否则,您的下一个 SQL 查询 select 在此数组中包含键的内容绝对是地狱...
示例:
对象 foo 作为 json: [{id: 1, name: "abs"}, {id:2, name: "cde"}
对象栏:[{id, 1, foos: [1, 2], {...}]
所以不要让实体像:
@Entity....
data class bar(
...
val foos: ArrayList<Int>)
下一个点赞:
@Entity(tablename="bar_foo", primaryKeys=["fooId", "barId"])
data class barFoo(val barId: Int, val fooId: Int)
让你的 foos 疼痛:[] 记录在这个 table.
我个人反对 @TypeConverters
/序列化,因为它们破坏了数据库的正常表单合规性。
对于这种特殊情况,它可能值得 defining a relationship using the @Relation 注释,它允许将嵌套实体查询到单个对象中,而不会增加声明 @ForeignKey
和编写所有 SQL 手动查询:
@Entity
public class MainActivityData {
@PrimaryKey
private String userId;
private String itemOneId;
private String itemTwoId;
}
@Entity
public class MyListItem {
@PrimaryKey
public int id;
public String ownerUserId;
public String text;
}
/* This is the class we use to define our relationship,
which will also be used to return our query results.
Note that it is not defined as an @Entity */
public class DataWithItems {
@Embedded public MainActivityData data;
@Relation(
parentColumn = "userId"
entityColumn = "ownerUserId"
)
public List<MyListItem> myListItems;
}
/* This is the DAO interface where we define the queries.
Even though it looks like a single SELECT, Room performs
two, therefore the @Transaction annotation is required */
@Dao
public interface ListItemsDao {
@Transaction
@Query("SELECT * FROM MainActivityData")
public List<DataWithItems> getAllData();
}
除了这个 1-N 示例,还可以定义 1-1 和 N-M 关系。
原生 Kotlin 版本 使用 Kotlin 的序列化组件 – kotlinx.serialization.
- 将 Kotlin 序列化 Gradle 插件和依赖项添加到您的
build.gradle
:
apply plugin: 'kotlinx-serialization'
dependencies {
...
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
}
- 将类型转换器添加到您的转换器中class;
class Converters {
@TypeConverter
fun fromList(value : List<String>) = Json.encodeToString(value)
@TypeConverter
fun toList(value: String) = Json.decodeFromString<List<String>>(value)
}
- 将您的转换器 class 添加到您的数据库 class:
@TypeConverters(Converters::class)
abstract class YourDatabase: RoomDatabase() {...}
大功告成!
额外资源:
Kotlin 答案
你需要做三件事:
- 创建转换器class。
- 在数据库上添加转换器 class。
- 只需定义您要在实体中使用的内容 class。
逐步使用示例:
第 1 步:
class Converters {
@TypeConverter
fun listToJsonString(value: List<YourModel>?): String = Gson().toJson(value)
@TypeConverter
fun jsonStringToList(value: String) = Gson().fromJson(value, Array<YourModel>::class.java).toList()
}
第 2 步:
@Database(entities = [YourEntity::class], version = 1)
@TypeConverters(Converters::class)
abstract class YourDatabase : RoomDatabase() {
abstract fun yourDao(): YourDao
}
第 3 步:
Note: You do not need to call functions of Converter which are listToJsonString() and jsonStringToList(). They are using in background by Room.
@Entity(tableName = "example_database_table")
data class YourEntity(
@PrimaryKey(autoGenerate = true) val id: Long = 0,
@ColumnInfo(name = "your_model_list") var yourModelList: List<YourModel>,
)