海蒂数据库管理工具-鸿蒙PC上架介绍
海蒂数据库管理工具:在鸿蒙 PC 上原生打造的桌面级数据库客户端
欢迎加入开源鸿蒙 PC 社区:https://harmonypc.csdn.net/
欢迎在 PC 社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
开源地址(AtomGit 仓库):https://atomgit.com/OpenHarmonyPCDeveloper/ohos_heidisql
数据库客户端是开发者与运维每天都在用、却总被忽视的基础设施。本文分享「海蒂数据库管理工具」(原 HeidiSQL 鸿蒙版):它在鸿蒙 PC 上用
ArkTS + ArkUI原生重写了一个桌面级数据库客户端,并把数据库协议层交给交叉编译到 OHOS ARM64 的 C 客户端库。文章侧重技术实现——架构怎么分层、NAPI 桥怎么搭、C 库在 musl 工具链下怎么编,以及 ArkTS 领域层几个关键模块的设计取舍。

一、项目定位:原生重写,不是「移植套壳」
上游 HeidiSQL 是 Windows / Delphi 技术栈,在鸿蒙上没有任何直接复用的可能:没有 Delphi 运行时、没有 VCL、可执行文件格式也不一样。所以这里没有走「翻译源码」这条路,而是做了一个清醒的取舍——以上游功能规格为参照,用 ArkTS + ArkUI 全新原生实现界面与领域逻辑,只把真正绕不开的「数据库客户端协议」这层用 C 库解决。上游代码、文案与资源零复制,许可证沿用 GPL-2.0。
有一点需要明确:这不是「把某个 Linux 程序塞进壳里跑」。它跟 HNP 类移植不一样,是一个纯原生 GUI 应用——界面、事件循环、状态管理全部是 ArkUI 声明式范式,数据库连接是 HAP 内的 native 库,不存在独立进程、不存在 HNP、不存在终端命令。所有东西打成一个 HAP,装上就能用。
项目的四个目标:
- 平台补位:为鸿蒙 PC 提供一款成熟、开源、免费的图形化数据库管理工具;
- 原生体验:全部界面用 ArkUI 声明式范式重写,适配键鼠与高分屏,同时保持触控可用;
- 对齐上游核心流程:覆盖「连接管理 → 数据库浏览 → 数据查看 → SQL 查询 → 数据编辑 → 结构编辑 → 数据导出」全流程;
- 可持续协作:以规格驱动开发(SDD)沉淀设计与验收标准,让社区贡献有据可依。
| 层 | 技术 |
|---|---|
| UI | ArkTS + ArkUI 声明式范式(State Management V1),ArkTS 严格模式 |
| 领域逻辑 | ArkTS:SQL 构建、事务编排、导入导出、目录服务 |
| 数据库桥接 | NAPI(C++):统一 provider 接口 + MySQL / PostgreSQL / SQLite 三个实现 |
| 数据库客户端 | 交叉编译至 HarmonyOS ARM64 的 C 库:libmariadb、libpq、sqlite3;TLS 依赖 OpenSSL |
| 构建 | DevEco Studio + hvigor(API 12);native 层 CMake + OHOS LLVM 工具链(musl) |
功能特性(主线,MySQL / MariaDB):
- 会话管理:新建 / 编辑 / 保存 / 删除 / 复制连接会话,连接前测试连接;密码经系统安全能力加密存储;
- 数据库树:实例 → 数据库 → 表 / 视图分层导航,右键快捷操作;
- 数据网格:浏览 / 编辑双模式,列宽拖拽、快速过滤;行内编辑 + 批量事务提交(提交 / 回滚),脏数据守卫;新增行自动列裁剪;无主键表编辑风险告警;BLOB / TEXT 大字段专用编辑器(文本 / 十六进制切换);
- SQL 编辑器:语法高亮、SQL 执行与结果面板、查询历史;
- 结构管理:字段编辑、索引与外键 DDL 编辑、DDL 查看、字段类型映射;
- 对象与运维:数据库 / 表对象编辑器、用户管理、偏好设置;
- 数据导入导出:SQL / CSV 导出、CSV 导入。
二、架构分层与工程结构
整个系统的分层很干净:ArkTS 管一切看得见的,C 库只管协议,NAPI 做桥。
┌──────────────────────────────────────────────┐
│ pages/ ArkUI 页面(会话管理/工作台…) │
├──────────────────────────────────────────────┤
│ viewmodel/ SQL 构建、事务编排、导入导出 │
│ catalog/ 数据库树目录服务 │
│ editor/ SQL 编辑器与语法高亮 │
│ model/ 会话、连接参数、安全存储 │
├──────────────────────────────────────────────┤
│ dbclient/ 连接注册表(ArkTS 侧) │
├──────────────────────────────────────────────┤
│ cpp/ NAPI 桥接:db_provider 统一接口 │
│ ├ mysql_provider(libmariadb) │
│ ├ postgres_provider(libpq) │
│ └ sqlite_provider(sqlite3) │
├──────────────────────────────────────────────┤
│ native/ 交叉编译 ARM64 .so(musl 兼容)│
└──────────────────────────────────────────────┘
对应的工程目录:
ohos_HeidiSQL/ # 工程根目录(DevEco Studio 打开这个)
├── build-profile.json5 # 工程级构建配置(SDK 版本 / 签名配置)
├── AppScope/ # 应用级配置(bundleName / 图标 / 显示名)
├── entry/ # 主模块(整个应用就是一个 HAP)
│ └── src/main/
│ ├── module.json5 # 设备类型(含 2in1)、INTERNET 权限、Ability 声明
│ ├── cpp/ # NAPI 桥接层(C++,编成 libdbclient.so)
│ │ ├── db_provider.h / .cpp # 数据库 provider 统一接口(连接/查询/转义…)
│ │ ├── mysql_provider.cpp # MySQL / MariaDB 实现(真实驱动 libmariadb)
│ │ ├── sqlite_provider.cpp # SQLite 实现(桩,待恢复完整实现)
│ │ ├── postgres_provider.cpp # PostgreSQL 实现(桩,待恢复完整实现)
│ │ └── napi_init.cpp # NAPI 模块注册(ArkTS ↔ C++ 全部入口)
│ └── ets/
│ ├── entryability/ # EntryAbility(PC 窗口尺寸适配就在这)
│ ├── pages/ # 20 个 ArkUI 页面(工作台/数据网格/SQL 编辑器…)
│ ├── viewmodel/ # SQL 构建、行编辑缓冲、导入导出等领域逻辑
│ ├── catalog/ # 数据库树目录服务
│ ├── editor/ # SQL 语法高亮与关键字表
│ ├── model/ # 会话模型 / HUKS 加密存储 / 偏好设置
│ └── dbclient/ # 连接注册表(ArkTS 侧持有 native 连接句柄)
└── native/ # C 库交叉编译配套(跟 HAP 构建解耦)
├── build/
│ ├── env.sh # OHOS 工具链环境变量(clang / sysroot / lld)
│ └── build_libs.sh # 一键交叉编译 6 个 C 库的主脚本
├── compat/
│ └── musl_compat.h # glibc 扩展符号在 musl 下的兼容桩
└── prebuilt/arm64-v8a/ # 交叉编译产物(.so + 头文件)
这条路线最大的好处是双向隔离:数据库客户端库以预编译 .so 随 HAP 分发,运行时靠 -Wl,-rpath,$ORIGIN 就近加载;领域逻辑全部在 ArkTS 层,换数据库只动 provider 一层,UI 一行不改;反过来 ArkTS 侧的演进也不触碰 C 库。
连接链路从前台一路贯通到 native 层:
EntryAbility(主窗口 1280×800)
└── MainWindowPage
├── SessionManagerPage # 会话管理
│ └── SecureSecretStore # HUKS 密码加密
└── WorkbenchPage # 工作台
└── DbConnection / ConnRegistry # ArkTS 连接注册表
└── NAPI: libdbclient.so
└── DbProvider 统一接口
├── mysql_provider → libmariadb
├── sqlite_provider → sqlite3(待集成)
└── postgres_provider → libpq(待集成)
三、跨语言桥接:NAPI 怎么把 C 库变成 ArkTS 可用的对象
3.1 统一 Provider 接口
C++ 侧的核心抽象是 DbProvider 纯虚接口,把三种数据库统一成同一套方法:
class DbProvider {
public:
virtual bool Connect(const std::map<std::string, std::string>& params, std::string& err) = 0;
virtual void Disconnect() = 0;
virtual bool Ping(std::string& err) = 0;
virtual std::string GetServerVersion() = 0;
virtual std::string GetServerInfo() = 0;
virtual std::string EscapeString(const std::string& s) = 0;
virtual ResultSet Query(const std::string& sql) = 0;
virtual bool IsConnected() = 0;
};
工厂按 kind 分发(mysql / mariadb / postgres / postgresql / sqlite / sqlite3),ArkTS 侧完全不关心底层是哪种库:
std::shared_ptr<DbProvider> CreateProvider(const std::string& kind) {
if (kind == "sqlite" || kind == "sqlite3") return CreateSqliteProvider();
if (kind == "mysql" || kind == "mariadb") return CreateMysqlProvider();
if (kind == "postgres" || kind == "postgresql") return CreatePostgresProvider();
return nullptr;
}
3.2 句柄路由:跨语言生命周期怎么管
NAPI 层用全局连接表 + 自增整型句柄把对象生命周期管起来。C++ 侧每个连接持有一个 std::shared_ptr<DbProvider>,只把一个 int32_t 句柄返回给 ArkTS,后续所有调用通过句柄路由,全局表用互斥锁保护:
std::mutex g_mutex;
std::map<int32_t, std::shared_ptr<heidi::DbProvider>> g_conns;
std::atomic<int32_t> g_nextHandle{1};
一个细节:RemoveProvider 会把 shared_ptr 先移出表、在锁外析构,避免 Provider 析构时回调上层再抢同一把锁造成死锁。
ArkTS 侧再用 DbConnection 把它包成面向对象的形式,上层业务不感知任何 NAPI 细节:
const conn = new DbConnection('mysql');
conn.connect({ host: '10.0.2.2', port: '3306', user: 'root', password: '***', database: 'test' });
const rs = conn.query('SELECT id, a FROM test.test;');
conn.close();
ConnRegistry 则在页面之间按 id 分发连接(registerConn / getConn / unregisterConn)。
3.3 ResultSet 归一化
ResultSet 的结构设计是这套桥接能"一次写、三库通用"的关键:所有列值统一以字符串承载,且额外带一份与行同形状的 NULL 标记数组:
struct ResultSet {
std::vector<Column> columns;
std::vector<std::vector<std::string>> rows;
std::vector<std::vector<bool>> nulls; // true 表示 NULL
int64_t affectedRows = 0;
int64_t insertId = 0;
bool ok = true;
std::string error;
std::string sqlState;
int32_t errorCode = 0;
};
好处是把类型转换问题后置到 ArkTS 侧按列类型自行解析,native 层不必为每个库维护不同的返回值结构;NAPI 出口时再把 nulls 为真的位置转成 JS 的 null。列元信息(dbTypeName / type / nullable / isPrimaryKey / maxLength)也一并带出,直接支撑上层的编辑与结构管理。
模块注册就是一张方法表,九个入口:
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"create", nullptr, M_Create, ...}, {"connect", ...}, {"disconnect", ...},
{"destroy", ...}, {"ping", ...}, {"getServerVersion", ...},
{"getServerInfo", ...}, {"escapeString", ...}, {"query", ...},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
四、协议层:6 个 C 库在 musl 工具链下的交叉编译
界面可以重写,数据库协议不必重造轮子。libmariadb / libpq / sqlite3 都是成熟 C 库,只要能交叉编译到 OHOS ARM64,协议层就交给它们。
4.1 工具链封装(native/build/env.sh)
所有构建脚本只从这一个文件取环境:SDK 自带 LLVM/Clang、--sysroot 指向 musl sysroot、链接器 ld.lld。其中两个变量必须并存,这是最容易踩的坑:
# autoconf 系工程的 --host 只认标准三元组
export TARGET_HOST="aarch64-linux-gnu"
# clang 的 --target 用 ohos 三元组
export TARGET_TRIPLE="aarch64-linux-ohos"
export TARGET_FLAGS="--target=$TARGET_TRIPLE --sysroot=$SYSROOT -D__linux__ -D__MUSL__ -fPIC"
export LDFLAGS="--target=$TARGET_TRIPLE --sysroot=$SYSROOT -fuse-ld=$LLVM_BIN/ld.lld"
两个自定义宏很关键:-D__linux__(musl 上部分头文件靠它才暴露 API)、-D__MUSL__(自己的桩与补丁全靠这个宏切换分支)。
4.2 版本锁定的 6 个库
脚本按「先依赖后主库」的顺序编译,版本全部写死,保证可复现:
| 顺序 | 库 | 版本 | 说明 |
|---|---|---|---|
| 1 | SQLite | 3.46.0 | 纯 C amalgamation,一条 clang 命令编成 .so(开 FTS5 / JSON1 / RTREE) |
| 2 | libiconv | 1.17 | libpq 的字符集依赖,手工 lld 链接(libcharset + libiconv 两个 .so) |
| 3 | libintl | gettext 0.22.5 | 只编 gettext-runtime 子集,libpq 依赖 |
| 4 | OpenSSL | 3.3.1 | libmariadb / libpq 的 TLS 依赖 |
| 5 | libpq | PostgreSQL 15.7 | 只编 src/interfaces/libpq,不编 server 端 |
| 6 | libmariadb | 3.3.3 | MySQL / MariaDB 客户端,认证插件全部静态链接 |
脚本本身是幂等的:源码包已存在则跳过下载、已解压则跳过解压、产物已存在则跳过编译,全流程可重复执行。
cd native/build
./build_libs.sh all # 按依赖顺序编译全部库
./build_libs.sh libmariadb # 也可单独编某一个
OHOS_SDK_PATH=/path/to/sdk ./build_libs.sh all # 指定 SDK 路径
4.3 musl 兼容桩(native/compat/musl_compat.h)
libmariadb / libpq / OpenSSL 这类库默认按 glibc 假设编译,交叉链接时会报一串 undefined symbol。逐个改库源码既脏又不可持续——每个库每个版本都要重新打补丁。最终解法是一个兼容桩头,通过 -include 强制注入到所有编译单元,对 glibc 专有符号提供空桩或等价实现:
| 符号 | 处理 |
|---|---|
backtrace / backtrace_symbols | musl 无 <execinfo.h>,libmariadb 崩溃诊断路径会引用,提供空桩 |
malloc_usable_size | glibc 扩展,用于内存统计,返回 0 |
program_invocation_name / program_invocation_short_name | glibc 全局变量,声明为外部符号 |
__libc_current_sigrtmin / sigrtmax | PostgreSQL 代码路径引用,返回常量 |
secure_getenv | musl 无,转发到 getenv |
PTHREAD_MUTEX_ADAPTIVE_NP / ROBUST_NP | 缺失宏,补 0 |
库源码零改动,升级库版本时不需要重新维护补丁集。
4.4 单库的特殊处理
OpenSSL 3.3.1:不走 autoconf,用自己的 Configure——平台选 linux-aarch64,shared no-tests no-asm 减少坑面;安装阶段要 unset CROSS_COMPILE,否则 make install 会拿交叉前缀去找本机不存在的工具。
libmariadb 3.3.3(坑最多的一个),三处处理:
CMAKE_SYSTEM_NAME=Linux必须显式指定,否则 CMake 在 macOS 上把它检测成 Darwin,交叉编译直接翻车;- 交叉环境下
FIND_PACKAGE(OpenSSL)内部的TRY_RUN无法执行,OPENSSL_FOUND拿不到值、TLS 支持会被静默裁掉——脚本用 sed 往它的 CMakeLists 注入一段OHOS_CROSS_COMPILE_HACK,跳过探测、直接注入头文件与库路径; - 认证插件全部静态链接(
CLIENT_PLUGIN_caching_sha2_password=STATIC等)。这是真机上最容易撞上的坑:MySQL 8+ 默认认证就是caching_sha2_password,插件若靠动态加载,运行时在应用沙箱里找不到插件文件,连接直接报错;GSSAPI 等单机用不到的直接 OFF。
libpq 15.7:--without-readline --without-zlib 砍掉不必要依赖、--with-openssl 接 TLS,只编客户端接口层;链接时需要 -Wl,-rpath-link,$PREFIX/lib 才能找到刚编出来的 libssl / libiconv。
4.5 组包与就近加载
NAPI 模块的 CMake 指向 native/prebuilt/arm64-v8a/,链接 sqlite3 / mariadb / ssl / crypto 加 SDK 的 ace_napi.z / hilog_ndk.z,并用 POST_BUILD 把运行期依赖的 .so(含 mariadb 插件目录)自动拷进 libdbclient.so 同目录:
target_link_options(dbclient PRIVATE "-Wl,-rpath,\$ORIGIN")
add_custom_command(TARGET dbclient POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PREBUILT_DIR}/lib" "$<TARGET_FILE_DIR:dbclient>"
...)
运行时靠 $ORIGIN 就近加载——HAP 自包含,设备上不需要任何预装库。
4.6 MySQL 8 认证失败的可读错误
真机连库失败时,最好别把 mysql_error 原样抛给用户。MysqlProvider::Connect 会识别错误特征并给出可操作的提示:
if (originalErr.find("caching_sha2_password") != std::string::npos ||
originalErr.find("Authentication plugin") != std::string::npos) {
err = "MySQL 8.0+ 使用 caching_sha2_password 认证失败。请确认:"
"1) libmariadb.so 已静态链接该插件(重新运行 build_libs.sh libmariadb);"
"2) OpenSSL 已正确链接。也可在服务器端执行:"
"ALTER USER '用户名'@'主机' IDENTIFIED WITH mysql_native_password BY '密码';";
}
五、ArkTS 领域层的几个关键设计
5.1 SQL 编辑器:自研词法高亮
编辑器没有依赖现成高亮库,而是写了一个词法扫描器 SqlHighlighter.ets:把 SQL 切成 token 序列,识别空白、单行 / 多行注释、字符串字面量、反引号 / 双引号标识符、数字(含十六进制与科学计数法)、关键字、数据类型、函数名(通过后跟 ( 判定)与标点,再按 token 类型生成带颜色的 HTML 交给 RichText 渲染。关键字字典按 SQL 标准 + 各库扩展关键字的子集维护,明暗双主题各有一套色板。
配套的 SqlExecutor 负责按 ; 拆多语句并逐条执行:拆分时会尊重字符串 / 注释 / 引号内的分号(词法级拆分,不做完整 SQL 解析),执行时统计每条语句的耗时、影响行数与错误,支持「首条失败即中止后续」;失败时带回 sqlState 与 errorCode,便于定位。
const { results, summary } = executor.execute(sql, /* abortOnError */ true);
// summary: { totalStatements, successCount, failedCount, totalElapsedMs, aborted }
5.2 数据网格:一条数据从服务端到屏幕
数据网格是数据库工具的体验核心,它的数据流由 DataGridLoader 管理,不是「SELECT * 一把梭」。一次刷新分三步:
- 探针查询拿列结构:首次进入用
LIMIT 0, 1探一次,拿到列名、类型、是否主键、最大长度; - 分页取数据:按
pageSize(默认 100)构造SELECT ... LIMIT n OFFSET m; - 单独统计总数:另发一条
SELECT COUNT(*)得到总行数,驱动分页控件。
同时接入了排序、过滤与长文本截断,SQL 全部在 ArkTS 侧拼接:
SELECT <列清单> FROM `db`.`table` WHERE <过滤条件> ORDER BY `col` DESC LIMIT 100 OFFSET 0
两个值得注意的细节:
- 长文本截断:对
TEXT/JSON/Enum/Set等文本类列,若maxLength超过阈值(1000)或未知,用LEFT(col, 1000)(SQLite 用SUBSTR)只取前 1000 字节,避免把超大字段整个拉回内存拖垮网格; - 过滤操作符:
eq / ne / like / notlike / gt / lt / gte / lte / isnull / isnotnull / raw,数值列走数字比较、文本列走转义后的引号比较,还支持直接写原生WHERE子句。
5.3 行编辑缓冲区:桌面级编辑的「脏数据」模型
数据网格支持行内编辑 + 批量事务提交 / 回滚,核心是 RowEditBuffer。它把用户在网格上的改动先暂存,点「提交」前不写库,点「回滚」丢弃全部待提交编辑:
export interface PendingEdit {
rowKey: string; // 行的唯一 key
op: 'update' | 'insert' | 'delete';
original: (string | null)[]; // 原始值(UPDATE/DELETE 用)
current: (string | null)[]; // 编辑后的值
changedColumns: Set<string>; // 被改过的列
}
几个关键决策:
- rowKey 生成:优先用主键列值拼接;无主键时退化为全部列值拼接——这也是「无主键表只能受限编辑」的由来;
- 值语义约定清晰:空串
''原样写缓冲,NULL只能通过显式的「设为 NULL」入口写入;数值列的空串在生成 SQL 时兜底按NULL处理; - INSERT 列裁剪:从未被触碰的列不出现在插入列清单里,自增列 / 有默认值的列交由数据库端生成,避免
INSERT时误写0或空串污染自增主键; - 提交顺序固定:
INSERT → UPDATE → DELETE,并用单个事务包起来(BEGIN → 逐条 → 失败 ROLLBACK → COMMIT),任一语句失败即整体回滚并把失败语句序号带回。
applyEditBuffer 里还有个小而实用的设计:对 update / delete 影响 0 行的语句,不当作失败,而是收集进 zeroRowWarnings 提示「目标行可能已被其他用户修改或删除」——本质上是一个轻量的并发冲突信号。

5.4 SQL 生成与三方方言适配
编辑缓冲最终由 SqlBuilder 变成 SQL 字符串,核心是按列类型格式化值 + 按方言引用标识符:
private formatValue(v: string | null, col: Column): string {
if (v === null) return 'NULL';
if (v === '' && (col.type === Integer || col.type === Real)) return 'NULL';
if (col.type === Integer || col.type === Real) {
if (/^-?\d+(\.\d+)?$/.test(v)) return v; // 合法数字不加引号
}
return "'" + this.conn.escapeString(v) + "'";
}
标识符引用按方言区分:MySQL / MariaDB 用反引号 `ident`,PostgreSQL / SQLite 用双引号 "ident"。这种差异在 AlterTableBuilder(结构管理的 DDL 生成)里体现得更明显:
| 能力 | MySQL / MariaDB | PostgreSQL | SQLite |
|---|---|---|---|
| MODIFY 列 | 一条 MODIFY COLUMN 整列重写 | 拆成多条 ALTER COLUMN TYPE / SET NOT NULL / SET DEFAULT / RENAME | 不支持,提示重建表 |
| 自增 | AUTO_INCREMENT | 用 SERIAL 类型表达 | 依赖 INTEGER PRIMARY KEY |
| DROP 列 | 直接 DROP COLUMN | 直接 DROP COLUMN | 3.35+ 才支持,老版本受限 |
方言差异集中在 SQL 构建层、UI 完全不感知——这也是后面接入 PostgreSQL / SQLite 时改动能收敛在 viewmodel 层的原因。

5.5 密码安全存储(HUKS)
SecureSecretStore 基于 @ohos.security.huks 实现,替代 Windows 上的 DPAPI 思路:
- 密钥由 HUKS 生成并持久化存储,密钥永不出 HUKS,加解密都在 HUKS 内部完成;
- 算法 AES-256-GCM,12 字节随机 IV + 16 字节认证标签;
- 密文按
Base64(IV + ciphertext + tag)拼接存储,每条密文独立; - 解密失败(密钥变更或数据被篡改)直接抛异常,不静默返回脏数据。
5.6 PC 交互适配
「让它像桌面应用、而不是大号手机应用」是重点投入的部分:
module.json5的deviceTypes显式声明2in1,随通用设备一起安装;EntryAbility.onWindowStageCreate里主窗口拉起即设为 1280×800,符合桌面工作区习惯;- 键鼠交互对齐桌面习惯:数据库树与表格的右键上下文菜单(
ContextMenuHelper)、数据网格列宽拖拽(ColResizeHandle)、多级树形导航; - 全程 ArkTS 严格模式(禁
any、禁对象字面量类型推断、禁动态属性访问),倒逼出 pages / viewmodel / model / dbclient / catalog 的清晰分层。
六、环境要求与构建步骤
| 项目 | 要求 | 备注 |
|---|---|---|
| IDE / SDK | DevEco Studio(含 HarmonyOS SDK) | 提供 OHOS LLVM/Clang、musl sysroot、hvigor、CMake |
| compatibleSdkVersion | 5.0.0(12) | 见 build-profile.json5 |
| targetSdkVersion | 6.1.1(24) | 实际编译目标版本 |
| 交叉编译工具 | cmake、make、curl、tar、xz、unzip | cmake 优先用 SDK 自带 |
| 目标设备 | 2in1(鸿蒙 PC)及通用设备 | deviceTypes = ["default", "2in1"] |
| 权限 | ohos.permission.INTERNET | 连接远程数据库必需,已在 module.json5 带用途说明声明 |
整个流程是「交叉编译 C 库 → 构建签名 HAP → 装到鸿蒙 PC」:
# 1. 交叉编译数据库客户端 C 库
cd native/build && ./build_libs.sh all
# 2. 构建签名 HAP(DevEco 里 Signing Configs 点 Fix 生成调试签名后 Run,或命令行)
hvigorw assembleHap
# 3. 安装并启动
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
产物落在 native/prebuilt/arm64-v8a/(.so + 头文件),native 依赖库全部随 HAP 打包,单文件即可分发安装。
七、当前能力状态(T0 / T1 / T2)
状态只有三种含义:已完成(代码在仓库里,按上文可复现构建)、桩 / 未集成(接口就位、依赖脚本备好,属排期问题)、暂不计划(对齐上游 Roadmap 的主动取舍)。
T0 基础能力(能不能编、能不能装、能不能起)—— 全部完成:交叉编译工具链封装完毕;SQLite / OpenSSL / libmariadb 交叉编译产物就绪;libpq / libiconv / libintl 脚本就绪;NAPI 统一桥接层与 HAP 构建通过;2in1 设备适配完成。
T1 主要能力(MySQL / MariaDB 主线)—— 已完成:会话管理、真实连接(native_password + caching_sha2_password 认证、可选 TLS)、数据库树导航、数据浏览与编辑、SQL 查询、结构管理、导入导出、对象与运维,全链路就位。
T2 待办与取舍:SQLite 与 PostgreSQL 两个后端为桩实现——libsqlite3.so 已交叉编译并链接进 libdbclient.so,libpq 构建脚本就绪,属于「恢复实现」的工作量,不存在架构性堵点;SSH 隧道与 MSSQL / Interbase / Firebird 暂不计划。
需要如实说明的边界:数据编辑的批量提交依赖目标表具备主键或唯一键,无主键表提供的是带风险告警的受限编辑。
八、开源与合规
- 本项目遵循 GPL-2.0,与上游 HeidiSQL 一致;
- 第三方组件及其许可证:
| 组件 | 用途 | 许可证 |
|---|---|---|
| libmariadb | MySQL / MariaDB 客户端 | LGPL-2.1(动态链接) |
| libpq | PostgreSQL 客户端 | PostgreSQL License |
| SQLite | SQLite 客户端 | Public Domain |
| OpenSSL | TLS | Apache-2.0 |
| libiconv / libintl | libpq 依赖 | LGPL |
- 关系声明:HeidiSQL 名称与上游项目版权归原权利人所有;本移植项目仅为功能规格层面的参照实现,未复制上游源码与资源。
- 开发流程采用规格驱动(SDD):以
spec/<feature>/组织spec.md(需求与验收标准)→plan.md(技术方案)→tasks.md(任务清单),让社区贡献有据可依。
九、总结
海蒂数据库管理工具把「原生重写」这条路走通了:界面与领域逻辑用 ArkTS / ArkUI 原生实现,协议层交叉编译成熟的 C 客户端库,NAPI 做桥,最终打成单一 HAP,装上即用——不存在独立进程或终端命令,是真正的原生桌面 GUI 应用。
回头看有三点经验值得同类项目借鉴:
- 能复用的协议层绝不重写:libmariadb 这类 C 库在 musl 工具链下的坑(三元组、
TRY_RUN、认证插件)虽多,但全部可以收敛进一个幂等脚本和一个兼容桩头,一次解决、次次复用; - 原生重写的成本大头在交互密度而非页面数量:数据网格一个组件的工作量接近全局三分之一,但这正是「桌面工具」与「手机套壳」的分水岭;
- 适配状态要按用户路径如实标注:能启动不等于能连接,界面出现不等于功能闭环——T0 / T1 / T2 的划分就是为了把「已验证的」与「待完成的」说清楚。
开源鸿蒙 PC 需要的正是这类日常基础设施软件。项目代码已托管在 AtomGit,无论是 SQLite / PostgreSQL 后端集成、数据网格性能打磨,还是新场景的适配反馈,都欢迎参与共建。
更多推荐



所有评论(0)