অ্যান্ড্রয়েড রুম দৃistence় লাইব্রেরি: উপস্থাপন করুন


102

অ্যান্ড্রয়েডের রুম অধ্যবসায় গ্রন্থাগারটি দয়া করে @ সন্নিবেশ এবং @ আপডেট টীকা অন্তর্ভুক্ত করে যা বস্তু বা সংগ্রহের জন্য কাজ করে। তবে আমার কাছে একটি ব্যবহারের কেস রয়েছে (একটি মডেলযুক্ত বিজ্ঞপ্তিগুলি ধাক্কা করুন) যার জন্য একটি ইউপিএসআরটি প্রয়োজন হবে কারণ ডেটাবেজে ডেটা থাকতে পারে বা নাও থাকতে পারে।

স্ক্লাইটের স্থানীয়ভাবে উত্সাহ নেই, এবং কার্যকারণগুলি এই ও প্রশ্নটিতে বর্ণিত হয়েছে । সেখানে সমাধানগুলি দেওয়া হলে, কীভাবে কেউ সেগুলি রুমে প্রয়োগ করবেন?

আরও সুনির্দিষ্টভাবে বলার জন্য, আমি কীভাবে রুমে একটি সন্নিবেশ বা আপডেট প্রয়োগ করতে পারি যা কোনও বিদেশী কী সীমাবদ্ধতা ভঙ্গ করে না? OnConflict = REPLACE সহ সন্নিবেশ ব্যবহার করা সেই সারিটির যে কোনও বিদেশী কী ডাকার জন্য অনডিলিটকে ডেকে আনবে। আমার ক্ষেত্রে ডিলিট একটি ক্যাসকেড সৃষ্টি করে এবং একটি সারি পুনরায় প্রবেশ করানো বিদেশী কী সহ অন্যান্য সারণীতে সারি সরিয়ে ফেলবে cause এটি উদ্দেশ্যমূলক আচরণ নয়।

উত্তর:


81

সম্ভবত আপনি নিজের বেসডাওকে এটি তৈরি করতে পারেন।

@ ট্রানজেকশনের সাহায্যে অপারেটর অপারেশন সুরক্ষিত করুন এবং সন্নিবেশ ব্যর্থ হলেই আপডেট করার চেষ্টা করুন।

@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
তুমি একেবারেই সঠিক! আমি এটি মিস করেছি, আমি ভেবেছিলাম আপনি লুপের জন্য সন্নিবেশ করছিলেন। এটি একটি দুর্দান্ত সমাধান।
টুনজি_ডি

4
এই সোনার। এটি আমার Florina এর পোস্ট, যা আপনি পড়া উচিত নেতৃত্বে: medium.com/androiddevelopers/7-pro-tips-for-room-fbadea4bfbd1 - ইঙ্গিতটি জন্য ধন্যবাদ @ yeonseok.seo!
বেনোইট ডাফেজ

4
@ পিআরএ যতদূর আমি জানি, এটি মোটেই গুরুত্বপূর্ণ নয়। docs.oracle.com/javase/specs/jls/se8/html/… দীর্ঘ দীর্ঘ থেকে আনবক্স করা হবে এবং পূর্ণসংখ্যার সমতা পরীক্ষা করা হবে। আমি ভুল হলে দয়া করে আমাকে সঠিক দিকে নির্দেশ করুন।
yeonseok.seo

80

আরও মার্জিত উপায় করার জন্য আমি দুটি বিকল্প প্রস্তাব করব:

একটি হিসাবে insertঅপারেশন থেকে রিটার্ন মান পরীক্ষা করা হচ্ছে (যদি এটি -1 এর সমান হয় তবে এর অর্থ সারিটি sertedোকানো হয়নি):IGNOREOnConflictStrategy

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

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

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

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

9
এটি স্বতন্ত্র সত্তার পক্ষে ভাল কাজ করে তবে সংগ্রহের জন্য কার্যকর করা শক্ত। কী সংগ্রহগুলি sertedোকানো হয়েছিল তা ফিল্টার করে আপডেট থেকে সেগুলি ফিল্টার করে নিলে ভাল লাগবে।
টুনজি_ডি

4
@ ড্যানিয়েলসিলসন এটি আপনার আবেদনের উপর নির্ভর করে, এই উত্তরটি একক সত্তাগুলির পক্ষে ভাল কাজ করে, তবে এটি আমার কাছে যা আছে তা সত্ত্বার তালিকার জন্য প্রযোজ্য নয়।
টুনজি_ডি

4
যে কারণেই হোক না কেন, যখন আমি প্রথম পদ্ধতির কাজ করি, ইতিমধ্যে বিদ্যমান আইডি সন্নিবেশ করায় যা বিদ্যমান থাকে তার চেয়ে বেশি সারি সংখ্যা দেয়, -1 এল নয়।
এলিওটএম

4
Ohmnibus হিসাবে অন্যান্য উত্তরে বললেন, ভাল চিহ্নিত upsertসঙ্গে পদ্ধতি @Transactionটীকা - stackoverflow.com/questions/45677230/...
Dr.jacky

41

আমি কোনও এসকিউএল কোয়েরি খুঁজে পাইনি যা আমার বিদেশী কীতে অযাচিত পরিবর্তন ঘটিয়ে sertোকানো বা আপডেট করা হবে, সুতরাং এর পরিবর্তে আমি প্রথমে সন্নিবেশ করানো বেছে নিয়েছি, যদি সেগুলির বিরোধগুলি ঘটে থাকে তা উপেক্ষা করে এবং ততক্ষণে আপডেট করে আবারও দ্বন্দ্বগুলি উপেক্ষা করে।

