Android Room Database : 엔티티에서 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설정합니다 . 하지만이 경우에는 Room에서와 같이를 가질 수 없습니다 . 엔티티는 다른 엔티티를 참조하지 않습니다. 그러나 뷰 모델 또는 유사한 POJO 구성은 및 관련를 가질 수 있습니다 .@ForeignKeyMainActivityDataMainActivityDataprivate ArrayList<MyListItems> myListItemsMainActivityDataArrayList<MyListItems>

옵션 # 2 : 몇 가지 기본 유형 (예 : 스토리지 형식으로 JSON을 사용 하는 것과 같은) 과 @TypeConverter변환 ArrayList<MyListItems>할 메서드 쌍을 설정합니다 String. 이제 직접 MainActivityData가질 수 ArrayList<MyListItems>있습니다. 그러나에 대한 별도의 테이블이 없으므로 MyListItems잘 쿼리 할 수 ​​없습니다 MyListItems.


알겠습니다. 빠른 답변에 감사드립니다. 먼저 두 번째 옵션을 시도 할 것입니다 (첫 번째 옵션은 tbh .. : E라는 것이 완전히 명확하지 않습니다).
Tushar Gogna

1
ArrayList-> String (Json 사용)과 그 반대의 경우도 잘 작동했습니다. Btw, 첫 번째 옵션도 더 자세히 설명해 줄 수 있나요? 대안을 알고 싶습니다. 어쨌든 감사합니다. :)
Tushar Gogna

@TusharGogna : 관계는 Room 문서 에서 다루고 있으며 "entities는 다른 엔티티를 직접 참조하지 않습니다"비트도 Room 문서 에서 다룹니다 .
CommonsWare

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
누구든지 List로 Kotlin에서 똑같이 할 수 있도록 도와 줄 수 있습니까? Java에서는 잘 작동했습니다. 나는 콜린에 변환 때 작동하지 않는 그
Ozeetee

2
해당 arraylist에서 어떻게 쿼리합니까?
Sanjog Shrestha

@SanjogShrestha 무슨 뜻인지 이해가 안 돼요. get 메소드를 사용하여 arraylist와 쿼리를 검색합니다.
Amit Bhandari

@AmitBhandari 위의 시나리오를 예로 들어 보겠습니다. myListItems에 (예 : a, b, c)가 있고 userId가 abc 인 테이블 (MainActivityData)을 검색하고 싶습니다. 이제 그러한 경우에 대한 쿼리를 어떻게 작성합니까?
Sanjog Shrestha

1
@bompf 제안에 감사드립니다. 이 예는 단지 예시 일뿐입니다. 일반적으로 우리는 항상 애플리케이션 수준에서 하나의 gson 인스턴스를 유지합니다.
Amit Bhandari

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
배열로 deserialize 한 다음 List로 변환하는 것보다 다음과 같은 List 유형을 사용하는 것이 좋습니다. val listType = object : TypeToken <List <JobWorkHistory >> () {} .type은 아래 답변에서 언급 한 Amit과 같습니다.
Sohayb Hassoun

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

2
이 솔루션은 불행한 흐름을 놓친다 (예 : null 및 빈 문자열, null 목록).
Julien Kronegg

예, 이것을 복사하여 붙여 넣는 실수를 저질렀 고 단일 쉼표로 요소를 만드는 단일 요소 목록에 적어도 한 시간을 잃었습니다. (Kotlin에서) 이에 대한 수정 사항을 제출하고 답변했습니다
Daniel Wilson

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

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/3675925 ArrayList 대신 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 클래스 나 Entity 클래스의 필드를 쿼리하기 위해 @TypeConverters (IntArrayListConverter :: class)를 추가 할 필요가 없으며 데이터베이스 클래스에 @TypeConverters (IntArrayListConverter :: class) 만 추가하면됩니다.

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

2

@TypeConverters/ serializations는 데이터베이스의 일반 형식 준수를 위반하므로 개인적으로 권장 하지 않습니다.

이 특별한 경우 에는 @Relation 어노테이션을 사용하여 관계정의 할 가치가 있습니다 . 이렇게 하면 a를 선언하고 @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 및 NM 관계도 정의 할 수 있습니다.


0

@TypeConverters변환기 클래스를 매개 변수로 추가

데이터베이스 및 Dao 클래스로, 내 쿼리가 작동하도록했습니다.


1
당신은 당신의 대답을 자세히 설명 할 수 있습니까 ??
K Pradeep Kumar Reddy

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에서 제공하는 임베디드


0

다음은 Room DB 테이블에 customObject 유형을 추가하는 예입니다. 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 메서드 반환 문자열 후 테이블 추가 COloum은 문자열이어야합니다.

 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

위의 모든 답변이 맞습니다. 예, 하나의 SQLite 필드에 무언가의 저장소 배열이 정말로 필요한 경우 TypeConverter가 솔루션입니다.

그리고 나는 내 프로젝트에서 받아 들여진 대답을 사용했습니다.

하지만하지 마세요 !!!

90 % 사례에서 Entity에 저장소 배열이 필요한 경우 일대 다 또는 다 대다 관계를 만들어야합니다.

그렇지 않으면이 배열 안에 키가있는 무언가를 선택하기위한 다음 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 호출에서 사용할 수 있지만 다음에서는 사용할 수없는 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. 변환기 클래스에 유형 변환기를 추가하십시오.
@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.