एंड्रॉइड रूम पर्सिस्टेंस लाइब्रेरी: उप्र


102

एंड्रॉइड के कमरे की दृढ़ता पुस्तकालय में विनम्रतापूर्वक @Insert और @Update एनोटेशन शामिल हैं जो ऑब्जेक्ट या संग्रह के लिए काम करते हैं। हालाँकि मेरे पास एक उपयोग का मामला है (एक मॉडल युक्त सूचनाएँ) जिसमें एक यूपीएसईआरटी की आवश्यकता होगी क्योंकि डेटा डेटाबेस में मौजूद हो सकता है या नहीं भी हो सकता है।

Sqlite में मूल रूप से मुखर नहीं है, और इस SO प्रश्न में वर्कअराउंड वर्णित हैं । वहाँ के समाधानों को देखते हुए, कोई उन्हें कमरे में कैसे लागू करेगा?

अधिक विशिष्ट होने के लिए, मैं कमरे में एक सम्मिलित या अद्यतन कैसे लागू कर सकता हूं जो किसी भी विदेशी कुंजी बाधाओं को नहीं तोड़ सकता है? OnConflict के साथ इन्सर्ट का उपयोग करना = REPLACE का कारण उस पंक्ति के लिए किसी भी विदेशी कुंजी के ऑनडेट को कहा जाएगा। मेरे मामले में onDelete एक झरना का कारण बनता है, और एक पंक्ति को फिर से स्थापित करने से विदेशी कुंजी के साथ अन्य तालिकाओं में पंक्तियों को हटा दिया जाएगा। यह अभीष्ट व्यवहार नहीं है।

जवाबों:


80

शायद आप अपने बेसडॉ को इस तरह से बना सकते हैं।

@ ऑपरेशन के साथ अपटर ऑपरेशन को सुरक्षित करें, और केवल तभी अपडेट करने का प्रयास करें जब सम्मिलन विफल हो।

@Dao
public abstract class BaseDao<T> {
    /**
    * Insert an object in the database.
    *
     * @param obj the object to be inserted.
     * @return The SQLite row id
     */
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public abstract long insert(T obj);

    /**
     * Insert an array of objects in the database.
     *
     * @param obj the objects to be inserted.
     * @return The SQLite row ids   
     */
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public abstract List<Long> insert(List<T> obj);

    /**
     * Update an object from the database.
     *
     * @param obj the object to be updated
     */
    @Update
    public abstract void update(T obj);

    /**
     * Update an array of objects from the database.
     *
     * @param obj the object to be updated
     */
    @Update
    public abstract void update(List<T> obj);

    /**
     * Delete an object from the database
     *
     * @param obj the object to be deleted
     */
    @Delete
    public abstract void delete(T obj);

    @Transaction
    public void upsert(T obj) {
        long id = insert(obj);
        if (id == -1) {
            update(obj);
        }
    }

    @Transaction
    public void upsert(List<T> objList) {
        List<Long> insertResult = insert(objList);
        List<T> updateList = new ArrayList<>();

        for (int i = 0; i < insertResult.size(); i++) {
            if (insertResult.get(i) == -1) {
                updateList.add(objList.get(i));
            }
        }

        if (!updateList.isEmpty()) {
            update(updateList);
        }
    }
}

यह प्रदर्शन के लिए बुरा होगा क्योंकि सूची में प्रत्येक तत्व के लिए कई डेटाबेस इंटरैक्शन होंगे।
तुन्जी_ड

13
लेकिन, वहाँ कोई "पाश के लिए सम्मिलित करें" है।
yeonseok.seo

4
आप बिल्कुल सही कह रहे है! मुझे याद आया कि, मुझे लगा कि आप लूप में सम्मिलित हो रहे हैं। यह एक महान समाधान है।
तुंजी_डी

2
यह सोना है। इसने मुझे फ्लोरिना के पद तक पहुँचाया , जिसे आपको पढ़ना चाहिए: medium.com/androiddevelopers/7-pro-tips-for-room-fbadea4bfbd1 - hint @ yeonseok.seo के लिए धन्यवाद!
बेनोइट डफेज़

