سؤال

نحن نستخدم AsyncTasks للوصول إلى جداول قاعدة البيانات والمؤشرات.

لسوء الحظ ، نشهد استثناءات عرضية فيما يتعلق بقاعدة البيانات التي يتم قفلها.

E/SQLiteOpenHelper(15963): Couldn't open iviewnews.db for writing (will try read-only):
E/SQLiteOpenHelper(15963): android.database.sqlite.SQLiteException: database is locked
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.native_setLocale(Native Method)
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.setLocale(SQLiteDatabase.java:1637)
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1587)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:638)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:659)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:652)
E/SQLiteOpenHelper(15963):  at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:482)
E/SQLiteOpenHelper(15963):  at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:158)
E/SQLiteOpenHelper(15963):  at com.iview.android.widget.IViewNewsTopStoryWidget.initData(IViewNewsTopStoryWidget.java:73)
E/SQLiteOpenHelper(15963):  at com.iview.android.widget.IViewNewsTopStoryWidget.updateNewsWidgets(IViewNewsTopStoryWidget.java:121)
E/SQLiteOpenHelper(15963):  at com.iview.android.async.GetNewsTask.doInBackground(GetNewsTask.java:338)
E/SQLiteOpenHelper(15963):  at com.iview.android.async.GetNewsTask.doInBackground(GetNewsTask.java:1)
E/SQLiteOpenHelper(15963):  at android.os.AsyncTask$2.call(AsyncTask.java:185)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:256)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.FutureTask.run(FutureTask.java:122)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:648)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:673)
E/SQLiteOpenHelper(15963):  at java.lang.Thread.run(Thread.java:1060)

هل لدى أي شخص مثال عام للرمز الذي يكتب إلى قاعدة بيانات من مؤشر ترابط مختلف عن القراءة الواحدة وكيف يمكننا ضمان سلامة مؤشرات الترابط.

اقتراح واحد لدي هو استخدام أ ContentProvider, ، لأن هذا من شأنه أن يتعامل مع وصول قاعدة البيانات من مؤشرات ترابط متعددة. سوف أنظر إلى هذا ، لكن هل هذه هي الطريقة الموصى بها للتعامل مع مثل هذه المشكلة؟ يبدو أن الوزن الثقيل بالنظر إلى أننا نتحدث في المقدمة أو الخلف.

هل كانت مفيدة؟

المحلول

لقد قمت بحل هذا الاستثناء نفسه فقط من خلال التأكد من إغلاق جميع قاعدة البيانات الخاصة بي ، (الأهم من ذلك) لضمان ذلك ، مما يجعل نطاق كل مثيل قاعدة بيانات محليًا فقط للطريقة التي تحتاجها. ContentProvider هي فئة جيدة وآمنة لاستخدامها عند الوصول إلى DB من مؤشرات ترابط متعددة ، ولكن أيضًا تأكد من أنك تستخدم ممارسات DB جيدة:

  • حافظ على مثيلات DB محلية (لا يوجد أعضاء فئة Sqlitedatabase!)
  • يتصل close() على DB في نفس الطريقة التي يتم فتحها بها
  • يتصل close() على المؤشرات التي تحصل عليها من DB
  • استمع إلى Logcat للحصول على أي شكاوى قد يكون لها sqlitedatabse

نصائح أخرى

استخدمنا أ ContentProvider في النهاية. هذا يبدو لتوضيح المشاكل.

قبل بعض التعليمات البرمجية ، دعنا نستأنف بعض النهج:

  • الرسائل: إلى حد بعيد أفضل حل. يذهب في قلب المشكلة: مشاركة الموارد! سوف يعالج قفل الوصول إلى قاعدة البيانات ، وتجنب النزاعات (database is locked).

  • تزامن جافا: نوع من تنفيذ الإشارة ، ولكن أقل صدق. استخدام synchronized لن تحل بسهولة بعض الحالات التي تنطوي على المعاملات.

  • ContentProvider: ينفذ ContentProvider حل المشكلة فقط لبعض الحالات (أو اكتسح المشكلة تحت السجادة). ستواجه نفس القضايا. الفرق هو ذلك ContentProvider سوف يرشدك النمط إلى عدم ارتكاب بعض أخطاء Commom عند الوصول إلى قاعدة بيانات SQLite. ال contentProvider مستندات يقول: "لا تحتاج إلى مزود لاستخدام قاعدة بيانات SQLite إذا كان الاستخدام بالكامل ضمن تطبيقك الخاص."

  • تقريبا إلزامي: الحفاظ على مثيلات DB محلية ، اتصل close() على DB في نفس الطريقة التي يتم فيها فتحها باستخدام finally صياغات، close() على المؤشرات باستخدام finally العبارات وما إلى ذلك تقريبيا إلزامي لتجنب المشاكل باستخدام SQLite.

دعنا نعرض مثالًا على حل Semaphore المقدم بواسطة موس, التي أخذتها من CL. و Improived لتغطية المعاملات.

class DataAccess {
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data readSomething(int id) {
        Cursor c = null;
        r.lock();
        try {
            c = getReadableDatabase().query(...);
            return c.getString(0);
        } finally {
            if (c != null) c.close();
            r.unlock();
        }
    }

    public void changeSomething(int id, int value) {
        w.lock();
        try {
            getWritableDatabase().update(...);
        } finally {
            w.unlock();
        }
    }

    private void beginTransactionWithSemaphores() {
        getWritableDatabase().beginTransactionWithListener(new SQLiteTransactionListener() {
            @Override
            public void onBegin() {
                w.lock();
            }

            @Override
            public void onRollback() {
                w.unlock();
            }

            @Override
            public void onCommit() {
                w.unlock();
            }
        });
    }
}

