Ich habe gerade Raum zum Speichern von Offline-Daten implementiert. In einer Entity-Klasse wird jedoch der folgende Fehler angezeigt:
Error:(27, 30) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
Und die Klasse ist wie folgt:
@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;
}
}
Grundsätzlich möchte ich die ArrayList in der Datenbank speichern, konnte aber nichts Relevantes dafür finden. Können Sie mir zeigen, wie Sie ein Array mit Room speichern können?
HINWEIS: Die Pojo-Klasse MyListItems enthält 2 Strings (ab sofort).
Danke im Voraus.
quelle
Typkonverter sind speziell dafür gemacht. In Ihrem Fall können Sie das unten angegebene Codefragment verwenden, um Daten in der Datenbank zu speichern.
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; } }
Und erwähnen Sie diese Klasse in Ihrer Room DB so
@Database (entities = {MainActivityData.class},version = 1) @TypeConverters({Converters.class})
Mehr Infos hier
quelle
Kotlin- Version für Typkonverter:
class Converters { @TypeConverter fun listToJson(value: List<JobWorkHistory>?) = Gson().toJson(value) @TypeConverter fun jsonToList(value: String) = Gson().fromJson(value, Array<JobWorkHistory>::class.java).toList() }
Ich habe ein
JobWorkHistory
Objekt für meinen Zweck verwendet, benutze das eigene Objekt@Database(entities = arrayOf(JobDetailFile::class, JobResponse::class), version = 1) @TypeConverters(Converters::class) abstract class MyRoomDataBase : RoomDatabase() { abstract fun attachmentsDao(): AttachmentsDao }
quelle
Gson
Instanz von einer beliebigen Stelle in Ihrer App abrufen. Das Initialisieren einer neuenGson
Instanz bei jedem Anruf kann teuer sein.Bessere Version des
List<String>
Konvertersclass StringListConverter { @TypeConverter fun fromString(stringListString: String): List<String> { return stringListString.split(",").map { it } } @TypeConverter fun toString(stringList: List<String>): String { return stringList.joinToString(separator = ",") } }
quelle
So gehe ich mit der Listenkonvertierung um
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; }}
Und dann mache ich in der Datenbank wie unten gezeigt
@Database(entities = {MovieEntry.class}, version = 1) @TypeConverters(GenreConverter.class)
Und unten ist eine Kotlin-Implementierung derselben;
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 }}
quelle
Hatte die gleiche Fehlermeldung wie oben beschrieben. Ich möchte hinzufügen: Wenn Sie diese Fehlermeldung in einer @Query erhalten, sollten Sie @TypeConverters über der @Query-Annotation hinzufügen.
Beispiel:
@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(); } }
quelle
Diese Antwort verwendet Kotin, um durch Komma zu teilen und die durch Kommas abgegrenzte Zeichenfolge zu konstruieren. Das Komma muss am Ende aller Elemente außer dem letzten stehen, damit auch einzelne Elementlisten behandelt werden.
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 } }
quelle
In meinem Fall war das Problem eine generische Typbasis auf dieser Antwort
https://stackoverflow.com/a/48480257/3675925 Verwenden Sie List anstelle von 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) } }
Es muss weder @TypeConverters (IntArrayListConverter :: class) hinzugefügt werden, um in der Dao-Klasse noch Felder in der Entity-Klasse abzufragen, sondern nur @TypeConverters (IntArrayListConverter :: class) zur Datenbankklasse hinzufügen
@Database(entities = [MyEntity::class], version = 1, exportSchema = false) @TypeConverters(IntArrayListConverter::class) abstract class MyDatabase : RoomDatabase() {
quelle
Ich würde persönlich davon abraten
@TypeConverters
/ Serialisierungen , da sie die normale Formularkonformität der Datenbank verletzen.In diesem speziellen Fall kann es sinnvoll sein , eine Beziehung mithilfe der Annotation @Relation zu definieren, mit der verschachtelte Entitäten in ein einzelnes Objekt abgefragt werden können, ohne die zusätzliche Komplexität, a zu deklarieren
@ForeignKey
und alle SQL-Abfragen manuell zu schreiben:@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(); }
Abgesehen von diesem 1-N-Beispiel können auch 1-1- und NM-Beziehungen definiert werden.
quelle
Hinzufügen
@TypeConverters
mit der Konverterklasse als ParameterZur Datenbank und zur Dao-Klasse haben meine Abfragen funktioniert
quelle
Json-Konvertierungen lassen sich in Bezug auf die Speicherzuweisung nicht gut skalieren. Ich würde lieber etwas Ähnliches wie die obigen Antworten mit einer gewissen Nullbarkeit wählen.
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(",") } }
Für einfache Datentypen kann das Obige verwendet werden, andernfalls für komplexe Datentypen, die Room Embedded bereitstellt
quelle
Hier ist das Beispiel zum Hinzufügen der customObject-Typen zur Room DB-Tabelle. https://mobikul.com/insert-custom-list-and-get-that-list-in-room-database-using-typeconverter/
Das Hinzufügen eines Typkonverters war einfach. Ich brauchte nur eine Methode, mit der die Liste der Objekte in eine Zeichenfolge umgewandelt werden kann, und eine Methode, mit der das Gegenteil möglich ist. Ich habe dafür gson verwendet.
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); } }
Ich habe dann dem Feld in der Entität eine Anmerkung hinzugefügt:
@TypeConverters(Converters.class) public final ArrayList<MyListItem> myListItems;
quelle
Wenn wir TypaConverters verwenden, sollte Datentyp der Typ der TypeConverter-Methode sein. Beispiel TypeConverter-Methode Return String, dann sollte das Hinzufügen von Table COloum String sein
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;"); } };
quelle
@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()); }
quelle
Alle obigen Antworten beziehen sich auf eine Liste von Zeichenfolgen. Im Folgenden finden Sie jedoch Informationen zum Konverter für die Liste Ihrer Objekte.
Fügen Sie anstelle von " YourClassName " Ihre Object-Klasse hinzu.
@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); }
quelle
Alle Antworten oben richtig. Ja, wenn Sie WIRKLICH ein Array von etwas in einem SQLite-Feld speichern müssen, ist TypeConverter eine Lösung.
Und ich habe die akzeptierte Antwort in meinen Projekten verwendet.
Aber tu es nicht !!!
Wenn Sie in 90% der Fälle ein Speicherarray in Entity benötigen, müssen Sie Eins-zu-Viele- oder Viele-zu-Viele-Beziehungen erstellen.
Andernfalls ist Ihre nächste SQL-Abfrage zur Auswahl eines Schlüssels in diesem Array die Hölle ...
Beispiel:
Objekt foo kommt als json: [{id: 1, name: "abs"}, {id: 2, name: "cde"}
Objektleiste: [{id, 1, foos: [1, 2], {...}]
Machen Sie also keine Entität wie:
@Entity.... data class bar( ... val foos: ArrayList<Int>)
Machen Sie wie folgt:
@Entity(tablename="bar_foo", primaryKeys=["fooId", "barId"]) data class barFoo(val barId: Int, val fooId: Int)
Und wund deine foos: [] als Aufzeichnungen in dieser Tabelle.
quelle
Native Kotlin-Version mit Kotlins Serialisierungskomponente - kotlinx.serialization .
build.gradle
:apply plugin: 'kotlinx-serialization' dependencies { ... implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1" }
@TypeConverter fun fromList(value : List<String>) = Json.encodeToString(value) @TypeConverter fun toList(value: String) = Json.decodeFromString<List<String>>(value)
@TypeConverters(Converters::class) abstract class YourDatabase: RoomDatabase() {...}
Und du bist fertig!
Zusätzliche Ressourcen:
quelle
Verwenden Sie die offizielle Lösung aus dem Raum, @Embedded Annotation:
@Embedded(prefix = "mylist_array") private ArrayList<MyListItems> myListItems
quelle