1
@PRA जहाँ तक मुझे पता है, यह बिल्कुल भी मायने नहीं रखता। docs.oracle.com/javase/specs/jls/se8/html/… लंबे समय तक अनबॉक्स किया जाएगा और पूर्णांक समानता परीक्षण किया जाएगा। अगर मैं गलत हूं तो कृपया मुझे सही दिशा की ओर इशारा करें।
yeonseok.seo

80

अधिक सुरुचिपूर्ण तरीके से करने के लिए कि मैं दो विकल्प सुझाऊंगा:

insertऑपरेशन से रिटर्न वैल्यू की जाँच के IGNOREरूप में OnConflictStrategy(यदि यह -1 के बराबर है तो इसका मतलब है कि पंक्ति सम्मिलित नहीं की गई है):

@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(Entity entity);

@Update(onConflict = OnConflictStrategy.IGNORE)
void update(Entity entity);

public void upsert(Entity entity) {
    long id = insert(entity);
    if (id == -1) {
        update(entity);   
    }
}

से अपवाद हैंडलिंग insertके साथ आपरेशन FAILएक के रूप में OnConflictStrategy:

@Insert(onConflict = OnConflictStrategy.FAIL)
void insert(Entity entity);

@Update(onConflict = OnConflictStrategy.FAIL)
void update(Entity entity);

public void upsert(Entity entity) {
    try {
        insert(entity);
    } catch (SQLiteConstraintException exception) {
        update(entity);
    }
}

9
यह व्यक्तिगत संस्थाओं के लिए अच्छी तरह से काम करता है, लेकिन एक संग्रह के लिए लागू करना कठिन है। यह अच्छा होगा कि किस संग्रह को सम्मिलित किया गया था और उन्हें अपडेट से फ़िल्टर करें।
तुनजी_डी

2
@DanielWilson यह आपके आवेदन पर निर्भर करता है, यह उत्तर एकल संस्थाओं के लिए अच्छी तरह से काम करता है, हालांकि यह उन संस्थाओं की सूची के लिए लागू नहीं होता है जो मेरे पास है।
ट्यूनी_डे

2
जो भी कारण के लिए, जब मैं पहला दृष्टिकोण करता हूं, तो पहले से मौजूद आईडी डालने से एक पंक्ति संख्या अधिक होती है जो मौजूद है, न कि -1 एल।
इलियटम

41

मुझे एक ऐसी SQLite क्वेरी नहीं मिली, जो मेरी विदेशी कुंजी में अवांछित परिवर्तन किए बिना सम्मिलित या अपडेट करेगी, इसलिए मैंने पहले डालने का विकल्प चुना, यदि वे हुए तो संघर्षों को अनदेखा करना, और तुरंत बाद अपडेट करना, फिर से विरोधों को अनदेखा करना।

सम्मिलित करने और अपडेट करने के तरीके सुरक्षित हैं, इसलिए बाहरी कक्षाएं केवल ऊपरी विधि को देखती हैं और उनका उपयोग करती हैं। ध्यान रखें कि यह एक सच्चा उभार नहीं है जैसे कि MyEntity POJOS में से किसी के पास अशक्त क्षेत्र हैं, वे डेटाबेस में वर्तमान में क्या हो सकता है को अधिलेखित कर देंगे। यह मेरे लिए एक चेतावनी नहीं है, लेकिन यह आपके आवेदन के लिए हो सकता है।

@Insert(onConflict = OnConflictStrategy.IGNORE)
protected abstract void insert(List<MyEntity> entities);

@Update(onConflict = OnConflictStrategy.IGNORE)
protected abstract void update(List<MyEntity> entities);

@Transaction
public void upsert(List<MyEntity> entities) {
    insert(models);
    update(models);
}

6
आप इसे और अधिक कुशल बनाना चाहते हैं और रिटर्न वैल्यू की जांच कर सकते हैं। -1 सिग्नल किसी भी तरह के संघर्ष।
jcuypers

