एंड्रॉइड रूम डेटाबेस: एक एंटिटी में Arraylist कैसे संभालें?


90

मैंने अभी ऑफ़लाइन डेटा सेविंग के लिए कमरा लागू किया है। लेकिन एक इकाई वर्ग में, मुझे निम्नलिखित त्रुटि मिल रही है:

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 को डेटाबेस में सहेजना चाहता हूं, लेकिन मैं इसके लिए प्रासंगिक कुछ भी नहीं पा रहा था। क्या आप मुझे निर्देशित कर सकते हैं कि कमरे का उपयोग करके एक ऐरे को कैसे बचाया जाए?

नोट: MyListItems पूजो वर्ग में 2 स्ट्रिंग्स शामिल हैं (अब के रूप में)

अग्रिम में धन्यवाद।

जवाबों:


82

विकल्प # 1: MyListItemsएक है @Entity, जैसा MainActivityDataहै। वापस MyListItemsस्थापित करना होगा । इस मामले में, हालांकि, जैसा कि कक्ष में नहीं हो सकता है , संस्थाएं अन्य संस्थाओं को संदर्भित नहीं करती हैं। एक दृश्य मॉडल या इसी तरह का POJO निर्माण हालांकि और उससे जुड़ा हो सकता है ।@ForeignKeyMainActivityDataMainActivityDataprivate ArrayList<MyListItems> myListItemsMainActivityDataArrayList<MyListItems>

विकल्प # 2: कुछ बुनियादी प्रकार से (जैसे, ए , जैसे कि जेएसएन को भंडारण प्रारूप के रूप में उपयोग करके) में @TypeConverterबदलने के ArrayList<MyListItems>लिए तरीकों की एक जोड़ी स्थापित करें String। अब, सीधे MainActivityDataकर सकते हैं ArrayList<MyListItems>। हालाँकि, इसके लिए कोई अलग तालिका नहीं होगी MyListItems, और इसलिए आप MyListItemsबहुत अच्छी तरह से क्वेरी नहीं कर सकते ।


ठीक है, त्वरित उत्तर के लिए धन्यवाद। मैं पहले 2 विकल्प की कोशिश कर रहा हूँ (1 विकल्प tbh होना पूरी तरह से स्पष्ट नहीं है ..: ई) और आप पर वापस जाएँ।
तुषार गोगना

1
ArrayList -> स्ट्रिंग (Json का उपयोग करके) और इसके विपरीत ने अच्छी तरह से काम किया। Btw, आप भी 1 विकल्प अधिक विस्तृत कर सकते हैं? बस विकल्प जानना चाहते हैं। फिर भी धन्यवाद। :)
तुषार गोगना

@ तुषारगोगना: रिश्ते कमरे के प्रलेखन में शामिल हैं , और "संस्थाएं सीधे अन्य संस्थाओं को संदर्भित नहीं करती हैं" बिट को भी कमरे के दस्तावेज में कवर किया गया है
कॉमन्सवेयर जूल

1
बस एक नोट के रूप में। यदि आप उदाहरण के लिए Int की सूची को जारी रखने जा रहे हैं, तो आपको इसे विकल्प 2 के लिए स्ट्रिंग के रूप में क्रमबद्ध करने की आवश्यकता है। यह प्रश्नों को अधिक जटिल बनाता है। मैं विकल्प 1 के लिए जाना चाहूंगा क्योंकि यह कम "प्रकार" पर निर्भर है।
axierjhtjz

5
भविष्य में कभी-कभी आपको अपने आइटम को क्वेरी करने की आवश्यकता हो सकती है, इसलिए मैं आमतौर पर # 1
जेफरी

111

टाइप कन्वर्टर इसके लिए विशेष रूप से बनाए जाते हैं। आपके मामले में, आप 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;
    }
}

और इस कक्षा का उल्लेख अपने कक्ष डीबी में इस तरह से करें

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

अधिक जानकारी यहाँ