خذ في الاعتبار أن قواعد بيانات SQLite تعتمد على الملفات ولا تهدف إلى أن تكون قادرة على الوصول إليها بطريقة متعددة العمليات. أفضل إجراء على خلط SQLite مع المعالجة المتعددة هو استخدام Semaphores (Aquire () ، release ()) في كل وصول متعلق بقاعدة البيانات.

إذا قمت بإنشاء غلاف DB الذي يطلق/يطلق Semaphore Global ، فسيكون وصول DB آمنًا. في الواقع ، هذا يعني أنه يمكنك الحصول على bootleneck لأنك تصطف على الوصول إلى DB. بالإضافة إلى ذلك ، يمكنك فقط لف الوصول باستخدام Semaphores إذا كانت عملية تغير قاعدة البيانات ، لذلك بينما تقوم بتغيير DB ، لن يتمكن أي شخص من الوصول إليه والانتظار حتى يتم الانتهاء من عملية الكتابة.

لم نتمكن من مشاركة اتصال DB مع مؤشر ترابط متعددة لإجراء عملية القراءة والكتابة في قاعدة البيانات بشكل متزامن. سيتعين علينا إنشاء كائن واحد من DB باستخدام مفهوم التزامن وسنقوم بإجراء مهمة واحدة في وقت واحد. سنستخدم نمط Singleton لجعل DB الكائن وسيتم مشاركته ضمن قوائم متعددة. سيؤدي الوقت إلى أداء مهمة واحدة. ثم سنبدأ مهمة أخرى أو أي عملية على DB. مزود المحتوى ليس هو حل مشكلة قفل DB.

import java.util.concurrent.atomic.AtomicInteger;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DatabaseManager {

private AtomicInteger mOpenCounter = new AtomicInteger();

private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
//private static String DB_PATH = "";
//  private static String DB_NAME = "xyz.db";// Database name
private static String dbPathh;

public static synchronized void initializeInstance(SQLiteOpenHelper helper,
        String dbPath) {
    if (instance == null) {
        instance = new DatabaseManager();
        mDatabaseHelper = helper;
        dbPathh=dbPath;
    }
  }

public static synchronized DatabaseManager getInstance() {
    if (instance == null) {
        throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
                " is not initialized, call initializeInstance(..) method first.");
    }

    return instance;
 }

  public synchronized SQLiteDatabase openDatabase(String thread) {

    if(mOpenCounter.get() == 0) {
        // Opening new database
        // mDatabase = mDatabaseHelper.getWritableDatabase();
        MyLog.e("Path Of DataBase", dbPathh);
        //  mDatabase=mDatabaseHelper.getWritableDatabase();
        mOpenCounter.incrementAndGet();
        mDatabase=SQLiteDatabase.openDatabase(dbPathh, null,   
 SQLiteDatabase.  CREATE_IF_NECESSARY|SQLiteDatabase.OPEN_READWRITE);   
        MyLog.e("Open Data Base", " New Connection created" +thread);
    }
    else{
        MyLog.e("Open Data Base", " Old Connection given " +thread);
    }
    //  Toast.makeText(NNacres.getConfig(), "open conn: present connection = 
   "   +mOpenCounter.get(), Toast.LENGTH_LONG).show();
    return mDatabase;
   }

    public synchronized void closeDatabase() {
    MyLog.e("Close db connection", ""+mOpenCounter.get());

    if(mOpenCounter.get() == 1) {
        // Closing database

        mDatabase.close();
        mOpenCounter.decrementAndGet();

        Log.e("DB CLOSED", "DONE");
    }
    //Toast.makeText(NNacres.getConfig(), "close conn: after close =   
 " +mOpenCounter.get(), Toast.LENGTH_LONG).show();
    }

    }

واكتب هذه الطريقة في فئة مساعدك

     public SQLiteDatabase getWritableDatabase() {
DatabaseManager.initializeInstance(this,"data/data/your packgae name/databases/xyz");
    return DatabaseManager.getInstance().openDatabase(getClass().getSimpleName());

}



public static String getMyDbPath(String DB_NAME, Context context) {

    String myDbPath = context.getDatabasePath(DB_NAME).getPath();
    MyLog.e("DB Path: "+myDbPath);
    return myDbPath;
}

يجب أن تتصل getWritableDatabase() من وظيفة بدلاً من ذلك ، فإن مُنشئ فئة DB Helper. إذا تم إنشاء كائن فئة DB Helper SQLiteDatabase.openOrCreateDatabase(DB_PATH, null); أو مشابه وبعد ذلك getWritableDatabase() يتم استدعاؤه من وظيفة ، وسوف يحاول إجراء مكالمة متزامنة إلى DB مما تسبب في استثناء قفل DB.

هل تتحدث عن إجراء مستخدم واحد ، داخل البرنامج ، يتسبب في تشغيل مؤشرات ترابط متعددة ، قد يصل أكثر من واحد منها إلى قاعدة البيانات في وضع التحديث؟

هذا تصميم سيء ، الفترة. لا توجد طريقة لتعرفك في الترتيب الذي سيتم فيه جدولة المواضيع بواسطة نظام التشغيل (/VM) ، وبالتالي لا توجد طريقة لتعرفك على أي ترتيب سيحدث قاعدة البيانات ، ومن المحتمل جدًا أن يعني ذلك جدًا أنه لا توجد طريقة لك لمعرفة أن الوصول إلى قاعدة البيانات ستحدث دائمًا بالترتيب الذي تتوقعه.

يجب أن يتم جميع الوصول إلى جميع قاعدة البيانات التي يتم إنشاؤها بواسطة/تأتي من بعض إجراءات المستخدم في مؤشر ترابط واحد.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top