21
एनोटेशन के upsertसाथ विधि को बेहतर तरीके से चिह्नित करें@Transaction
ओमनीबस

3
मुझे लगता है कि ऐसा करने का उचित तरीका यह पूछ रहा है कि क्या मूल्य पहले से ही डीबी पर है (इसकी प्राथमिक कुंजी का उपयोग करके)। आप ऐसा कर सकते हैं कि एब्स्ट्रैक्टक्लास का उपयोग करना (डाओ इंटरफेस को बदलने के लिए) या उस ऑब्जेक्ट के डाओ को कॉल करने वाली कक्षा का उपयोग करना
सेबस्टियन कोराडी

@ ओम्निबस नं, क्योंकि प्रलेखन कहता है> इस एनोटेशन को एक इन्सर्ट, अपडेट या डिलीट मेथड पर डालने से कोई प्रभाव नहीं पड़ता क्योंकि वे हमेशा एक लेनदेन के अंदर चलाए जाते हैं। इसी तरह, यदि यह क्वेरी के साथ एनोटेट किया गया है, लेकिन एक अपडेट या डिलीट स्टेटमेंट चलाता है, तो यह स्वचालित रूप से एक लेनदेन में लिपट जाता है। देखें लेन-देन डॉक्टर
लेवोन वर्दयान 19

1
@LevonVardanyan आपके द्वारा लिंक किए गए पेज में उदाहरण एक विधि के लिए बहुत समान है, जिसमें सम्मिलित करना और हटाना शामिल है। इसके अलावा, हम एनोटेशन को किसी इंसर्ट या अपडेट में नहीं डाल रहे हैं, बल्कि ऐसी विधि के लिए जिसमें दोनों शामिल हैं।
ओह्मनिबस

8

यदि तालिका में एक से अधिक स्तंभ हैं, तो आप उपयोग कर सकते हैं

@Insert(onConflict = OnConflictStrategy.REPLACE)

एक पंक्ति को बदलने के लिए।

संदर्भ - युक्तियों पर जाएं Android कक्ष कोडेलैब


19
कृपया इस विधि का उपयोग न करें। यदि आपके पास कोई विदेशी कुंजी है जो आपके डेटा को देख रही है, तो यह onDelete श्रोता को ट्रिगर कर देगी और आप शायद यह नहीं चाहते हैं
एलेक्ज़ेंडर ज़ुरकोव

@AlexandrZhurkov, मुझे लगता है कि इसे केवल अपडेट पर ट्रिगर करना चाहिए, फिर कोई भी श्रोता अगर इसे लागू करता है तो यह सही तरीके से करेगा। वैसे भी अगर हमारे पास डेटा पर श्रोता हैं और onDelete ट्रिगर करता है तो इसे कोड द्वारा नियंत्रित किया जाना चाहिए
विकास पांडे

@AlexandrZhurkov यह deferred = trueविदेशी कुंजी के साथ इकाई पर स्थापित करते समय अच्छी तरह से काम करता है ।
15 अप्रैल को ubuntudroid

@ubuntudroid यह भी अच्छी तरह से काम नहीं करता है जब उस झंडे को विदेशी कुंजी पर सेट किया जाता है, बस परीक्षण किया जाता है। डिलीट कॉल अभी भी एक बार लेन-देन पूरा होने के बाद भी हो जाता है क्योंकि यह प्रक्रिया के दौरान खारिज नहीं किया जाता है, यह तब नहीं होता है जब ऐसा होता है लेकिन लेनदेन के अंत में अभी भी होता है।
शैडो

4

यह कोटलिन में कोड है:

@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(entity: Entity): Long

@Update(onConflict = OnConflictStrategy.REPLACE)
fun update(entity: Entity)

@Transaction
fun upsert(entity: Entity) {
  val id = insert(entity)
   if (id == -1L) {
     update(entity)
  }
}

1
लॉन्ग आईडी = इंसर्ट (यूनिट) वैल आईडी होना चाहिए = कोटलिन के लिए इंसर्ट (एंटिटी)
किबोटू