2
क्या कोई मुझे सूची के साथ कोटलिन में ऐसा करने में मदद कर सकता है। जावा में यह ठीक काम कर रहा था। लेकिन जब मैंने इसे कोलिन में परिवर्तित किया, तो यह काम नहीं कर रहा था
ओजेटी

2
आप उस सरणी सूची से कैसे क्वेरी करते हैं?
संजोग श्रेष्ठ

@SanjogShrestha मुझे समझ में नहीं आ रहा है कि आपका क्या मतलब है। आप बस विधि प्राप्त करने का उपयोग करके सरणी सूची और क्वेरी को पुनः प्राप्त करें
अमित भंडारी

@AmitBhandari ऊपर दिए गए परिदृश्य को एक उदाहरण के रूप में लेते हैं। मैं तालिका (MainActivityData) की खोज करना चाहता हूं, जहां myListItems सम्‍मिलित है (उदा। A, b, c) और userId abc है। अब हम ऐसे मामले के लिए क्वेरी कैसे लिखते हैं?
संजोग श्रेष्ठ

1
सुझाव के लिए @bompf धन्यवाद। हालांकि यहां यह उदाहरण सिर्फ दृष्टांत है। आम तौर पर हम हमेशा आवेदन स्तर पर एक जीएसई उदाहरण रखते हैं।
अमित भंडारी

55

टाइप कनवर्टर के लिए कोटलिन संस्करण:

 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
मुझे लगता है कि किसी सारणी में उतरने के बजाय और फिर सूची में कनवर्ट करने के बजाय, इस तरह की सूची प्रकार का उपयोग करना बेहतर है: Val listType = ऑब्जेक्ट: TypeToken <List <JobWorkHistory >> () {} .type जैसे अमित नीचे दिए गए उत्तर में उल्लिखित है।
सोहेब हसौन

3
इसके अलावा, आप Gsonअपने ऐप में कहीं से कैश्ड इंस्टेंस लाना चाह सकते हैं । Gsonहर कॉल पर नई शुरुआत करना महंगा हो सकता है।
अप्सालिया

18

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
का उपयोग कर के बर्तन "," के रूप में विभाजक के रूप में कभी कभी अपने स्ट्रिंग एक ही चरित्र हो सकता है और यह एक गड़बड़ हो सकता है।
इमरशाह

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)

और नीचे उसी का एक कोटलिन कार्यान्वयन है;

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

मैं इस समाधान को सरल प्रकारों के लिए उपयोग करता हूं (जैसे सूची <Integer>, List <Long>) क्योंकि यह gson-based समाधानों की तुलना में हल्का है।
जुलिएन क्रोनग सिप

2
यह समाधान दुखी प्रवाह (जैसे शून्य और रिक्त स्ट्रिंग, अशक्त सूची) को याद करता है।
जुलिएन क्रोनग सिप

हाँ, मैंने इसे कॉपी करने की गलती की है और कम से कम एक घंटे में एकल तत्व सूचियों को खो दिया है जो एकल कॉमा के साथ तत्वों का निर्माण करता है। मैंने इसके लिए (कोटलिन में)
डैनियल विल्सन

6

जैसा कि ऊपर वर्णित त्रुटि संदेश था। मैं जोड़ना चाहूंगा: यदि आपको यह त्रुटि संदेश किसी @Query में मिलता है, तो आपको @TypeConverters को @Query एनोटेशन से ऊपर जोड़ना चाहिए।

उदाहरण:

@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

5

मैं व्यक्तिगत रूप से @TypeConverters/ क्रमबद्धता के खिलाफ सलाह दूंगा, क्योंकि वे डेटाबेस के सामान्य रूपों के अनुपालन को तोड़ते हैं।

इस विशेष मामले के लिए, यह @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-एन उदाहरण के अलावा, 1-1 और एनएम संबंधों को भी परिभाषित करना संभव है।


4

