问题
I just implemented Room for offline data saving. But in an Entity class, I am getting the following error:
Error:(27, 30) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
And the class is as following:
@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;
}
}
So basically I want to save the ArrayList in the database but I was not able to find anything relevant to it. Can you guide me regarding how to save an Array using Room?
NOTE: MyListItems Pojo class contains 2 Strings (as of now)
Thanks in advance.
回答1:
Option #1: Have MyListItems
be an @Entity
, as MainActivityData
is. MyListItems
would set up a @ForeignKey
back to MainActivityData
. In this case, though, MainActivityData
cannot have private ArrayList<MyListItems> myListItems
, as in Room, entities do not refer to other entities. A view model or similar POJO construct could have a MainActivityData
and its associated ArrayList<MyListItems>
, though.
Option #2: Set up a pair of @TypeConverter
methods to convert ArrayList<MyListItems>
to and from some basic type (e.g., a String
, such as by using JSON as a storage format). Now, MainActivityData
can have its ArrayList<MyListItems>
directly. However, there will be no separate table for MyListItems
, and so you cannot query on MyListItems
very well.
回答2:
Type Converter are made specifically for that. In your case, you can use code snippet given below to store data in DB.
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;
}
}
And mention this class in your Room DB like this
@Database (entities = {MainActivityData.class},version = 1)
@TypeConverters({Converters.class})
More info here
回答3:
Kotlin version for type converter:
class Converters {
@TypeConverter
fun listToJson(value: List<JobWorkHistory>?): String {
return Gson().toJson(value)
}
@TypeConverter
fun jsonToList(value: String): List<JobWorkHistory>? {
val objects = Gson().fromJson(value, Array<JobWorkHistory>::class.java) as Array<JobWorkHistory>
val list = objects.toList()
return list
}
}
I Used JobWorkHistory
object for my purpose, use the object of your own
@Database(entities = arrayOf(JobDetailFile::class, JobResponse::class), version = 1)
@TypeConverters(Converters::class)
abstract class MyRoomDataBase : RoomDatabase() {
abstract fun attachmentsDao(): AttachmentsDao
}
回答4:
This is how i handle List conversion
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;
}}
And then on the database i do as shown below
@Database(entities = {MovieEntry.class}, version = 1)
@TypeConverters(GenreConverter.class)
And below is a kotlin implementation of the same;
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
}}
回答5:
Had the same error message as described above. I would like to add: if you get this error message in a @Query, you should add @TypeConverters above the @Query annotation.
Example:
@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();
}
}
回答6:
Better version of List<String>
converter
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 = ",")
}
}
回答7:
This answer uses Kotin to split by comma and construct the comma delineated string. The comma needs to go at the end of all but the last element, so this will handle single element lists as well.
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
}
}
回答8:
in my case problem was generic type base on this answer
https://stackoverflow.com/a/48480257/3675925 use List instead of 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)
}
}
it doesn't need add @TypeConverters(IntArrayListConverter::class) to query in dao class nor fields in Entity class and just add @TypeConverters(IntArrayListConverter::class) to database class
@Database(entities = [MyEntity::class], version = 1, exportSchema = false)
@TypeConverters(IntArrayListConverter::class)
abstract class MyDatabase : RoomDatabase() {
回答9:
Adding @TypeConverters
with the converter class as params
to Database & to the Dao class, made my queries work
回答10:
Json conversions don't scale well in terms of memory allocation.I'd rather go for something similar to responses above with some nullability.
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(",")
}
}
For simple data types the above can be used, otherwise for complex datatypes Room provides Embedded
回答11:
Here is the example for adding the customObject types to Room DB table. https://mobikul.com/insert-custom-list-and-get-that-list-in-room-database-using-typeconverter/
Adding a type converter was easy, I just needed a method that could turn the list of objects into a string, and a method that could do the reverse. I used gson for this.
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);
}
}
I then added an annotation to the field in the Entity:
@TypeConverters(Converters.class)
public final ArrayList<MyListItem> myListItems;
回答12:
Use official solution from room, @Embedded annotation :
@Embedded(prefix = "mylist_array") private ArrayList<MyListItems> myListItems
来源:https://stackoverflow.com/questions/44986626/android-room-database-how-to-handle-arraylist-in-an-entity