@Sam, मैं कैसे null valuesशून्य से अद्यतन नहीं करना चाहता, लेकिन पुराने मूल्य को बनाए रखना चाहता हूं। ?
Binrebin

3

कोटलिन मॉडल के डेटा को बनाए रखने के साथ ऐसा करने के लिए बस एक अद्यतन (उदाहरण के रूप में एक काउंटर में इसका उपयोग करने के लिए शायद):

//Your Dao must be an abstract class instead of an interface (optional database constructor variable)
@Dao
abstract class ModelDao(val database: AppDatabase) {

@Insert(onConflict = OnConflictStrategy.FAIL)
abstract fun insertModel(model: Model)

//Do a custom update retaining previous data of the model 
//(I use constants for tables and column names)
 @Query("UPDATE $MODEL_TABLE SET $COUNT=$COUNT+1 WHERE $ID = :modelId")
 abstract fun updateModel(modelId: Long)

//Declare your upsert function open
open fun upsert(model: Model) {
    try {
       insertModel(model)
    }catch (exception: SQLiteConstraintException) {
        updateModel(model.id)
    }
}
}

आप डेटाबेस का उपयोग करके अधिक जटिल लेनदेन के लिए @ ट्रांसशिप और डेटाबेस कंस्ट्रक्टर वैरिएबल का उपयोग भी कर सकते हैं ।openHelper.writableDatabase.execSQL ("SQL STATEMENT")


0

एक अन्य दृष्टिकोण जो मैं सोच सकता हूं कि इकाई को डीएओ द्वारा क्वेरी के माध्यम से प्राप्त करना है, और फिर कोई वांछित अपडेट करना है। पूर्ण इकाई को पुनः प्राप्त करने के कारण रनटाइम के संदर्भ में इस थ्रेड में अन्य समाधानों की तुलना में यह कम कुशल हो सकता है, लेकिन संचालन के संदर्भ में बहुत अधिक लचीलेपन की अनुमति देता है जैसे कि क्या फ़ील्ड / चर अद्यतन करने के लिए।

उदाहरण के लिए :

private void upsert(EntityA entityA) {
   EntityA existingEntityA = getEntityA("query1","query2");
   if (existingEntityA == null) {
      insert(entityA);
   } else {
      entityA.setParam(existingEntityA.getParam());
      update(entityA);
   }
}

0

इस प्रकार के कथन से संभव होना चाहिए:

INSERT INTO table_name (a, b) VALUES (1, 2) ON CONFLICT UPDATE SET a = 1, b = 2

आपका क्या अर्थ है? एनोटेशन ON CONFLICT UPDATE SET a = 1, b = 2द्वारा समर्थित नहीं है Room @Query
१ent

-1

यदि आपके पास विरासत कोड है: जावा में कुछ इकाइयाँ और BaseDao as Interface(जहाँ आप एक कार्य निकाय नहीं जोड़ सकते हैं) या आप जावा-बच्चों के लिए सभी के implementsसाथ बदलने के लिए बहुत आलसी हैं extends

नोट: यह केवल कोटलिन कोड में काम करता है। मुझे यकीन है कि आप कोटलिन में नया कोड लिख रहे हैं, मैं सही हूं? :)

अंत में एक आलसी समाधान दो जोड़ना है Kotlin Extension functions:

fun <T> BaseDao<T>.upsert(entityItem: T) {
    if (insert(entityItem) == -1L) {
        update(entityItem)
    }
}

fun <T> BaseDao<T>.upsert(entityItems: List<T>) {
    val insertResults = insert(entityItems)
    val itemsToUpdate = arrayListOf<T>()
    insertResults.forEachIndexed { index, result ->
        if (result == -1L) {
            itemsToUpdate.add(entityItems[index])
        }
    }
    if (itemsToUpdate.isNotEmpty()) {
        update(itemsToUpdate)
    }
}

ऐसा लगता है जैसे यह त्रुटिपूर्ण है? यह ठीक से लेनदेन नहीं करता है।
रावा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.