Android Roomデータベース:エンティティでArraylistを処理する方法は?


84

オフラインデータ保存用のRoomを実装しました。しかし、Entityクラスでは、次のエラーが発生します。

Error:(27, 30) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.

そして、クラスは次のとおりです。

@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クラスには2つの文字列が含まれています(現在)

前もって感謝します。

回答:


78

オプション#1:持ってはMyListItemsなる@Entityよう、MainActivityDataです。MyListItems@ForeignKey戻る設定しMainActivityDataます。この場合は、しかし、MainActivityData持っていないことができprivate ArrayList<MyListItems> myListItems、ルームのように、エンティティは他のエンティティを指すものではありません。ただし、ビューモデルまたは同様のPOJO構造には、MainActivityDataとが関連付けられている可能性がArrayList<MyListItems>あります。

オプション#2:いくつかの基本的なタイプとの間で@TypeConverter変換するArrayList<MyListItems>ためのメソッドのペアを設定します(たとえば、Stringストレージ形式としてJSONを使用するなど)。今、MainActivityDataそれをArrayList<MyListItems>直接持つことができます。ただし、の個別のテーブルはないためMyListItems、クエリをMyListItemsうまく実行することはできません。


了解しました。迅速な回答に感謝します。最初に2番目のオプションを試し(1番目のオプションがtbh ..:Eであることが完全に明確ではありません)、返信します。
Tushar Gogna 2017

1
ArrayList-> String(Jsonを使用)およびその逆はうまく機能しました。ところで、1番目のオプションについても詳しく説明できますか?代替案を知りたいだけです。とにかくありがとう。:)
Tushar Gogna 2017

@TusharGogna:関係は部屋のドキュメントでカバーされおり、「エンティティは他のエンティティを直接参照しない」ビットも部屋のドキュメントでカバーされています
CommonsWare 2017

1
ちょうどメモとして。たとえば、Intのリストを永続化する場合は、オプション2の文字列としてシリアル化する必要があります。これにより、クエリがより複雑になります。「タイプ」への依存度が低いため、オプション1を選択します。
axierjhtjz 2018年

5
将来的には、アイテムのクエリが必要になる可能性があるため、通常はオプション#1を使用します
Jeffrey

107

タイプコンバーターはそのために特別に作られています。あなたの場合、以下に示すコードスニペットを使用してデータを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;
    }
}

そして、このクラスをルームDBで次のように言及します

@Database (entities = {MainActivityData.class},version = 1)
@TypeConverters({Converters.class})

詳細はこちら


2
Kotlin withListで同じことをするのを誰かが手伝ってくれませんか。Javaでは正常に動作していました。しかし、私がコリンでそれを変換したとき、それは機能していません
Ozeetee 2018年

2
その配列リストからどのようにクエリしますか?
Sanjog Shrestha 2018年

@SanjogShresthaどういう意味かわかりません。getメソッドを使用して配列リストとクエリを取得するだけです
Amit Bhandari

@AmitBhandari上記のシナリオを例として取り上げましょう。myListItemsに含まれているテーブル(MainActivityData)(a、b、cなど)を検索したいのですが、userIdはabcです。では、このような場合のクエリをどのように記述しますか?
Sanjog Shrestha 2018年

1
@bompf提案をありがとう。この例は単なる説明ですが。通常、アプリケーションレベルで常に1つのgsonインスタンスを保持します。
アミットバンダーリ

51

タイプコンバーターの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
}

2
配列に逆シリアル化してからListに変換するよりも、次のようなListタイプを使用する方がよいと思います。vallistType= object:TypeToken <List <JobWorkHistory >>(){}。typeは、以下の回答で説明されているAmitのようです。
Sohayb Hassoun 2018

3
また、Gsonアプリのどこかからキャッシュされたインスタンスをフェッチすることもできます。Gson呼び出しごとに新しいインスタンスを初期化すると、コストがかかる可能性があります。
Apsaliya

14

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 = ",")
    }
}

6
文字列が同じ文字を持っている場合があり、混乱する可能性があるため、区切り文字として「、」を使用することに注意してください。
emarshah

9

これは私がリスト変換を処理する方法です

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
}}

このソリューションは、gsonベースのソリューションよりも軽いため、単純な型(List <Integer>、List <Long>など)に使用します。
Julien Kronegg 2018

2
この解決策は、不幸な流れを見逃します(たとえば、nullと空の文字列、nullリスト)。
Julien Kronegg 2018

