Skip to content

PostgreSQL 驱动

dc3-driver-postgresql 把一个 PostgreSQL 数据库当作数据源接入 IoT DC3:按采集周期执行 SELECT 把查到的值当采集值,并支持用位号上配置的 UPDATE/INSERT 写查询向库里写值。读完你能把已有库表里的字段当位号采进平台,并理解读/写两条 SQL 路径各自的边界。

协议背景

不是所有数据都来自现场协议设备。很多业务数据、历史数据、第三方系统沉淀的结果,本身就躺在一张 PostgreSQL 表里——MES 的工单状态、计量系统结算后的累计量、上游平台只对外开放的一个数据库视图。这类数据没有 Modbus、OPC UA 这样的现场总线协议可走,但它们确实是设备世界的延伸,需要被纳入统一的位号体系来管理和消费。

PostgreSQL 是一套开源的对象-关系型数据库(ORDBMS),以严格的 SQL 标准遵循度、强事务(MVCC)、丰富的数据类型(JSON/JSONB、数组、范围、几何)著称,默认监听 5432 端口,客户端通过标准 JDBC(org.postgresql.Driver)连接。把它接入物联网平台,本质上是把"数据库当成一类设备" :一张表的一行一列,就是一个位号的取值来源。

在物联网四层参考架构里,数据库桥接驱动横跨网络层与平台层的边界——它不解析任何现场总线报文,而是把"已经落到库里的数据" 以位号的形式重新接入采集管线。理解这条路径如何与时序存储、流处理衔接,见时序数据与流处理

与现场协议驱动的本质差异

Modbus、OPC UA 这类驱动面对的是"物理量在线"——寄存器实时变化、读到的就是此刻的现场值。数据库驱动面对的是"数据已落库" ——你读到的是某个上游系统写入的、可能已经过期的快照。把数据库字段当位号采集时,采集周期与上游写库节奏要对得上,否则会反复采到同一个旧值。

属性配置

接入分三类属性:驱动属性(设备级,定位到哪个库、用什么账号)、位号属性(每个位号的读/写 SQL)、写命令属性 (保留项,当前未生效)。所有属性的默认值都来自驱动的 application.yml ,在设备/位号 上为它们填具体值即可,三层来历见属性与配置

