欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

请添加图片描述

前言

虽然移动应用大多使用本地数据库(如 SQLite),但在某些特定场景,比如内部企业应用、数据看板,或者 Serverless 架构中,客户端直接连接远程数据库进行即时查询是非常便捷的。

postgres 是一个纯 Dart 实现的 PostgreSQL 驱动,拥有完整的协议支持,不需要任何原生库绑定(JNI/FFI)。这意味着它不仅能在服务器端(Dart VM)运行完美,同样也能在 Flutter 移动端及 Web 端流畅运行,当然也包括 OpenHarmony。

一、核心特性

  • 纯 Dart 实现:零原生依赖,全平台通用。
  • SSL/TLS 安全连接:保障数据传输安全。
  • 流式查询:支持大结果集的流式读取,避免 OOM。
  • 连接池:内置连接池管理,适应高并发场景。

通过 TCP/SSL 连接

PostgreSQL 协议交互

执行 SQL 语句

底层查询

原始结果集

映射后的 Dart 对象

OpenHarmony 应用

远程数据库服务器

内置连接池

数据库实例

Flutter 组件渲染

二、集成与配置

2.1 添加依赖

dependencies:
  postgres: ^3.5.9

2.2 连接配置 (SSLMode)

在公网环境连接数据库,强烈建议开启 SSL。

import 'package:postgres/postgres.dart';

final endpoint = Endpoint(
  host: 'db.example.com',
  port: 5432,
  database: 'my_db',
  username: 'user',
  password: 'password',
);

final connection = await Connection.open(
  endpoint,
  settings: ConnectionSettings(
    sslMode: SslMode.require, // 强制 SSL
  ),
);

在这里插入图片描述

三、核心操作与示例

3.1 示例一:执行简单查询

查询用户列表并将结果映射为 Dart 对象。

Future<void> queryUsers() async {
  // 假设已有 connection
  final result = await connection.execute('SELECT id, name FROM users WHERE active = @active', 
    parameters: {'active': true},
  );

  for (final row in result) {
    print('User: ${row[0]} - ${row[1]}');
  }
}

在这里插入图片描述

3.2 示例二:事务处理

转账操作必须在事务中完成。

Future<void> transferMoney(int fromId, int toId, double amount) async {
  await connection.runTx((session) async {
    // 1. 扣款
    await session.execute(
      'UPDATE accounts SET balance = balance - @amount WHERE id = @from',
      parameters: {'amount': amount, 'from': fromId},
    );

    // 2. 加款
    await session.execute(
      'UPDATE accounts SET balance = balance + @amount WHERE id = @to',
      parameters: {'amount': amount, 'to': toId},
    );
    
    // 如果抛异常,自动回滚
  });
}

在这里插入图片描述

3.3 示例三:监听数据库通知 (LISTEN/NOTIFY)

PostgreSQL 有个超酷的功能:发布订阅。App 可以实时收到数据库变更通知。

Future<void> listenChanges() async {
  // 建立专用连接用于监听
  final conn = await Connection.open(endpoint);
  
  // 订阅频道 'user_updates'
  await conn.execute("LISTEN user_updates");
  
  // 监听通知流
  conn.channels['user_updates']?.listen((payload) {
    print('收到数据库通知: $payload');
    // 刷新 UI
  });
}

在这里插入图片描述

四、OpenHarmony 平台适配

4.1 网络权限

连接远程数据库必须声明网络权限。

"requestPermissions": [
  { "name": "ohos.permission.INTERNET" }
]

4.2 安全与证书

如果在内网且自签名证书,需要在连接时添加受信任的根证书。postgres 库允许通过 SecurityContext 自定义证书验证逻辑。

五、完整实战示例:实时数据看板

本示例将连接一个远程 PostgreSQL 数据库,并展示实时更新的销售数据。每当有新订单插入时(通过 LISTEN/NOTIFY),看板自动刷新。

5.1 示例代码

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:postgres/postgres.dart';

void main() {
  runApp(const MaterialApp(home: DashboardPage()));
}

class DashboardPage extends StatefulWidget {
  const DashboardPage({super.key});

  
  State<DashboardPage> createState() => _DashboardPageState();
}

class _DashboardPageState extends State<DashboardPage> {
  Connection? _conn;
  List<Map<String, dynamic>> _salesData = [];
  bool _connecting = true;
  String _error = '';

  
  void initState() {
    super.initState();
    _connectDB();
  }

  Future<void> _connectDB() async {
    try {
       // ⚠️ 真实项目中不要硬编码密码!应通过安全方式获取
      final endpoint = Endpoint(
        host: '192.168.1.100', // 替换为你的服务器 IP
        port: 5432,
        database: 'sales_db',
        username: 'flutter_app',
        password: 'secure_password',
      );

      _conn = await Connection.open(endpoint, settings: ConnectionSettings(sslMode: SslMode.disable));
      
      // 订阅实时更新
      await _conn!.execute("LISTEN new_sale");
      _conn!.channels['new_sale']?.listen((_) => _refreshData());

      await _refreshData(); // 初始加载

      setState(() {
        _connecting = false;
        _error = '';
      });
    } catch (e) {
      setState(() {
        _connecting = false;
        _error = '连接失败: $e';
      });
    }
  }

  Future<void> _refreshData() async {
    if (_conn == null) return;
    try {
      final result = await _conn!.execute('SELECT product, amount, created_at FROM sales ORDER BY created_at DESC LIMIT 20');
      final data = result.map((row) => {
        'product': row[0] as String,
        'amount': row[1] as double,
        'time': row[2] as DateTime,
      }).toList();

      if (mounted) {
        setState(() => _salesData = data);
      }
    } catch (e) {
      print('查询失败: $e');
    }
  }

  
  void dispose() {
    _conn?.close();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('实时销售看板')),
      body: _connecting
          ? const Center(child: CircularProgressIndicator())
          : _error.isNotEmpty
              ? Center(child: Text(_error, style: const TextStyle(color: Colors.red)))
              : RefreshIndicator(
                  onRefresh: _refreshData,
                  child: ListView.builder(
                    itemCount: _salesData.length,
                    itemBuilder: (context, index) {
                      final item = _salesData[index];
                      return ListTile(
                        leading: const Icon(Icons.shopping_cart),
                        title: Text(item['product']),
                        subtitle: Text(item['time'].toString().substring(11, 19)),
                        trailing: Text(
                          '¥${item['amount']}',
                          style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: Colors.green),
                        ),
                      );
                    },
                  ),
                ),
      floatingActionButton: FloatingActionButton(
        tooltip: '模拟下单',
        child: const Icon(Icons.add),
        onPressed: () {
          // 仅用于演示:客户端插入一条数据触发通知
          _conn?.execute("INSERT INTO sales (product, amount) VALUES ('OpenHarmony Device', 999.0); NOTIFY new_sale;");
        },
      ),
    );
  }
}

在这里插入图片描述

六、总结

在 OpenHarmony 上通过 postgres 库直连数据库,为开发者提供了极大的灵活性,尤其适合快速原型开发、内部工具以及实时性要求极高的场景。

最佳实践

  1. 安全性:不要把生产环境数据库端口直接暴露在公网,建议配合 VPN 或 SSH 隧道使用,或者至少限制 IP 白名单。
  2. 连接池:虽然库内置了连接池,但移动端资源有限,应谨慎开启过多连接。通常一个长连接用于监听,短连接按需使用。
  3. 架构设计:对于大型 C 端应用,仍建议通过 HTTP API 访问数据,直连仅限特定后端或管理端场景。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