rntpc_react-native-sqlite-storage:基于 react-native-sqlite-storage 的 OpenHarmony 适配版,SQLite 数据库存储

基于 react-native-sqlite-storage 的 OpenHarmony 适配版,SQLite 数据库存储

分支5Tags5
当前项目代码仓暂无内容

模板版本:v0.4.2

react-native-sqlite-storage

本项目基于 react-native-sqlite-storage 开发。

该第三方库的仓库已迁移至 Gitcode,并支持直接从 npm 下载。新的包名为:@react-native-ohos/react-native-sqlite-storage。版本从属关系如下:

三方库名称 三方库版本(npm 地址) 发布信息 支持的 RN 版本 Autolink 编译 API 版本 社区基线版本 源码地址
@react-native-ohos/react-native-sqlite-storage ~ 6.2.0(开发中) Gitcode 发布 0.82/0.84 API12+ 6.0.1 master
@react-native-ohos/react-native-sqlite-storage ~ 6.1.2 Gitcode 发布 0.77 API12+ 6.0.1 br_rnoh0.77
@react-native-ohos/react-native-sqlite-storage ~ 6.0.4 Gitcode 发布 0.72 API12+ 6.0.1 br_rnoh0.72
@react-native-oh-tpl/react-native-sqlite-storage <= 6.0.1-0.1.1@deprecated GitHub 发布(已弃用) 0.72 API12+ 6.0.1 sig

简介

这是一个基于 SQLite 的 React Native 本地数据库第三方库,提供数据库创建/打开、SQL 查询与更新、事务处理等常用能力,便于在应用内持久化结构化数据。

下载安装

进入工程目录并执行以下命令:

npm

npm install @react-native-ohos/react-native-sqlite-storage

yarn

yarn add @react-native-ohos/react-native-sqlite-storage

以下代码展示了该库的基本使用场景:

使用时,import 的库名保持不变。

import React, { Component } from 'react';
import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from 'react-native';
import SQLite from 'react-native-sqlite-storage';

// 全局基础配置
SQLite.DEBUG(true);
SQLite.enablePromise(false);

const db_name = 'Test.db';
const db_version = '1.0';
const db_displayname = 'SQLite Test Database';
const db_size = 200000;
let db;

