数据库备份与恢复 (C/C++)
================

#### 场景介绍

如果操作或存储的过程中出现问题，开发者可以使用恢复功能，将数据库恢复到之前的状态，重新对数据库进行操作。

在数据库被篡改、删除、或者设备断电场景下，数据库可能会因为数据丢失、数据损坏、脏数据等而不可用，可以通过数据库的备份恢复能力将数据库恢复至可用状态。

当前仅支持使用关系型数据库（C/C++）进行备份与恢复。

#### 开发步骤

数据库操作或者存储过程中，有可能会因为各种原因发生非预期的数据库异常的情况，可以根据需要使用关系型数据库的备份能力，以便在数据库异常时，可靠高效地恢复数据保证业务数据正常使用。

1.  CMakeLists.txt中添加以下lib。
    
    libnative\_rdb\_ndk.z.so
    
2.  导入头文件。
    
    #include <cstring>
    #include "database/rdb/relational\_store.h"
    #include "hilog/log.h"
    
3.  调用OH\_Rdb\_Backup接口实现数据库备份。
    
    OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
    OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el2/database");
    OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL2);
    OH\_Rdb\_SetStoreName(config, "RdbTest.db");
    OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
    OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
    int errCode = 0;
    OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
    // 备份数据库
    int result = OH\_Rdb\_Backup(store, "/data/storage/el2/database/RdbTest\_bak.db");
    OH\_Rdb\_CloseStore(store);
    store = nullptr;
    OH\_Rdb\_DestroyConfig(config);
    config = nullptr;
    
4.  调用OH\_Rdb\_Restore接口实现数据库恢复。
    
    OH\_Rdb\_ConfigV2 \*config = OH\_Rdb\_CreateConfig();
    OH\_Rdb\_SetDatabaseDir(config, "/data/storage/el2/database");
    OH\_Rdb\_SetArea(config, RDB\_SECURITY\_AREA\_EL2);
    OH\_Rdb\_SetStoreName(config, "RdbRestoreTest.db");
    OH\_Rdb\_SetSecurityLevel(config, OH\_Rdb\_SecurityLevel::S3);
    OH\_Rdb\_SetBundleName(config, "com.example.nativedemo");
    int errCode = 0;
    OH\_Rdb\_Store \*store = OH\_Rdb\_CreateOrOpen(config, &errCode);
    // 恢复数据库
    int result2 =
        OH\_Rdb\_Restore(store, "/data/storage/el2/database/RdbTest\_bak.db");
    OH\_Rdb\_CloseStore(store);
    store = nullptr;
    OH\_Rdb\_DestroyConfig(config);
    config = nullptr;
    
5.  调用OH\_Rdb\_RegisterCorruptedHandler接口注册数据库异常处理。
    
    从API version 22开始，支持注册数据库异常处理，开发者可根据需要调用OH\_Rdb\_RegisterCorruptedHandler接口注册数据库异常处理。
    
    // 数据库异常后处理的回调函数。
    // context为OH\_Rdb\_RegisterCorruptedHandler调用时传入的指针，生命周期由业务自身管理
    // config为OH\_Rdb\_RegisterCorruptedHandler调用时拷贝的临时变量，不可在回调函数外部使用
    // store为发生异常的DB句柄，如果DB无法打开则为空指针，注意判空。该指针由系统产生，回调函数结束后即刻释放，不可在回调函数外部使用
    void CorruptedHandler(void \*context, OH\_Rdb\_ConfigV2 \*config, OH\_Rdb\_Store \*store)
    {
        const char\* restorePath = "/data/storage/el2/database/RdbTest\_bak.db";
        // store为空代表非DB文件或者DB文件彻底异常无法打开
        if (store == nullptr) {
            OH\_Rdb\_DeleteStoreV2(config);
            // 重新创建数据库，如果有备库可以重建后调用恢复接口
            return;
        }
        // 通过store句柄使用备库进行数据库恢复
        int errCode = OH\_Rdb\_Restore(store, restorePath);
        // restore在有其它接口占用写链接时会失败，建议等待其它调用结束后再调用
        if (errCode != 0) {
            OH\_LOG\_ERROR(LOG\_APP, "restore failed! errCode is: %{public}d", errCode);
            // 等待其它线程调用结束，进行重试。不建议重试次数过多或等待时间过长，避免占用太多系统资源。
            errCode = OH\_Rdb\_Restore(store, restorePath);
            // 或采用标记的方式标记数据库异常，后续在进程重启或业务空闲时进行恢复
        }
    }
    OH\_Rdb\_ConfigV2\* config3 = OH\_Rdb\_CreateConfig();
    OH\_Rdb\_SetDatabaseDir(config3, "/data/storage/el2/database");
    OH\_Rdb\_SetArea(config3, RDB\_SECURITY\_AREA\_EL2);
    OH\_Rdb\_SetStoreName(config3, "RdbRestoreTest.db");
    OH\_Rdb\_SetSecurityLevel(config3, OH\_Rdb\_SecurityLevel::S3);
    OH\_Rdb\_SetBundleName(config3, "com.example.nativedemo");
    int errCode3 = 0;
    OH\_Rdb\_Store \*store3 = OH\_Rdb\_CreateOrOpen(config3, &errCode3);
    
    // 备份数据库
    int result = OH\_Rdb\_Backup(store3, "/data/storage/el2/database/RdbTest\_bak.db");
    
    void \*context = nullptr;
    Rdb\_CorruptedHandler handler = CorruptedHandler;
    // 注册数据库异常处理
    OH\_Rdb\_RegisterCorruptedHandler(config3, context, handler);
    
6.  调用OH\_Rdb\_UnregisterCorruptedHandler接口取消注册数据库异常处理。
    
    从API version 22开始，支持取消注册数据库异常处理，开发者可根据需要调用OH\_Rdb\_UnregisterCorruptedHandler接口取消注册数据库异常处理。
    
    OH\_Rdb\_ConfigV2\* config4 = OH\_Rdb\_CreateConfig();
    OH\_Rdb\_SetDatabaseDir(config4, "/data/storage/el2/database");
    OH\_Rdb\_SetArea(config4, RDB\_SECURITY\_AREA\_EL2);
    OH\_Rdb\_SetStoreName(config4, "RdbRestoreTest.db");
    OH\_Rdb\_SetSecurityLevel(config4, OH\_Rdb\_SecurityLevel::S3);
    OH\_Rdb\_SetBundleName(config4, "com.example.nativedemo");
    int errCode4 = 0;
    OH\_Rdb\_Store \*store4 = OH\_Rdb\_CreateOrOpen(config4, &errCode4);
    
    void \*context = nullptr;
    Rdb\_CorruptedHandler handler = CorruptedHandler;
    // 取消注册数据库异常处理，handler和context必须要和订阅时保持一致，否则取消失败
    OH\_Rdb\_UnregisterCorruptedHandler(config4, context, handler);