数据库管理工具:连接远程MySQL/PostgreSQL数据库(94)
基于鸿蒙(HarmonyOS)生态开发远程数据库管理工具,核心在于平衡功能强大与系统安全。以下是两种主流技术路线的完整架构与代码实现,分别面向通用应用和 PC 专业工具。
一、 应用层安全架构:基于 HTTP API 中间件(推荐)
此方案遵循“客户端-服务器”模式,鸿蒙客户端不直接连接数据库,而是通过安全的 HTTPS API 与后端中间件通信。这是最安全、最符合现代应用架构的实践。
1. 权限与网络配置
首先,在 module.json5 中声明网络访问权限,这是应用连接外部服务器的基础。
// module.json5
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "应用需要访问远程数据接口以管理数据库"
}
]
}
}
2. 核心数据访问服务类 (ArkTS)
封装一个 DatabaseService 类,用于处理所有与后端 API 的 HTTP 通信。此类应运行在独立的 Worker 线程中,以避免阻塞 UI。
// model/DatabaseService.ets
import http from '@ohos.net.http';
import { BusinessError } from '@kit.BasicServicesKit';
// 定义请求数据结构
interface QueryRequest {
query: string;
parameters?: any[];
}
// 定义响应数据结构
interface QueryResponse {
code: number;
message: string;
data: any[];
}
export class DatabaseService {
private client: http.HttpRequest;
private apiBase: string = 'https://api.yourserver.com'; // 你的后端API地址
constructor() {
this.client = http.createHttp();
}
/**
* 执行远程SQL查询
* @param sqlTemplate SQL语句模板
* @param params 参数化查询的参数数组,防止SQL注入
* @returns 查询结果数组
*/
async executeQuery(sqlTemplate: string, params: any[] = []): Promise<any[]> {
const url = `${this.apiBase}/api/data/query`;
const requestData: QueryRequest = {
query: sqlTemplate,
parameters: params
};
const options: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json',
// 可在此处添加认证Token,如 'Authorization': `Bearer ${token}`
},
extraData: JSON.stringify(requestData),
connectTimeout: 15000, // 连接超时15秒
readTimeout: 15000, // 读取超时15秒
};
try {
const result = await this.client.request(url, options);
if (result.responseCode === 200) {
const responseData: QueryResponse = JSON.parse(result.result as string);
if (responseData.code === 0) { // 假设0为业务成功码
return responseData.data;
} else {
throw new Error(`业务错误: ${responseData.message}`);
}
} else {
throw new Error(`HTTP错误: ${result.responseCode}`);
}
} catch (err) {
const error = err as BusinessError;
console.error('数据库查询请求失败:', error.code, error.message);
throw error; // 抛出错误,由调用方处理UI提示
}
}
/**
* 销毁HTTP客户端,释放资源
*/
destroy() {
this.client.destroy();
}
}
3. 在 Worker 线程中调用服务
为避免耗时操作阻塞主线程,数据库查询应在 Worker 中执行。
// workers/DbWorker.ets
import worker from '@kit.ArkTS';
import { DatabaseService } from '../model/DatabaseService';
const dbService = new DatabaseService();
const workerPort = worker.workerPort;
// 监听主线程消息
workerPort.onmessage = async function(e) {
const { id, sql, params } = e.data; // id用于关联请求和响应
try {
const data = await dbService.executeQuery(sql, params);
// 查询成功,将结果发回主线程
workerPort.postMessage({ id, success: true, data });
} catch (error) {
// 查询失败,将错误信息发回主线程
workerPort.postMessage({ id, success: false, error: (error as Error).message });
}
}
4. 在 UI 组件中使用
在 ArkUI 组件中创建 Worker 并与之通信,更新 UI。
// pages/Index.ets
import { worker } from '@kit.ArkTS';
@Entry
@Component
struct Index {
@State queryResult: any[] = [];
@State isLoading: boolean = false;
private dbWorker: worker.ThreadWorker | null = null;
aboutToAppear() {
// 页面出现时创建Worker
this.dbWorker = new worker.ThreadWorker('workers/DbWorker.ets');
this.dbWorker.onmessage = (e) => {
this.isLoading = false;
const { id, success, data, error } = e.data;
if (success) {
this.queryResult = data;
} else {
// 使用promptAction.showToast提示用户
console.error('查询失败:', error);
}
};
}
aboutToDisappear() {
// 页面销毁时终止Worker
if (this.dbWorker) {
this.dbWorker.terminate();
}
}
build() {
Column() {
if (this.isLoading) {
LoadingProgress().width(50)
}
List(this.queryResult) {
// ...渲染列表
}
Button('查询数据')
.onClick(() => {
this.isLoading = true;
// 向Worker发送查询任务
this.dbWorker?.postMessage({
id: 1,
sql: 'SELECT * FROM users LIMIT 10',
params: []
});
})
}
}
}
二、 PC端特化架构:基于 Electron 的桌面数据库工具
此方案适用于功能复杂的专业桌面工具,利用 Electron 在鸿蒙 PC 上运行 Node.js 环境,可直接使用成熟的数据库驱动。
1. 架构运行链路
- Electron 主进程 (main.js):负责创建窗口、管理生命周期,并作为 Node.js 运行时,加载
mysql2或pg等原生驱动。 - 渲染进程 (Renderer):使用 React/Vue 等框架构建 UI,通过 Electron 的
ipcRenderer与主进程通信。 - 预加载脚本 (preload.js):作为安全桥梁,向渲染进程暴露特定的 API。
2. 主进程代码 (Node.js)
主进程负责建立真实的数据库连接。
// main.js (Electron Main Process)
const { app, BrowserWindow, ipcMain } = require('electron')
// 以 PostgreSQL 为例,使用 pg 库
const { Client } = require('pg')
let mainWindow
function createWindow () {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js') // 预加载脚本
}
})
mainWindow.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
})
// 监听来自渲染进程的数据库查询请求
ipcMain.handle('db-query', async (event, config, sql) => {
const client = new Client({
host: config.host,
port: config.port,
user: config.user,
password: config.password,
database: config.database,
})
try {
await client.connect()
const res = await client.query(sql)
return { success: true, data: res.rows }
} catch (err) {
console.error(err)
return { success: false, error: err.message }
} finally {
await client.end()
}
})
3. 预加载脚本 (安全桥梁)
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('dbAPI', {
query: (config, sql) => ipcRenderer.invoke('db-query', config, sql)
})
4. 渲染进程代码 (前端)
前端通过暴露的 window.dbAPI 调用数据库功能。
// renderer.ts (在渲染进程中)
async function fetchData() {
const config = {
host: 'your-db-host',
port: 5432,
user: 'your-user',
password: 'your-password',
database: 'your-db'
};
const sql = 'SELECT * FROM users LIMIT 10';
const result = await (window as any).dbAPI.query(config, sql);
if (result.success) {
console.log('查询结果:', result.data);
// 更新UI
} else {
console.error('查询失败:', result.error);
}
}
三、 本地数据持久化:基于 RelationalStore 的缓存与状态管理
当应用处于弱网或离线状态时,远程 API 请求会失败。标准的做法是利用鸿蒙内置的关系型数据库(RelationalStore)作为本地缓存,将远程拉取的数据落盘,实现“离线可用”的体验。
核心代码示例(ArkTS):
import { relationalStore } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
export class LocalCacheManager {
private rdbStore: relationalStore.RdbStore | undefined = undefined;
// 1. 初始化本地数据库
async init(context: common.UIAbilityContext) {
const config: relationalStore.StoreConfig = {
name: 'db_cache.db',
securityLevel: relationalStore.SecurityLevel.S1
};
this.rdbStore = await relationalStore.getRdbStore(context, config);
// 创建本地缓存表
await this.rdbStore.executeSql(`
CREATE TABLE IF NOT EXISTS USER_CACHE (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
USER_ID INTEGER,
DATA TEXT,
SYNC_TIME INTEGER
)
`);
}
// 2. 将远程拉取的数据写入本地缓存
async saveToCache(userId: number, jsonData: string) {
const bucket: relationalStore.ValuesBucket = {
USER_ID: userId,
DATA: jsonData,
SYNC_TIME: Date.now()
};
await this.rdbStore?.insert('USER_CACHE', bucket);
}
// 3. 离线状态下读取本地数据
async getFromCache(userId: number): Promise<string | null> {
const predicates = new relationalStore.RdbPredicates('USER_CACHE');
predicates.equalTo('USER_ID', userId).orderByDesc('SYNC_TIME').limitAs(1);
const resultSet = await this.rdbStore?.query(predicates);
if (resultSet && resultSet.goToNextRow()) {
const data = resultSet.getString(resultSet.getColumnIndex('DATA'));
resultSet.close(); // ️ 关键:用完必须关闭 ResultSet 防止连接池耗尽
return data;
}
resultSet?.close();
return null;
}
}
四、 数据库版本升级:平滑迁移与防数据丢失
随着业务迭代,本地缓存表或应用内置数据库的结构必然发生变化(如新增字段)。必须通过版本号控制机制执行增量迁移 SQL,防止用户升级应用后丢失历史数据。
核心代码示例(ArkTS):
async function upgradeDatabase(store: relationalStore.RdbStore): Promise<void> {
const currentVersion = store.version;
// 从 v1 升级到 v2:新增 TAG 字段
if (currentVersion < 2) {
await store.executeSql('ALTER TABLE USER_CACHE ADD COLUMN TAG TEXT DEFAULT \'\'');
store.version = 2;
}
// 从 v2 升级到 v3:为高频查询字段添加索引
if (currentVersion < 3) {
await store.executeSql('CREATE INDEX IF NOT EXISTS idx_user_id ON USER_CACHE(USER_ID)');
store.version = 3;
}
}
五、 跨设备一致性:分布式数据同步
鸿蒙的核心优势在于分布式软总线。对于多端协同的数据库管理工具,可利用 SyncConfig 将本机的数据增量推送到用户的平板或 PC 端,实现无缝接续。
核心代码示例(ArkTS):
import { relationalStore } from '@kit.ArkData';
async function syncDataToOtherDevices(store: relationalStore.RdbStore, targetDeviceId: string) {
const syncConfig: relationalStore.SyncConfig = {
mode: relationalStore.SyncMode.SYNC_MODE_PUSH, // 采用推送模式
devices: [targetDeviceId] // 指定目标设备ID
};
try {
await store.sync(syncConfig);
console.info('跨设备数据同步成功');
} catch (err) {
console.error(`同步失败: ${(err as BusinessError).message}`);
}
}
六、 数据安全与合规:加密存储与手动备份
对于包含敏感信息的数据库工具,除了网络传输使用 HTTPS,本地落盘的数据也必须加密。同时,需提供数据库备份能力以防沙箱数据意外损坏。
核心代码示例(ArkTS):
export class SecureDbManager {
// 1. 动态更改数据库加密密钥
async changeEncryptionKey(store: relationalStore.RdbStore, newKey: Uint8Array) {
try {
await store.changeEncryptKey(newKey);
console.info('数据库加密密钥已更新');
} catch (err) {
console.error('更改密钥失败:', (err as BusinessError).message);
}
}
// 2. 将数据库安全备份到应用沙箱的指定目录
async backupDatabase(store: relationalStore.RdbStore, backupPath: string) {
try {
await store.backup(backupPath);
console.info(`数据库已成功备份至: ${backupPath}`);
} catch (err) {
console.error('数据库备份失败:', (err as BusinessError).message);
}
}
}
七、 现成商业工具:Navicat Premium Lite 鸿蒙化
对于希望直接使用成熟工具的用户,Navicat Premium Lite 已完成鸿蒙生态的初步适配,是连接和管理多种数据库的便捷选择。
- 多库支持:全面兼容 MySQL、PostgreSQL、SQL Server、SQLite、MariaDB 以及国产的 OceanBase、TiDB、PolarDB 等。
- 安全连接:支持通过 SSH 隧道和 SSL/TLS 双重加密保障远程访问安全。
- 跨端协同:支持连接配置、查询脚本通过 Navicat Cloud 跨设备实时同步。
开发者可直接在鸿蒙 PC 应用市场搜索下载,无需自行从零构建底层驱动。
- 线程调度 (Critical):鸿蒙规定 UI 操作必须在主线程执行。无论是通过 HTTP 请求还是本地驱动连接,数据库查询均属于耗时操作,必须在子线程(如 Worker)中完成,随后通过
UITaskDispatcher或状态管理切换到主线程更新 UI,否则会导致应用无响应(ANR)。 - 网络安全红线:
- 推荐方案:采用 API 中间件模式,数据库不直接暴露在公网。
- 直连方案(不推荐):若必须直连,远程数据库必须开启 IP 白名单,仅允许 App 所在设备的 IP 访问。严禁在客户端代码中硬编码数据库用户名和密码,应通过安全的配置文件或系统加密存储读取。
- 异常与超时处理:移动网络环境复杂,需妥善处理网络断开、连接超时、SQL 语法错误等异常。设置合理的
connectTimeout和readTimeout,并通过 Toast 等方式友好地提示用户,提升 App 稳定性。 - 驱动兼容性:不同数据库的 JDBC 驱动类和 URL 格式不同(如 MySQL 使用
com.mysql.cj.jdbc.Driver),在封装底层工具类时需根据实际数据库类型动态调整。
更多推荐



所有评论(0)