class SQLiteDemo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      progress: [],
    };
  }

  updateProgress = (text) => {
    this.setState((prevState) => ({
      progress: [...prevState.progress, text],
    }));
  };

  // 1. 打开/初始化数据库
  loadDB = () => {
    this.updateProgress('Opening database...');
    db = SQLite.openDatabase(
      db_name, db_version, db_displayname, db_size,
      () => { this.updateProgress('Database OPENED successfully'); },
      (err) => { this.updateProgress('Database OPEN Error: ' + err.message); }
    );
  };

  // 2. 事务分发与写操作 (执行建表、表插入等)
  populateDB = () => {
    if (!db) return this.updateProgress('Error: Please Open DB first');
    this.updateProgress('Populating database...');
    db.transaction((tx) => {
      tx.executeSql('DROP TABLE IF EXISTS Users;');
      tx.executeSql(
        'CREATE TABLE IF NOT EXISTS Users(id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(55))',
        [],
        () => this.updateProgress('Table created successfully'),
        (err) => this.updateProgress('Table create failed: ' + err.message)
      );
      tx.executeSql('INSERT INTO Users (name) VALUES ("HarmonyOS");', []);
      tx.executeSql('INSERT INTO Users (name) VALUES ("React Native");', []);
      this.updateProgress('Insert queries executed');
    });
  };

  // 3. 执行查询操作并解析结果集
  queryData = () => {
    if (!db) return this.updateProgress('Error: Please Open DB first');
    this.updateProgress('Querying data...');
    db.transaction((tx) => {
      tx.executeSql(
        'SELECT * FROM Users',
        [],
        (tx, results) => {
          let len = results.rows.length;
          this.updateProgress('Query completed, total rows: ' + len);
          for (let i = 0; i < len; i++) {
            let row = results.rows.item(i);
            this.updateProgress('Record ' + i + ': ' + row.name);
          }
        },
        (err) => this.updateProgress('Query error: ' + err.message)
      );
    });
  };

  // 4. 重置与清理操作 (关闭与删除)
  closeDB = () => {
    if (db) {
      db.close(
        () => {
          this.updateProgress('Database CLOSED (connection released on API 12+, error reported on older versions)');
          db = null;
        },
        (err) => this.updateProgress('Database CLOSE Error: ' + err.message)
      );
    } else {
      this.updateProgress('Database is already closed');
    }
  };

  deleteDB = () => {
    this.updateProgress('Deleting database...');
    this.closeDB(); 
    SQLite.deleteDatabase(
      db_name,
      () => this.updateProgress('Database DELETED'),
      (err) => this.updateProgress('Database DELETE Error: ' + err.message)
    );
    SQLite.deleteDatabase(
      'test2.db',
      () => this.updateProgress('test2.db DELETED'),
      (err) => this.updateProgress('test2.db DELETE Error: ' + err.message)
    );
  }

  // 5. 附加其他数据库
  attachDB = () => {
    if (db) {
       this.updateProgress('Attaching test2.db...');
       let dbMaster = SQLite.openDatabase('test2.db', db_version, db_displayname, db_size, () => {
          dbMaster.attach(
            db_name, 
            'aliasName', 
            () => this.updateProgress('Database Attached successfully'), 
            (err) => this.updateProgress('Attach ERROR: ' + err.message)
          );
       });
    } else {
      this.updateProgress('Error: Master DB not open');
    }
  }

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.toolbar}>
          <TouchableOpacity style={styles.btn} onPress={this.loadDB}><Text style={styles.btnText}>Open DB</Text></TouchableOpacity>
          <TouchableOpacity style={styles.btn} onPress={this.populateDB}><Text style={styles.btnText}>Populate</Text></TouchableOpacity>
          <TouchableOpacity style={styles.btn} onPress={this.queryData}><Text style={styles.btnText}>Query</Text></TouchableOpacity>
          <TouchableOpacity style={styles.btn} onPress={this.attachDB}><Text style={styles.btnText}>Attach</Text></TouchableOpacity>
          <TouchableOpacity style={styles.btnDanger} onPress={this.closeDB}><Text style={styles.btnText}>Close DB</Text></TouchableOpacity>
          <TouchableOpacity style={styles.btnDanger} onPress={this.deleteDB}><Text style={styles.btnText}>Delete DB</Text></TouchableOpacity>
        </View>
        <ScrollView style={styles.logContainer}>
          {this.state.progress.map((item, index) => (
            <Text key={index} style={styles.logText}>{item}</Text>
          ))}
        </ScrollView>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 10 },
  toolbar: { flexDirection: 'row', flexWrap: 'wrap', marginBottom: 10 },
  btn: { paddingHorizontal: 10, paddingVertical: 8, margin: 5, borderWidth: 1, borderColor: '#ccc' },
  btnDanger: { paddingHorizontal: 10, paddingVertical: 8, margin: 5, borderWidth: 1, borderColor: '#ccc' },
  btnText: { color: '#333', fontSize: 14 },
  logContainer: { flex: 1, backgroundColor: '#f5f5f5', padding: 10, borderWidth: 1, borderColor: '#eee' },
  logText: { color: '#333', fontSize: 13, marginBottom: 5 }
});

export default SQLiteDemo;
是否支持 autolink RN 框架版本
~6.2.0 0.82/0.84

使用 AutoLink 的工程需依据该文档完成配置,Autolink 框架指导文档:https://gitcode.com/CPF-RN/ohos_react_native/blob/master/docs/zh-cn/Autolinking.md

若当前版本支持 Autolink,且工程已接入 Autolink,则可跳过 ManualLink 配置。

ManualLink:本步骤为手动配置原生依赖项的指导

首先,请使用 DevEco Studio 打开项目中的 HarmonyOS 工程 harmony

1. Overrides RN SDK

为使工程依赖统一版本的 RN SDK,需在工程根目录的 oh-package.json5 中添加 overrides 字段,并指向工程要使用的 RN SDK 版本。替换版本可以是具体版本号,也可以是模糊版本,还可以是本地已存在的 HAR 包或源码目录。

关于该字段的作用,请参阅官方说明

{
  "overrides": {
    "@rnoh/react-native-openharmony": "file:../react_native_openharmony"
  }
}

2. 引入原生端代码

