基于鸿蒙(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 应用市场搜索下载,无需自行从零构建底层驱动。

  1. 线程调度 (Critical):鸿蒙规定 UI 操作必须在主线程执行。无论是通过 HTTP 请求还是本地驱动连接,数据库查询均属于耗时操作,必须在子线程(如 Worker)中完成,随后通过 UITaskDispatcher 或状态管理切换到主线程更新 UI,否则会导致应用无响应(ANR)。
  2. 网络安全红线
    • 推荐方案:采用 API 中间件模式,数据库不直接暴露在公网。
    • 直连方案(不推荐):若必须直连,远程数据库必须开启 IP 白名单,仅允许 App 所在设备的 IP 访问。严禁在客户端代码中硬编码数据库用户名和密码,应通过安全的配置文件或系统加密存储读取。
  3. 异常与超时处理:移动网络环境复杂,需妥善处理网络断开、连接超时、SQL 语法错误等异常。设置合理的 connectTimeout 和 readTimeout,并通过 Toast 等方式友好地提示用户,提升 App 稳定性。
  4. 驱动兼容性:不同数据库的 JDBC 驱动类和 URL 格式不同(如 MySQL 使用 com.mysql.cj.jdbc.Driver),在封装底层工具类时需根据实际数据库类型动态调整。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