ええ、私はこれをコピーして貼り付けるのを間違え、単一のコンマで要素を作成する単一の要素リストに少なくとも1時間を失いました。私は(Kotlin中)の修正を提出して答えている
ダニエル・ウィルソン

6

上記と同じエラーメッセージが表示されました。追加したいのですが、@ 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();
    }
}

1
クエリアノテーションの上に@TypeConvertersを追加しようとしましたが、それでも同じエラーが発生します
zulkarnain shah 2018

2

この回答では、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
        }
    }

2

私の場合、問題はこの答えに基づくジェネリック型でした

https://stackoverflow.com/a/48480257/3675925ArrayListの 代わりにListを使用します

 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)
     } 
}

daoクラスのクエリやエンティティクラスのフィールドに@TypeConverters(IntArrayListConverter :: class)を追加する必要はなく、データベースクラスに@TypeConverters(IntArrayListConverter :: class)を追加するだけです。

@Database(entities = [MyEntity::class], version = 1, exportSchema = false)
@TypeConverters(IntArrayListConverter::class)
abstract class MyDatabase : RoomDatabase() {

2

@TypeConverters/ serializationsはデータベースの通常のフォームへの準拠に違反するため、個人的には/ serializationsに反対することをお勧めします。

この特定のケースでは、@ Relationアノテーションを使用して関係定義する価値があるかもしれません。これにより、を宣言してすべてのSQLクエリを手動で記述するという複雑さを増すことなく、ネストされたエンティティを単一のオブジェクトにクエリできます。@ForeignKey

@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とNMの関係を定義することもできます。


0

@TypeConvertersコンバータークラスをパラメーターとして追加

データベースとDaoクラスに、クエリを機能させました


1
あなたはあなたの答えを詳しく説明できますか?
K Pradeep KumarReddy19年

0

Json変換は、メモリ割り当ての観点からは適切に拡張できません。上記の応答と同様の、ある程度のヌル可能性を備えたものを選択したいと思います。

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を提供します


0

これは、customObjectタイプをRoomDBテーブルに追加するための例です。 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;

0

TypaConvertersを使用する場合、DatatypeはTypeConverterメソッドの戻り値の型である必要があります。TypeConverterメソッドの例文字列を返し、テーブルの追加列は文字列である必要があります

 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;");
    }
};

0
 @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());
 }

0

上記のすべての回答は、文字列のリストに関するものです。ただし、以下は、オブジェクトのリストのコンバーターを見つけるのに役立ちます。

YourClassName」の代わりに、Objectクラスを追加します。

 @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);
        }

0

上記のすべての答えは正しいです。はい、本当に何かの配列を1つのSQLiteフィールドに格納する必要がある場合は、TypeConverterが解決策です。

そして、私は自分のプロジェクトで受け入れられた答えを使用しました。

しかし、それをしないでください!!!

90%の場合、エンティティに配列を格納する必要がある場合は、1対多または多対多の関係を作成する必要があります。

そうでなければ、この配列内のキーを持つものを選択するための次のSQLクエリは絶対に地獄になります...

例:

オブジェクト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:[]を痛めます。


yopuが最初のAPI呼び出しで使用可能で次のAPI呼び出しでは使用できなかったIDのリストを保存していた場合は、必ずそれらのIDをどこかに保存し、それらを使用してapiにクエリを実行してジャンクションテーブルのあるテーブルに保存することを想定しないでください。 、これは両方のソリューションを使用します。これは簡単な方法と見
なされ

0

Kotlinのシリアル化コンポーネント–kotlinx.serializationを使用したネイティブKotlinバージョン

  1. Kotlinシリアル化Gradleプラグインと依存関係をbuild.gradle:に追加します。
apply plugin: 'kotlinx-serialization'

dependencies {
   ...
   implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
}
  1. TypeコンバーターをConverterクラスに追加します。
@TypeConverter
fun fromList(value : List<String>) = Json.encodeToString(value)

@TypeConverter
fun toList(value: String) = Json.decodeFromString<List<String>>(value)
  1. Converterクラスをデータベースクラスに追加します。
@TypeConverters(Converters::class)
abstract class YourDatabase: RoomDatabase() {...}

これで完了です。

追加のリソース:


-2

部屋からの公式ソリューション、@ Embeddedアノテーションを使用してください:

@Embedded(prefix = "mylist_array") private ArrayList<MyListItems> myListItems
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.