驱动属性(设备级 driver-attribute

把一个 PostgreSQL 库接进来时,在设备 上填这些属性。它们决定连到哪个库、用什么账号、连接超时多久:

属性code类型默认值说明
HosthostSTRINGlocalhostPostgreSQL 主机地址
PortportINT5432PostgreSQL 端口
DatabasedatabaseSTRING(空)要连接的数据库名
UsernameusernameSTRINGroot连接账号
PasswordpasswordSTRING(空)账号密码
Query TimeoutqueryTimeoutINT30连接获取超时(秒),同时作为 Hikari 连接池的 connectionTimeout

驱动用 hostportdatabase 拼出 JDBC URL(形如 jdbc:postgresql://host:port/database)。设备配置校验(validate() )会逐项检查 hostportdatabaseusernamepassword 五项是否填写——任一为空都不通过;queryTimeout 非必填,缺省按 30 秒。每个设备在驱动内对应一个独立的 HikariCP 连接池(maximumPoolSize=5minimumIdle=1),按设备 ID 缓存复用。

位号属性(point-attribute

每个采集位号上填它对应的读/写 SQL:

属性code类型默认值说明
Read QueryreadQuerySTRING(空)读取位号值的 SELECT 语句
Write QuerywriteQuerySTRING(空)写值用的 UPDATE/INSERT,用单个 ? 占位待写入值(按参数绑定)

Read Query 取结果的第一行第一列

readQuery 是一条普通 SELECT,驱动执行后取结果集第一行的第一列rs.getObject(1))作为该位号的值,再 toString() 交给位号按其数据类型(PointpointTypeFlag)解析。所以写成 SELECT temperature FROM sensor WHERE id = 1 这种只返回单行单列的查询最稳妥。readQuery 是位号的必填项,位号校验( validatePoint())缺它不通过。

写命令属性(command-attribute

写命令上保留了一个属性,但当前不被实现消费:

属性code类型默认值说明
Execute QueryexecuteQuerySTRING(空)命令执行用的 SQL(保留项)

executeQuery 当前未被实现消费

写值实际走的是位号上的 writeQuery:驱动的 write()point-attributewriteQuery,用预编译参数绑定执行 UPDATE/ INSERTcommand-attribute 上的 executeQuery 仅作为配置项保留,当前驱动代码中没有任何地方读取或执行它——不存在" 按命令直接执行一段 SQL"的独立路径。需要写值时,请把 SQL 配在位号的 writeQuery 上,配 executeQuery 不会生效。

采集与健康

  • 采集周期:默认 cron 0/30 * * * * ?(每 30 秒读一轮)。
  • 自定义周期:驱动还有一个 custom 调度,默认 cron 0/5 * * * * ?(每 5 秒);JDBC 数据库驱动的 schedule() 为空实现,该调度当前不做任何事。
  • 健康/在线:设备健康检查默认 cron 0/15 * * * * ?,租约超时 45 秒。健康检查通过从连接池取一条连接、conn.isValid(5) 判断库是否可达——连不上则设备置为离线。在线状态机制见设备

故障排查

数据库桥接接入失败,多半不在协议本身,而在连接参数、SQL 形态或上游库的状态。以下按出现频率排列:

设备一直离线 / 连接池建不起来

健康检查靠 conn.isValid(5) 判定。若设备始终离线,先确认 host/port 可达(容器内连宿主机别用 localhost)、database 名大小写正确、账号 username/password 有该库的登录权限。PostgreSQL 默认还受 pg_hba.conf 约束——来源 IP 或认证方式不被允许时会直接拒连,这类错误不在驱动侧,要去库端放行。

Read Query 必须只读且能定位到单值

驱动只取 readQuery 结果的第一行第一列,所以查询要返回单行单列、且带 WHERE 主键条件稳定定位目标行。返回多行/多列时只会取到第一个,可能不是你想要的那条;查询无结果时位号值为 null。别在 readQuery 里写 UPDATE/DELETE——采集是只读路径,写进去会污染数据。把过滤条件写全,避免随表数据变化取到错行。

Write Query 用 ? 占位符,不是 ${value}

写值时 writeQuery 用单个 ? 代表写入值(如 UPDATE sensor SET temperature = ? WHERE id = 1),驱动用 ps.setString(1, value) 把命令参数作为 JDBC 参数绑定——这是预编译参数绑定,不是字符串拼接,天然防 SQL 注入。不要手动拼接值,也不要用 ${value} 这类模板语法:那样既不会被替换、也失去防注入的好处。写操作以"受影响行数 > 0"判成功,UPDATE 命中 0 行(WHERE 没匹配到)会被当作写失败。

库名 / 表名大小写按 PostgreSQL 规则

PostgreSQL 对不加引号的标识符会折叠成小写,加双引号则按字面大小写匹配。database 填错大小写连不上库;readQuery/ writeQuery 里表名、列名大小写与实际建库时不符也会报"不存在"。按真实大小写填,必要时在 SQL 里用双引号包住标识符。

查询超时 / 慢 SQL

queryTimeout(默认 30 秒)被用作 Hikari 连接池的 connectionTimeout,即"取不到连接时的等待上限"。涉及大表或慢 SQL 时,应优化 SQL、加索引、缩小 WHERE 范围,而不是一味调大超时——慢查询会占住连接池(仅 5 条连接),拖累整批位号的采集。

在 IoT DC3 中如何落地

  • 驱动名 / codePostgreSQL Driver / PostgresqlDriverdc3.driver.code 是稳定路由标识,平台内据它路由消息,不可随意改)。
  • 类型DRIVER_CLIENT——驱动主动连库并发起查询,不监听外部推送。
  • 读 / 写 / 订阅能力:与驱动能力矩阵一致——读 ✓、写 ✓、订阅 —。读走 SELECT 取首值,写走 writeQuery 的预编译绑定;数据库没有变更订阅,靠周期轮询拉取。

实现状态:可用(非骨架)

PostgreSQL 驱动是可用实现,不是骨架。连接、读、写、健康检查均由共享的 dc3-common-sql 抽象基类 AbstractJdbcDriverCustomService 落地(与 MySQL、Oracle、SQL Server 共用同一套逻辑),PostgreSQL 子类只提供 JDBC URL 拼装、驱动类名 org.postgresql.Driver 和默认端口 5432。唯一需要留意的是 command-attributeexecuteQuery 属性保留但未接线(见上文属性配置)。

最小接入示例

把一张 sensor 表里 id=1 那行的 temperature 字段当温度位号采进来:

  1. PostgreSQL Driver 创建设备,driver 属性填 host=192.168.1.10port=5432database=iotusername=rootpassword=******
  2. 给设备绑定的模板加一个温度位号pointTypeFlag=FLOATREAD_ONLY),point 属性填 readQuery=SELECT temperature FROM sensor WHERE id = 1
  3. 启动驱动,30 秒内就能在位号值里看到查到的温度值。

需要可写位号时,再在同一位号的 writeQuery 上配 UPDATE sensor SET temperature = ? WHERE id = 1,并把位号 rwFlag 设为可写。一次完整流程见设备接入

延伸阅读

基于 AGPL-3.0 协议发布