कोटलिन के क्रमांकन घटक - kotlinx.serialization का उपयोग करके मूल कोटलिन संस्करण

  1. कोटलिन क्रमांकन ग्रेड प्लग और निर्भरता को अपने में जोड़ें 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. अपने डेटाबेस वर्ग में अपने कनवर्टर वर्ग को जोड़ें:
@TypeConverters(Converters::class)
abstract class YourDatabase: RoomDatabase() {...}

और आपने कल लिया!

अतिरिक्त संसाधन:


3

यह उत्तर कॉटिन को कॉमा द्वारा विभाजित करने और कॉमा डीलिनेटेड स्ट्रिंग के निर्माण के लिए उपयोग करता है। अल्पविराम को सभी लेकिन अंतिम तत्व के अंत में जाने की आवश्यकता है, इसलिए यह एकल तत्व सूचियों को भी संभाल लेगा।

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

मेरे मामले में समस्या इस उत्तर पर सामान्य प्रकार का आधार थी

ArstList के बजाय https://stackoverflow.com/a/48480257/3675925 उपयोग सूची

 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) जोड़ने की जरूरत नहीं है और न ही एंटिटी क्लास में फील्ड और सिर्फ डेटाबेस क्लास में @TypeConverters (IntArrayListConverter) क्लास जोड़ें।

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

0

@TypeConvertersParams के रूप में कनवर्टर वर्ग के साथ जोड़ना

डेटाबेस और दाओ वर्ग के लिए, मेरे प्रश्नों ने काम किया


1
क्या आप अपना उत्तर विस्तृत कर सकते हैं ??
के प्रदीप कुमार रेड्डी

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

सरल डेटा प्रकारों के लिए उपरोक्त का उपयोग किया जा सकता है, अन्यथा जटिल डेटाटाइप्स के लिए कक्ष एंबेडेड प्रदान करता है


0

यहाँ CustomObject प्रकार को कक्ष DB तालिका में जोड़ने के लिए उदाहरण दिया गया है। https://mobikul.com/insert-custom-list-and-get-that-list-in-room-database-using-typeconverter/

एक प्रकार का कनवर्टर जोड़ना आसान था, मुझे बस एक ऐसी विधि की आवश्यकता थी जो वस्तुओं की सूची को एक स्ट्रिंग में बदल सकती है, और एक विधि जो रिवर्स कर सकती है। मैंने इसके लिए गेसन का इस्तेमाल किया।

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 का उपयोग करके r करते हैं तो डेटाटाइप को 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 " के स्थान पर , अपनी ऑब्जेक्ट क्लास जोड़ें।

 @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% मामलों में इकाई में स्टोर सरणी की आवश्यकता है तो आपको एक-से-कई या कई-से-कई संबंध बनाने की आवश्यकता है।

अन्यथा, इस सरणी के अंदर कुंजी के साथ कुछ का चयन करने के लिए आपकी अगली SQL क्वेरी बिल्कुल नरक होगी ...

उदाहरण:

ऑब्जेक्ट फू जूसन के रूप में आता है: [{आईडी: 1, नाम: "एब्स"}, {आईडी: 2, नाम: "शेड"}

ऑब्जेक्ट बार: [{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)

और अपने झोले में झांका: [] इस तालिका में रिकॉर्ड के रूप में।


यह मत मानिए कि अगर योपू आईडी की एक सूची संग्रहीत कर रहा था जो पहले एपीआई कॉल में उपलब्ध थी, लेकिन अगले में नहीं है, तो हर तरह से उन आईडी को स्टोर करें और फिर एपी स्टोर करने के लिए उनका उपयोग एक जंक्शन टेबल के साथ करें। , यह दोनों समाधानों का उपयोग करता है, मैं आपसे सहमत हूं कि यह एक आसान तरीका के रूप में देखा जा सकता है और बहुत सारे कारणों से महान नहीं है
martinseal1987

-2

कमरे से आधिकारिक समाधान का उपयोग करें, @ एंबेडेड एनोटेशन:

@Embedded(prefix = "mylist_array") private ArrayList<MyListItems> myListItems
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.