当前有两种方式:

  1. 通过 har 包引入(待 IDE 完善相关功能后,此方式将被弃用,当前首选该方式);
  2. 直接引用源码。

方式一:通过 har 包引入(推荐)

har 包位于三方库安装路径下的 `harmony` 文件夹中。

打开 entry/oh-package.json5,添加以下依赖

"dependencies": {
    "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
    # 0.82/0.84
    "@react-native-ohos/react-native-sqlite-storage": "file:../../node_modules/@react-native-ohos/react-native-sqlite-storage/harmony/sqlite_storage.har"
  }

点击右上角的 sync 按钮

或在终端中执行:

cd entry
ohpm install

方法二:直接链接源码

如需使用直接链接源码,请参考[直接链接源码说明](https://gitcode.com/CPF-RN/usage-docs/blob/master/zh-cn/link-source-code.md)

3. 配置 CMakeLists 并引入 SQLitePluginPackage

打开 entry/src/main/cpp/CMakeLists.txt,添加:

...

project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")

add_subdirectory("${RNOH_CPP_DIR}" ./rn)

# RNOH_END: manual_package_linking_1
add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package)
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-sqlite-storage/src/main/cpp" ./sqlite_storage)
# RNOH_END: manual_package_linking_1

add_library(rnoh_app SHARED
    "./PackageProvider.cpp"
    "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)

target_link_libraries(rnoh_app PUBLIC rnoh)

# RNOH_BEGIN: manual_package_linking_2
target_link_libraries(rnoh_app PUBLIC rnoh_sample_package)
+ target_link_libraries(rnoh_app PUBLIC rnoh_sqlite_storage)
# RNOH_BEGIN: manual_package_linking_2

打开 entry/src/main/cpp/PackageProvider.cpp,添加:

#include "RNOH/PackageProvider.h"
+ #include "SqliteStoragePackage.h"

using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
+        std::make_shared<SqliteStoragePackage>(ctx)
}

4. 在 ArkTs 侧引入 SQLitePluginPackage

打开 entry/src/main/ets/RNPackagesFactory.ts,添加:

  ...
+ import {SQLitePluginPackage} from '@react-native-ohos/react-native-sqlite-storage/ts';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new SamplePackage(ctx),
+   new SQLitePluginPackage(ctx)
  ];
}

运行

点击右上角的 sync 按钮

或者在终端执行:

cd entry
ohpm install

然后编译、运行即可。

约束与限制

兼容性

本文档内容已基于以下版本验证通过:

  1. RNOH: 0.82.22; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.878; ROM:6.0.0.130
  2. RNOH: 0.84.2; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.878; ROM:6.0.0.130

平台差异说明

  • 语法错误的 SQL 不报错:底层 relationalStore.querySql 遇到语法错误的 SQL(如表名/列名拼写错误、无效语句)时,不会抛出异常,而是返回 0 列的空结果集,并按成功处理(Android 的 rawQuery 会抛出 SQLiteException)。因此,executeSql/transaction 对这类 SQL 会正常 resolve(查询结果为空),事务也不会中止。由于“合法但无结果集”的语句(PRAGMA/DETACH 等)与语法错误在该层面无法区分,本库不做弥补。业务侧需自行保证 SQL 正确性,调试期可开启 SQLite.DEBUG 查看原生执行日志。

  • SQLCipher 加密不支持(key 参数被忽略):鸿蒙端基于 @ohos.data.relationalStore 实现,未内置 SQLCipher。openDatabase 传入的 key 参数在鸿蒙端会被静默忽略,数据库将以非加密(明文)方式落盘。如需启用 SQLCipher,需自行编译带加密能力的 SQLite 内核并绕过 relationalStore,这属于大型原生改造,超出三方库适配范围,因此明确标注“不支持”。(注:SQLCipher 本身是跨平台的 SQLite 加密扩展,并非 iOS 特有;但在本库范围内,Android 桥接层不读取 key,也从未支持加密;iOS 端 key 仅在自行编译 SQLCipher 内核并定义 SQLCIPHER 宏时才生效——上游默认安装下三端行为一致,均会忽略 key。真实差异仅存在于“iOS 自备 SQLCipher 内核”的迁移场景。)

    Warning

    若原应用依赖 SQLCipher 加密(在本库范围内,仅 iOS 自备加密内核场景可启用),迁移到鸿蒙后 key 会被忽略,加密将退化为明文,请勿假设数据仍受加密保护,数据保护请另行处理。鸿蒙 relationalStore 自带库级加密能力(encrypt/cryptoParam,AES_256_GCM/CBC),但算法并非 SQLCipher,密文与 iOS/Android 的 SQLCipher 库文件不互通,且本库未接入该能力。

