sqlite 备份机房数据库

ut6juiuv  于 2022-11-15  发布在  SQLite
关注(0)|答案(7)|浏览(156)

我正在尝试以编程方式备份房间数据库。
为此,我只需复制包含整个数据库的.sqlite文件
但是,在复制之前,由于房间启用了预写日志,我们必须关闭数据库,以便将-shm文件和-wal文件合并到单个.sqlite文件中。As pointed out here
我对RoomDatabase对象运行.close()
备份一切正常,但稍后,当我尝试执行INSERT查询时,我收到以下错误:

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

关闭房间db后如何正确重新打开?

Ps:RoomDatabase对象上的.isOpen()INSERT之前返回true
房型:1.1.1-rc1

but5z9lq

but5z9lq1#

这并没有回答最初的问题
关闭房间db后如何正确地重新打开它?
但是,将所有内容移动到原始数据库文件是您想要做的,这样您就不必首先关闭数据库。相反,您可以使用wal_checkpoint杂注强制检查点。
针对数据库查询以下语句。我们在这里使用原始查询,因为Room还不支持pragma(它将触发UNKNOWN query type错误)。在您的DAO中包含以下查询:

@RawQuery
int checkpoint(SupportSQLiteQuery supportSQLiteQuery);

然后,当您调用检查点方法时,使用以下查询:

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

这一链接可能会为wal_checkpoint的功能提供一些线索。

ncecgwcz

ncecgwcz2#

为了更确切地回答你的问题,这是我如何在我的一个应用程序中备份房间数据库的。
1.检查对外部存储器的读/写权限。如果您写入您的应用程序文件目录,则可以忽略此步骤。
1.关闭你的RoomDatabase。在我的例子中,AppDatabase指的是一个单例,它包含最初用于构建房间数据库的逻辑。AppDatabase.getInstance(this).getDatabase()获取从RoomDatabase扩展的单例及其当前数据库类的当前示例。这实际上称为RoomDatabase.close()
1.定义源文件和目标文件,具体取决于备份或还原。我包括SHM和WAL文件,即使它们是临时文件。
1.使用您选择的方法复制文件。在本例中,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();
    }
 }
mo49yndu

mo49yndu3#

作为替代方案,您可以始终创建Room数据库,同时强制其不使用预写日志记录:

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

2lpgd9684#

为了更确切地回答你的问题,这是我如何在我的一个应用程序中备份房间数据库的。
1-检查对外部存储器的读/写权限。2-关闭您的房间数据库。在我的例子中,AppDatabase指的是一个单例,它包含最初用于构建房间数据库的逻辑。GetInstance(this@MainActivity)获取从RoomDatabase扩展的单例及其当前数据库类的当前示例。3-然后实质上调用dbInstance.Close()。

private fun createBackup() {
        val db = AppDatabase.getInstance(this@MainActivity)
        db.close()
        val dbFile: File = getDatabasePath(DATABASE_NAME)
        val sDir = File(Environment.getExternalStorageDirectory(), "Backup")
        val fileName = "Backup (${getDateTimeFromMillis(System.currentTimeMillis(), "dd-MM-yyyy-hh:mm")})"
        val sfPath = sDir.path + File.separator + fileName
        if (!sDir.exists()) {
            sDir.mkdirs()
        }
        val saveFile = File(sfPath)
        if (saveFile.exists()) {
            Log.d("LOGGER ", "File exists. Deleting it and then creating new file.")
            saveFile.delete()
        }
        try {
            if (saveFile.createNewFile()) {
                val bufferSize = 8 * 1024
                val buffer = ByteArray(bufferSize)
                var bytesRead: Int
                val saveDb: OutputStream = FileOutputStream(sfPath)
                val indDb: InputStream = FileInputStream(dbFile)
                do {
                    bytesRead = indDb.read(buffer, 0, bufferSize)
                    if (bytesRead < 0)
                        break
                    saveDb.write(buffer, 0, bytesRead)
                } while (true)
                saveDb.flush()
                indDb.close()
                saveDb.close()
            }
        } catch (e: Exception) {
            e.printStackTrace()

        }
    }

您必须将保存的文件包含在

try {
//backup process
      }
        } catch (e: Exception) {
            e.printStackTrace()

        }

以便在出现任何错误时进行排序,并避免应用程序崩溃。
要从CurrentTimeMillis中获取日期,请使用以下函数

fun getDateTimeFromMillis(millis: Long, pattern: String): String {
    val simpleDateFormat = SimpleDateFormat(pattern, Locale.getDefault()).format(Date())
    return simpleDateFormat.format(millis)
}

