基于 react-native-sqlite-storage 的 OpenHarmony 适配版,SQLite 数据库存储
模板版本: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;
Link
| 是否支持 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. 引入原生端代码
当前有两种方式:
- 通过 har 包引入(待 IDE 完善相关功能后,此方式将被弃用,当前首选该方式);
- 直接引用源码。
方式一:通过 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
然后编译、运行即可。
约束与限制
兼容性
本文档内容已基于以下版本验证通过:
- RNOH: 0.82.22; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.878; ROM:6.0.0.130
- 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 |
遗留问题
- BLOB 数据类型返回
Uint8Array而非 Base64 字符串,与上游行为不一致(平台 API 差异,不可修改) 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) ,欢迎自由使用并参与开源共建。