rawfile 资源放置路径

预填充数据库文件需放置在 entry/src/main/resources/rawfile/rdb/ 目录下。源码中使用 getRawFdSync('rdb/' + dbName) 读取此路径。

API

“平台”列表示该属性在原三方库上支持的平台。

“HarmonyOS 支持”列的 yes 表示 HarmonyOS 平台支持该属性;no 表示不支持;partially 表示部分支持。跨平台使用方法一致,效果对标 iOS 或 Android 的效果。

名称 说明 类型 必填 平台 HarmonyOS 支持
openDatabase 打开或初始化。当参数 createFromLocation 设为 1 时,会从 rawfile/rdb 将现成的 SQLite 数据库文件导入项目。readOnly 选项在 API 12+ 支持;key 加密参数不支持(会被静默忽略,数据库明文存储,详见“平台差异说明”) SQLitePlugin yes All partially(readOnly 需 API 12+)
transaction 执行 SQL 事务 void yes All yes
readTransaction 执行只读事务 void yes All yes
executeSql 在后台执行一批 SQL 语句,不阻塞当前线程或进程 void yes All yes
executeSqlBatch 批量执行 SQL 语句 void yes All yes
sqlBatch 批量执行 SQL 语句(Promise 模式) void yes All yes
close 关闭数据库连接并释放资源。API 12+ 真正关闭数据库并清理引用;API 11 及以下 close() 调用失败,经 error 回调上报 void yes All yes
deleteDatabase 删除数据库 void yes All yes
attach 连接或关联外部数据库 void yes All yes
detachDatabase 断开关联的外部数据库 void yes All yes
echoTest 测试原生模块通信 void yes All yes
enablePromise 启用/禁用 Promise 模式 void yes All yes
DEBUG 开启/关闭调试模式 void yes All yes
sqliteFeatures 获取 SQLite 特性 void yes All yes

遗留问题

  1. BLOB 数据类型返回 Uint8Array 而非 Base64 字符串,与上游行为不一致(平台 API 差异,不可修改)
  2. rowsAffected 在 CREATE/DROP/INSERT 语句中硬编码为 1,与上游 SQLite 标准行为不一致(修改会引入不兼容变更)

其它

目录结构

/rntpc_react-native-sqlite-storage  # 项目根目录
├── harmony                         # 鸿蒙适配代码
│   ├── sqlite_storage.har          # har 包
│   └── sqlite_storage              # 鸿蒙适配核心代码
│       ├── index.ets               # 鸿蒙适配代码入口
│       ├── ts.ets                  # Package / TurboModule 导出
│       ├── src/main/ets
│       │   ├── SQLitePluginPackage.ets      # RNOH Package 注册
│       │   ├── SQLitePluginTurboModule.ts   # 实现 TurboModule 接口
│       │   ├── CommonConstants.ts           # 常量
│       │   ├── Logger.ts                    # 日志
│       │   └── generated/                   # codegen 生成代码
│       └── src/main/cpp
│           ├── SqliteStoragePackage.h       # C++ Package
│           ├── CMakeLists.txt
│           └── generated/                   # codegen 生成代码
├── lib
│   └── sqlite.core.js              # SQLite JS 核心实现
├── sqlite.js                       # RN 入口(Promise/Callback 运行时封装)
├── NativeSQLitePlugin.ts           # TurboModule 类型 / Spec
├── README.md                       # 中文安装使用方法
└── README.OpenSource               # 开源说明

代码贡献

在使用过程中如发现任何问题,欢迎提交 Issue,同时也非常欢迎提交 PR

开源协议

本项目基于 The MIT License (MIT) ,欢迎自由使用并参与开源共建。

项目介绍

基于 react-native-sqlite-storage 的 OpenHarmony 适配版,SQLite 数据库存储

定制我的领域