База данных резервных копий

Я пытаюсь сделать резервную копию базы данных комнаты программно.

Для этого я просто .sqlite файл .sqlite который содержит всю базу данных

Но, перед копированием, из - за того, что помещение имеет упреждающее журналирование включено, мы должны закрыть базу данных, так что -shm файл и -wal файл сливаются в единый .sqlite файл. Как указано здесь

Я запустил .close() в объекте RoomDatabase:

Все работает отлично с резервным копированием, НО, позже, когда я пытаюсь выполнить запрос INSERT, я получаю эту ошибку:

android.database.sqlite.SQLiteException: no such table: room_table_modification_log (code 1)

Как я могу правильно заново открыть комнату db после ее закрытия?

PS: .isOpen() на объекте RoomDatabase возвращает true до INSERT

Версия комнаты: 1.1.1-rc1

Ответ 1

Как я могу правильно открыть комнату БД после закрытия?

Мне жаль, что у меня нет ответа на этот вопрос.

Но если вы хотите переместить все в исходный файл базы данных, вам не нужно закрывать базу данных. wal_checkpoint этого вы можете форсировать контрольную точку, используя прагму wal_checkpoint.

Запросите следующее утверждение к базе данных. Здесь мы используем необработанные запросы, так как Room еще не поддерживает pragma (она вызовет ошибку UNKNOWN query type). Имейте этот запрос внутри вашего DAO:

@RawQuery
int checkpoint(SupportSQLiteQuery supportSQLiteQuery);

И затем, когда вы вызываете метод контрольной точки, используйте запрос затем:

myDAO.checkpoint(new SimpleSQLiteQuery("pragma wal_checkpoint(full)"));

Эта ссылка может пролить некоторый свет на то, что делает wal_checkpoint.

Ответ 2

Чтобы более точно ответить на ваш вопрос, так я резервирую базу данных в одной из моих приложений.

  1. Проверьте разрешение на чтение/запись во внешнюю память. Этот шаг можно проигнорировать, если вы пишете в своем каталоге файлов приложений.
  2. Закройте свою RoomDatabase. В моем случае AppDatabase ссылается на синглтон, содержащий AppDatabase логику для создания базы данных комнаты. AppDatabase.getInstance(this).getDatabase() получает текущий экземпляр singleton и его текущий класс базы данных, который выходит из RoomDatabase. Это по существу вызывает RoomDatabase.close().
  3. Определите исходный и целевой файлы, в зависимости от резервного копирования или восстановления. Я включаю файлы shm и wal, хотя это временные файлы.
  4. Скопируйте файлы с помощью выбранного вами метода. FileUtils в этом случае относится к commons-io.

Код

if(id == R.id.action_save_db) {
    int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if(permission == PackageManager.PERMISSION_GRANTED) {
        AppDatabase.getInstance(this).getDatabase().close();

        File db = getDatabasePath("my-db");
        File dbShm = new File(db.getParent(), "my-db-shm");
        File dbWal = new File(db.getParent(), "my-db-wal");

        File db2 = new File("/sdcard/", "my-db");
        File dbShm2 = new File(db2.getParent(), "my-db-shm");
        File dbWal2 = new File(db2.getParent(), "my-db-wal");

        try {
            FileUtils.copyFile(db, db2);
            FileUtils.copyFile(dbShm, dbShm2);
            FileUtils.copyFile(dbWal, dbWal2);
        } catch (Exception e) {
            Log.e("SAVEDB", e.toString());
        }
    } else {
        Snackbar.make(mDrawer, "Please allow access to your storage", Snackbar.LENGTH_LONG)
                .setAction("Allow", view -> ActivityCompat.requestPermissions(this, new String[] {
                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                }, 0)).show();
    }
} else if(id == R.id.action_load_db) {
    int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
    if(permission == PackageManager.PERMISSION_GRANTED) {
        AppDatabase.getInstance(this).getDatabase().close();

        File db = new File("/sdcard/", "my-db");
        File dbShm = new File(db.getParent(), "my-db-shm");
        File dbWal = new File(db.getParent(), "my-db-wal");

        File db2 = getDatabasePath("my-db");
        File dbShm2 = new File(db2.getParent(), "my-db-shm");
        File dbWal2 = new File(db2.getParent(), "my-db-wal");

        try {
            FileUtils.copyFile(db, db2);
            FileUtils.copyFile(dbShm, dbShm2);
            FileUtils.copyFile(dbWal, dbWal2);
        } catch (Exception e) {
            Loge("RESTOREDB", e.toString());
        }
    } else {
        Snackbar.make(mDrawer, "Please allow access to your storage", Snackbar.LENGTH_LONG)
                .setAction("Allow", view -> ActivityCompat.requestPermissions(this, new String[] {
                        Manifest.permission.READ_EXTERNAL_STORAGE
                }, 0)).show();
    }
 }

Ответ 3

В качестве альтернативы вы всегда можете создать свою базу данных в комнате, не заставляя ее не использовать запись на запись вперед:

Room.databaseBuilder(context, db.class, dbName)
    .setJournalMode(JournalMode.TRUNCATE)
    .build();

Ответ 4

Во-первых, база данных должна быть закрыта, чтобы применить изменения из "dbName.db-wal".

Затем вы можете скопировать базу данных со всеми таблицами и последними изменениями данных

 AppDatabase appDatabase = AppDatabase.getAppDatabase(getApplicationContext());
 appDatabase.close();

Ответ 5

Как правильно открыть базу данных после копирования файла? Я открываю его в AppModule следующим образом:

@Singleton
@Provides
fun provideDb(app: Application): FastcountDb {
    val db: FastcountDb = Room.databaseBuilder(app, FastcountDb::class.java, AppConfig.DB_NAME + ".db")
            .fallbackToDestructiveMigration()
            .setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
            .build()

    return db
}