重启数据库代码*将文件对象传递给Uri.from文件

try {
                                    val fileUri: Uri = Uri.fromFile(file)
                                    val inputStream = contentResolver.openInputStream(fileUri)
                                    println("restoring ")
                                    restoreDatabase(inputStream);
                                    inputStream?.close()
                                } catch (e: IOException) {
                                    println( e.message)
                                    e.printStackTrace()
                                }

或返回结果的开始活动的结果

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 12 && resultCode == RESULT_OK && data != null) {
            Uri fileUri = data.getData();
            try {
                assert fileUri != null;
                InputStream inputStream = getContentResolver().openInputStream(fileUri);       
                    restoreDatabase(inputStream);
                    inputStream.close();
                
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

RestoreDatabase函数

private fun restoreDatabase(inputStreamNewDB: InputStream?) {
        val db = AppDatabase.getInstance(this@MainActivity)
        db.close()
        val oldDB = getDatabasePath(DATABASE_NAME)
        if (inputStreamNewDB != null) {
            try {
                copyFile(inputStreamNewDB as FileInputStream?, FileOutputStream(oldDB))
                println("restore success")
            } catch (e: IOException) {
                Log.d("BindingContextFactory ", "ex for is of restore: $e")
                e.printStackTrace()
            }
        } else {
            Log.d("BindingContextFactory ", "Restore - file does not exists")
        }
    }

**现在需要将备份中的文件复制到真实的数据库文件中使用复制文件

@Throws(IOException::class)
    fun copyFile(fromFile: FileInputStream?, toFile: FileOutputStream) {
        var fromChannel: FileChannel? = null
        var toChannel: FileChannel? = null
        try {
            fromChannel = fromFile?.channel
            toChannel = toFile.channel
            fromChannel?.transferTo(0, fromChannel.size(), toChannel)
        } finally {
            try {
                fromChannel?.close()
            } finally {
                toChannel?.close()
            }
        }
    }
idv4meu8

idv4meu85#

需要做的第一件事是创建具有适当日记模式的数据库。

Room.databaseBuilder(context, AppDatabase::class.java, name)
    .setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
    .build()

之后,需要执行以下检查点查询,以确保应用所有挂起的事务。
为此,需要向数据库DAO接口添加以下方法

interface UserDao {
    @RawQuery
    fun checkpoint(supportSQLiteQuery: SupportSQLiteQuery?): Single<Int>
}

然后需要使用以下SQL查询调用该方法

userDao.checkpoint((SimpleSQLiteQuery("pragma wal_checkpoint(full)")))

一旦检查点方法成功,就可以最终保存数据库备份文件。
最后,可以使用以下代码检索数据库备份文件

File(database.openHelper.writableDatabase.path)

然后需要将该文件复制到备份文件位置。
要恢复文件,需要做的唯一一件事是用备份文件覆盖数据库文件(这可以使用上面的片段检索)。
你可以在我的博客上读到更详细的内容
https://androidexplained.github.io/android/room/2020/10/03/room-backup-restore.html

a11xaf1n

a11xaf1n6#

首先,必须关闭数据库才能应用"dbName.db-wal"文件中的更改。
然后,您可以复制包含所有表和上次数据更改的数据库

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

fdbelqdn7#

上面已经回答过了。无需关闭/重新打开数据库。
我在我的安卓应用程序中使用MVVM模式来备份数据库文件,以便将其上传到Google Drive。我只想总结一下对我有效的解决方案:
在DAO文件中提到以下代码:

@RawQuery
    int checkpoint(SupportSQLiteQuery supportSQLiteQuery);

在您的存储库文件中提到以下代码:

/* Android database has three files under /data/data/com.package.app/databases/
    ** test.db, test.db-shm, test.db-wal - those extra files have recent commits.
    ** To merge data from other shm and wal files to db, run following method - useful before taking backup.
    */
    void checkPoint() {
        ItemRoomDatabase.databaseWriteExecutor.execute(() -> {
           itemDao.checkpoint(new SimpleSQLiteQuery("pragma wal_checkpoint(full)"));
        });
    }

在您的ViewModel中提到以下内容:

public void checkPoint() {
      itemRepository.checkPoint();
}

现在,您可以在从活动文件执行备份之前调用此方法

ItemViewModel itemViewModel = new ViewModelProvider(this).get(ItemViewModel.class);
itemViewModel.checkPoint();

相关问题