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

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

分支5Tags6
文件最后提交记录最后更新时间
6 天前
5 个月前
6 天前
1 个月前
1 个月前
6 天前
1 个月前
1 个月前
10 年前
1 个月前
3 个月前
1 年前
18 天前
18 天前
3 个月前
6 天前
1 年前

模板版本: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.1.2 Gitcode Releases 0.77 否 API12+ 6.0.1 br_rnoh0.77

简介

一个基于 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.1.2 否 0.77

使用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",
    "@react-native-ohos/react-native-sqlite-storage": "file:../../node_modules/@react-native-ohos/react-native-sqlite-storage/platforms/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.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;

平台差异说明

  • 语法错误的 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

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

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

Name Description Type Required Platform HarmonyOS Support
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 数据库存储

定制我的领域