সন্নিবেশ এবং আপডেটের পদ্ধতিগুলি সুরক্ষিত তাই বাহ্যিক ক্লাসগুলি কেবল সন্নিবেশ পদ্ধতিটি দেখতে এবং ব্যবহার করে। মনে রাখবেন যে এটি সত্যিকারের উপগ্রহ নয় যেমন মাইএনটিটি পিওজেসের কোনও শূন্য ক্ষেত্র রয়েছে, তারা বর্তমানে ডাটাবেসে কী থাকতে পারে সেগুলি ওভাররাইট করে। এটি আমার পক্ষে সাবধান নয়, তবে এটি আপনার আবেদনের জন্য হতে পারে।

@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
ওহমনিবাস

4
আমি অনুমান করি যে এটি করার সঠিক উপায়টি জিজ্ঞাসা করছে যে মানটি ইতিমধ্যে ডিবিতে ছিল (এটির প্রাথমিক কীটি ব্যবহার করে)। আপনি এটি করতে পারেন একটি অ্যাবস্ট্রাক্ট ক্লাস ব্যবহার করে (দাও ইন্টারফেসটি প্রতিস্থাপন করতে) বা ক্লাসটি ব্যবহার করে যা বস্তুর
দাওকে

@ ওহমনিবস না, কারণ ডকুমেন্টেশন বলছে> এই টীকাকে একটি sertোকান, আপডেট করুন বা মুছুন পদ্ধতির উপর প্রভাব ফেলবে না কারণ এগুলি সর্বদা লেনদেনের অভ্যন্তরে চালিত হয়। একইভাবে, যদি এটি ক্যোয়ারির সাথে টিকা দেওয়া থাকে তবে একটি আপডেট চালায় বা বিবৃতি মুছলে তা স্বয়ংক্রিয়ভাবে লেনদেনে মুড়ে যায়। লেনদেনের ডকটি দেখুন
লেভন বর্ধন্যান

4
@ লিভন ওয়ার্ডানিয়ান আপনার যে পৃষ্ঠায় লিঙ্ক করেছেন তার উদাহরণটিতে একটি সন্নিবেশ এবং একটি মুছুন যুক্ত একটি উপকরণের সাথে অনুরূপ একটি পদ্ধতি দেখায়। এছাড়াও, আমরা টীকাটি একটি সন্নিবেশ বা আপডেটে রাখছি না, তবে এমন একটি পদ্ধতিতে যা উভয়ই রয়েছে।
ওহমনিবাস

8

যদি টেবিলের একাধিক কলাম থাকে তবে আপনি ব্যবহার করতে পারেন

@Insert(onConflict = OnConflictStrategy.REPLACE)

একটি সারি প্রতিস্থাপন।

রেফারেন্স - অ্যান্ড্রয়েড রুম কোডল্যাব টিপসে যান


19
দয়া করে এই পদ্ধতিটি ব্যবহার করবেন না। আপনার যদি কোনও বিদেশী কী আপনার ডেটার দিকে তাকিয়ে থাকে, তবে এটি ডিলিট শ্রোতাকে ট্রিগার করবে এবং আপনি সম্ভবত এটি চান না
আলেকজান্ডার ঝুরকভ

@ আলেকজান্ডারজুরকোভ, আমি অনুমান করি এটি কেবল আপডেটে ট্রিগার করা উচিত, তবে যদি কোনও শ্রোতা এটি প্রয়োগ করে তবে এটি সঠিকভাবে করতে পারে would যাইহোক, যদি আমাদের ডেটাতে শ্রোতা থাকে এবং অন-ডিলিট ট্রিগার হয় তবে অবশ্যই কোড দ্বারা এটি পরিচালনা করা উচিত
বিকাশ পান্ডে

@ আলেকজান্ডারজুরকভ deferred = trueবিদেশী কী দিয়ে সত্তাটি স্থাপন করার সময় এটি ভাল কাজ করে।
উবুন্টুড্রয়েড

@ বুটুনড্রয়েড সবেমাত্র পরীক্ষা করা সত্তা বিদেশী কী-তে এই পতাকাটি স্থাপন করার পরেও এটি ভাল কাজ করে না। ডিলিট কলটি একবারে লেনদেন শেষ হওয়ার পরেও যায় কারণ এটি প্রক্রিয়া চলাকালীন বরখাস্ত হয় না, এটি ঘটে তখনই ঘটে না তবে লেনদেনের শেষে এখনও হয়।
শ্যাডো

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

4
লম্বা আইডি = সন্নিবেশ (সত্তা) কোটলিনের জন্য ভাল আইডি = সন্নিবেশ (সত্তা) হওয়া উচিত
কিবোটু

@ স্যাম, null valuesআমি যেখানে নাল দিয়ে আপডেট করতে চাই না তবে কীভাবে ডিল করতে হবে তবে পুরানো মান ধরে রাখতে পারি। ?
বিন্রেবিন

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

আপনি আরও বেশি জটিল লেনদেনের জন্য ডাটাবেস.ওপেনহেল্পার.গ্রন্থযোগ্য ডেটাবেস.এক্সসিএসকিউএল ("এসকিউএল স্ট্যাটেন্ট") ব্যবহার করে @ ট্রানজেকশন এবং ডাটাবেস কনস্ট্রাক্টর ভেরিয়েবল ব্যবহার করতে পারেন You


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
isabmitted

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

এটি ত্রুটিযুক্ত বলে মনে হচ্ছে? এটি কোনও লেনদেন সঠিকভাবে তৈরি করে না।
রাওয়া
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.