OpenHIS 医疗系统的数据库信创化小例
Approximate reading time: 3 h
- Cite article
- https://docs.Kingbase.com.cn/cn/KES-V9R1C10
- https://openhis.org.cn/
- https://open.tntlinking.com/resource/industryKnowledge?site=gitee
- https://deepwiki.com/tntlinking-opensource/openhis-itai
- https://wanghaisheng.github.io/fhir-cn/doc/overview.html
- https://help.Kingbase.com.cn/v9/install-updata/k-deploy/index.html
- https://help.Kingbase.com.cn/v9/development/develop-transfer/kdts-plus/index.html
- https://help.Kingbase.com.cn/v8/development/client-interfaces/jdbc/index.html
Tips: 基于各行各业的数据库信创化正在如火如荼的展开,作为数据库运维行业从业员,也是希望能够及时参与到这一重要变革当中。
因为客观环境,无法直接在实际生产环境去进行信创流程验证与测试,便寄希望自己部署一套应用测试环境,将其原先的数据库基座替换成 Kingbase 数据库,来看看当前会有哪些问题和难题。
当然该应用系统无法与实际的生产应用环境相提并论,但还是具备有一定的参考和学习意义。
内容目录
1 系统基础简介
2 系统基础展示
3 数据库信创改造
3.1 数据库目标端
3.2 数据库迁移工作
3.3 更换数据源配置
3.4 适配异常处理
4 总结
1 系统基础简介
目前医院系统开源的选择并不多,基于 Oracle 数据库的医疗开源软件更是屈指可数。
天天开源的OpenHIS 医院系统(信创版)集十大核心模块于一体,涵盖目录管理、基础数据配置、个性化设置、门诊/住院全流程管理、药房药库智能管控、精细化耗材管理、财务核算体系、医保合规对接及多维报表分析等功能模块,共计 372 项标准化功能。
其采用的数据库是 PostgreSQL,属于可信创的数据库,大致符合信创改造的应用系统需求,同时这意味着信创难度不大....... 但还是有去实践的必要!
1.1 系统概述
OpenHIS 采用分层架构模式,在表示层、应用层、领域层和数据层之间实现了清晰的分离。该系统是作为现代 Web 应用程序构建的,前端使用 Vue.js 框架,后端则通过 Spring Boot 框架实现,前后端之间可进行通信。
1.2 系统技术栈
OpenHIS 采用现代化的企业级技术构建而成,这些技术确保了系统的可扩展性、可维护性。
组件(Component) | 技术(Technology) | 版本(Version) | 用途(Purpose) |
|---|---|---|---|
前端框架(Frontend Framework) | Vue.js | 3.3.9 | 用户界面与客户端逻辑 |
后端框架(Backend Framework) | Spring Boot | Java 17 | 应用服务器与业务逻辑 |
数据库(Database) | PostgreSQL | 16.2 | 主要数据持久化(存储) |
缓存(Cache) | Redis | 稳定版本 | 会话管理与缓存 |
构建工具(Build Tool) | Vite | 5.0.4 | 前端构建与开发(支持) |
UI 组件库(UI Components) | Element Plus | 2.4.3 | (前端)组件库 |
状态管理(State Management) | Pinia | 2.1.7 | 前端状态管理(如数据共享) |
1.3 数据模型架构
该系统的数据架构遵循受 FHIR【FHIR 标准,它是卫生保健信息电子化交换的一种标准。】 启发的模式,核心医疗实体之间存在明确的关系。
如下是该系统数据库所有业务表:
1.4 开发运行展示
系统后端采用 IntelliJ IDEA 工具进行开发运行测试:
系统前端采用 Visual Studio Code 工具进行开发运行测试:
2 系统基础展示
2.1 管理页面
2.2 患者列表
2.3 门诊管理
2.4 病人管理
2.5 药品目录
系统还涉及其他功能很多模块,便不一一展示。
3 数据库信创改造
基于上述章节描述,大家应该对该系统有大致了解,本次测试计划使用 Kingbase 主备集群进行数据库信创替代工作。
3.1 数据库目标端
3.1.1 主备集群部署
采用 Kingbase 的数据库部署工具搭建一套 Kingbase v9 主备集群,其数据库兼容模式设置为 PG。
Kingbase数据库部署工具由纯java编写,用户仅需知道所需部署服务器的ip、port等信息,再根据工具的引导,输入集群脚本的关键参数,完成集群的引导步骤即可部署一套完整的集群服务,同时还提供集群状态监控服务。部署完成后,集群状态可被实时监控,在出现错误后提供日志供予错误分析,还可在查看监控的同时,修改集群参数,进行配置下发,以达到最好的状态。
请根据源库类型确认 Kingbase 的数据库兼容模式,否则后期将遭遇信创数据库不支持原有 SQL 特有写法, 导致系统运行异常。
类似如下告警信息【 not supported. pos】:
16:09:17.240 [http-nio-18080-exec-11] ERROR c.a.d.f.s.StatFilter - [mergeSql,160] - merge sql error, dbType Kingbase, druid-1.2.23, sql : SELECT dict_label FROM sys_dict_data WHERE dict_type = ? AND dict_value::varchar = ? LIMIT 1
com.alibaba.druid.sql.parser.ParserException: not supported.pos 73, line 1, column 72, token ::
at com.alibaba.druid.sql.parser.SQLStatementParser.parseStatementList(SQLStatementParser.java:654)
at com.alibaba.druid.sql.parser.SQLStatementParser.parseStatementList(SQLStatementParser.java:106)
at com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils.parameterize(ParameterizedOutputVisitorUtils.java:164)
at com.alibaba.druid.sql.visitor.ParameterizedOutputVisitorUtils.parameterize(ParameterizedOutputVisitorUtils.java:135)
........如果是是使用 Oracle 的兼容模式,可能出现业务数据库表与数据库内部视图产生冲突,导致业务的查询对象被指向到内部视图,导致查询校验异常。
例如 sys_user 表冲突:
-- 解决办法:修改search_path
ALTER DATABASE hisdb SET search_path TO '$config', his, public, sys, sys_catalog, pg_catalog;Kingbase 主备集群具体安装部署过程便不在此展示,下面检查主备集群是否正常:
3.1.2 集群状态检查
显示集群状态中包含: 上游节点、PID、是否暂停、最后一次观测到的时间等
集群运行信息:
字段 | 内容 |
|---|---|
ID | 数据库节点ID |
Name | 数据库在集群内部名称 |
Role | 数据库节点角色,包含两种:primary(主节点)、standby(备节点) |
Status | 状态,说明如下: |
Upstream | 上游节点名称,即当前节点的数据同步来源节点 |
Hamgrd | 管理软件 remgrd 的运行状态 |
PID | 管理软件 repmgrd 的进程ID |
Paused | 管理软件 repmgrd 是否暂停,说明如下: |
Upstream last seen | 本节点距离上一次同步数据的时间(单位:秒) |
3.1.3 集群同步测试
- 模拟增量业务数据
- 观察 Kingbase 复制延迟
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
write_lag, flush_lag, replay_lag
FROM sys_catalog.sys_stat_replication; \watch 10输出指标解析:
指标 | 含义 | 健康值 | 当前状态 |
|---|---|---|---|
client_addr | 备库 IP 地址 | - | 192.168.40.112 (固定) |
state | 复制状态 | streaming | ✅ 正常 (持续流复制) |
sent_lsn | 主库已发送的 LSN | - | 持续增长 (0/27000000 → 0/406487A8) |
write_lsn | 备库已写入的 LSN | ≈ sent_lsn | ✅ 与 sent_lsn 一致 |
flush_lsn | 备库已刷盘的 LSN | ≈ write_lsn | ✅ 与 write_lsn 一致 |
replay_lsn | 备库已应用的 LSN | ≈ flush_lsn | ⚠️ 15:38 时段有 267ms 延迟 |
replay_lag | 数据应用延迟 | < 100ms | ⚠️ 15:38 时段达 267ms |
3.2 数据库迁移工作
主备集群检测完成,便可以开始进行数据库迁移工作,Kingbase 官方采用 KDTS 进行迁移工作。
数据库数据迁移平台(KDTS: Kingbase Data Transformation Service)是为用户提供 Oracle、Mysql、SQLServer、Gbase、PostgreSQL、DM、KingbaseES 数据库数据迁移到 KingbaseES数据库的数据迁移工具。
源端数据库支持版本 ¶
源端数据库 | 支持版本 |
|---|---|
Oracle | 9i、10g、11g、12c、19c |
MySQL | 5.X、8.X |
SQLServer | 2000、2005、2008、2012、2014、2016、2017、2019 |
Gbase | 8s、8g |
PostgreSQL | 12 |
DM | 8 |
Db2 | 11 |
神通(OSCAR) | 7 |
KingbaseES | V7、V8R3、V8R6、V8R6C7、V9 |
3.2.1 KDTS 工具部署
部分 Kingbase 数据库安装包自带 KDTS 工具,其目录为:/home/Kingbase/ES/V9/ClientTools/guitools/KDts。
- 访问地址: http://localhost:54523/
- 默认用户名及密码:Kingbase / Kingbase 或者 Kb_DI@2019
- 会话保存策略:会话保存时间为一天
3.2.2 创建迁移任务
为了完成数据迁移工作,需要管理数据源信息。数据源分为源数据库与目标数据库两种,包括添加和删除数据源连接信息,添加时填写数据库基本信息等。
- 新增源数据库
查询源库相关版本:
SELECT version ();填写数据源连接信息并测试:
- 新增目标数据库
- 创建迁移任务
- 模式选择下进行全选
- 选择迁移对象并确认
- 配置参数【鉴于目前测试,默认选择,不做变更】
3.2.3 启动迁移业务
- 确认选择
- 查看进度
- 确认迁移结果
- Kingbase数据库查询数据库表
3.3 更换数据源配置
当一个程序项目需要变更数据源时,通常需要进行应用参数的变更。
3.3.1 确认参数路径
在 Java 语言的程序开发项目中,尤其是使用 SpringBoot 开发框架时,application. yml、application-druid. yml 这两个文件都是用于配置项目参数的 YAML 格式配置文件。下面分别介绍它们的功能:
- application. yml
application. yml 是 Spring Boot 项目的主配置文件,它通常包含以下内容:
- 基础配置:定义应用的基本属性,如应用名称、服务器端口、日志级别等。
- 数据源配置:配置数据库连接信息,如数据库 URL、用户名、密码等。
- 其他服务配置:配置如消息队列、缓存、邮件服务、定时任务等中间件的属性。
- 应用特性配置:配置应用级别的特性,如是否启用某些功能、API 版本等。
这个文件通常包含了应用在开发环境下的默认配置。在启动 Spring Boot 应用时,如果不指定特定的配置文件,Spring Boot 会默认加载 application. yml。
- application-druid. yml
application-druid. yml 是一个专门用于配置 Druid 数据源的配置文件。Druid 是一个高性能、功能强大的数据库连接池,这个文件通常包含以下内容:
- Druid 连接池配置:配置数据源的初始化大小、最大连接数、最小空闲连接数等。
- 监控配置:配置 Druid 的监控统计功能,如 SQL 监控、Web 监控等。
- 过滤器配置:配置 Druid 的各种过滤器,如统计过滤器、日志过滤器等。
- 密码加密配置:配置数据库密码的加密方式。
这个文件通常在需要更细粒度地控制数据源配置时使用,它允许将数据源配置从主配置文件中分离出来,便于管理和维护。
由上述内容可知,在进行一个 SpringBoot 信创开发项目的时候,适配国产数据库的主要配置参数,便在 application-druid. yml,其他类型项目也会有其他参数的配置路径,总体而言,大差不差。
3.3.2 准备数据源配置
- 引入 POM 数据库驱动依赖包
为了使用KingbaseES数据库,官方提供了KingbaseES JDBC的驱动包:
驱动包 | 支持最低版本 |
|---|---|
Kingbase8-9.0.0.jre6.jar | JDK1.6 |
Kingbase8-9.0.0.jre7.jar | JDK1.7 |
Kingbase8-9.0.0.jar | JDK1.8 |
KingbaseES 的 JDBC 驱动程序存放在数据库安装程序目录的 JDBC 文件夹内,应用程序只需把 Oracle 的 JDBC 驱动程序和数据库连接字符串替换为 KingbaseES 的对应内容即可。
也可以在maven项目的pom.xml文件中添加依赖,根据需求选择需要的版本:
<!-- 最低可支持JDK1.8 -->
<dependency>
<groupId>cn.com.Kingbase</groupId>
<artifactId>Kingbase8</artifactId>
<version>9.0.0</version>
</dependency>
<!-- 最低可支持JDK1.7 -->
<dependency>
<groupId>cn.com.Kingbase</groupId>
<artifactId>Kingbase8</artifactId>
<version>9.0.0.jre7</version>
</dependency>
<!-- 最低可支持JDK1.6 -->
<dependency>
<groupId>cn.com.Kingbase</groupId>
<artifactId>Kingbase8</artifactId>
<version>9.0.0.jre6</version>
</dependency>3.3.3 更新参数文件
# driverClassName: com.Kingbase8.Driver
# master:
# url: jdbc:Kingbase8://192.168.40.111:54322/hisdb
# username: system
# password: 12345678ab3.3.4 程序编译启动
开始编译运行时,数据库连接正常,便开始进入数据库适配的核心地带,处理出现的一系列问题。
3.4 适配异常处理
3.4.1 中间件异常
Java Spring 应用中集成了业务流程引擎 Flowable 框架,当 Java 应用通过 Kingbase Connector/J 驱动连接到 Kingbase 数据库中,应用连接遇到了报错 nested exception is org.flowable.common.engine.api.FlowableException: couldn't deduct database type from database product name 'KingbaseES'。
Caused by: org. flowable. common. engine. api. FlowableException: couldn't deduct database type from database product name 'KingbaseES'1. 原因分析:
- flowable启动后会从已有的databaseTypeMappings集合中看到数据库类型。
- databaseTypeMappings集合调用的是getDefaultDatabaseTypeMappings方法
- 在getDefaultDatabaseTypeMappings函数中并没有 KingbaseES 类型,所以会报错。
2. 解决方案
- 方法一:程序层面添加代码规避该问题【通过AOP直接指定databaseType为 postgres】,难度较大,且不太符合现实,但也可以规避该情况。
package com.openhis;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.flowable.app.engine.AppEngineConfiguration;
import org.springframework.stereotype.Component;
@Aspect
@Component
@RequiredArgsConstructor
public class KingbaseSupport {
private final AppEngineConfiguration appEngineConfiguration;
@Pointcut("execution(* org.flowable.app.engine.AppEngineConfiguration.buildAppEngine())")
public void access() {
}
@Before("access()")
public void before() {
appEngineConfiguration.setDatabaseType("postgres");
}
}还是会有些问题,但是不影响程序正常使用。
- 方法二: 官方驱动适配该情况(OB、DM 官方社区存在适配案例),目前查看 Kingbase 官方连接串参数、和驱动配置未发现有相关适配【官方社区也有说直接使用 PG 驱动连接 Kingbase 数据库】。
程序应用正常,但是相关 Kingbase 支持的驱动特性功能【读写分离负载均衡】可能无法使用。
后端服务启动后,开始启动前端 WEB 服务:
3.4.2 业务语法异常
随后对相关的业务系统功能进行测试,发现部分业务 SQL 还是存在异常情况,按理来说 PostgreSQL 数据库迁移到 Kingbase v9 PostgreSQL 兼容模式还能出现语法异常吗?我表示不理解........
查看后端运行日志,可明显得知 【a bad SQL grammar】 SQL语法错误:
### SQL: SELECT COUNT(*) AS total FROM (SELECT T1.tenant_id, T1.id AS encounter_id, T1.start_time, TO_CHAR(T1.start_time, 'YYYY-MM-DD') AS encounter_date, T1.organization_id AS department_id, T2.gender_enum, T2.name AS patient_name, T2.id_card, T3.name AS department_name FROM adm_encounter AS T1 INNER JOIN adm_patient AS T2 ON T1.patient_id = T2.id LEFT OUTER JOIN adm_organization AS T3 ON T1.organization_id = T3.id GROUP BY T1.id, T1.start_time, T1.organization_id, T2.gender_enum, T2.name, T2.id_card, T3.name ORDER BY T1.start_time DESC, T1.organization_id, T1.patient_id) WHERE (tenant_id = ? AND start_time >= ? AND start_time <= ?)
### Cause: com.Kingbase8.util.KSQLException: ERROR: subquery in FROM must have an alias
Hint: For example, FROM (SELECT ...) [AS] foo.
Position: 31 At Line: 1, Line Position: 31
; bad SQL grammar []; nested exception is com.Kingbase8.util.KSQLException: ERROR: subquery in FROM must have an alias
Hint: For example, FROM (SELECT ...) [AS] foo.
Position: 31 At Line: 1, Line Position: 31
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:101)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:82)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:82)
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:439)
at jdk.proxy2/jdk.proxy2.$Proxy144.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:224)原数据库PostgreSQL 16 正常支持该 SQL 语法 (PostgreSQL 16开始取消了该限制):
14:08:32.709 [http-nio-18080-exec-17] DEBUG c.o.a.m.O.selectList - [debug,135] - ==> Parameters: 1(Integer)
14:08:32.709 [http-nio-18080-exec-16] DEBUG c.o.w.p.m.W.selectEncounterInfoListPage_mpCount - [debug,135] - ==> Preparing: SELECT COUNT(*) AS total FROM (SELECT T1.tenant_id, T1.id AS encounter_id, T1.start_time, TO_CHAR(T1.start_time, 'YYYY-MM-DD') AS encounter_date, T1.organization_id AS department_id, T2.gender_enum, T2.name AS patient_name, T2.id_card, T3.name AS department_name FROM adm_encounter AS T1 INNER JOIN adm_patient AS T2 ON T1.patient_id = T2.id LEFT OUTER JOIN adm_organization AS T3 ON T1.organization_id = T3.id GROUP BY T1.id, T1.start_time, T1.organization_id, T2.gender_enum, T2.name, T2.id_card, T3.name ORDER BY T1.start_time DESC, T1.organization_id, T1.patient_id) WHERE (tenant_id = ? AND start_time >= ? AND start_time <= ?)
14:08:32.710 [http-nio-18080-exec-16] DEBUG c.o.w.p.m.W.selectEncounterInfoListPage_mpCount - [debug,135] - ==> Parameters: 1(Integer), 2025-09-17 00:00:00.0(Timestamp), 2025-09-17 23:59:59.0(Timestamp)
14:08:32.711 [http-nio-18080-exec-17] DEBUG c.o.a.m.O.selectList - [debug,135] - <== Total: 6
14:08:32.712 [http-nio-18080-exec-16] DEBUG c.o.w.p.m.W.selectEncounterInfoListPage_mpCount - [debug,135] - <== Total: 1本想去官方社区查看该问题是否存在案例,我没想到是如下情况....... 不知道哪里有敏感词.....
随后查询其他资源可知,【https://juejin.cn/post/7080795177362653191】可以通过数据库Patch的方式去规避这个问题,显然我这边无法进行该操作,那么只能进行业务SQL语法改写【幸亏我能改......】。
找到该业务的Mapper文件(mapper.xml 文件是 MyBatis 中用于定义 SQL 语句与 Java 方法之间映射关系的 XML 配置文件),添加别名。
随后测试业务,前后端测试显示正常。
4 总结
经过上述的一系列操作实践可知,即便在改造难度系数最低的应用系统面前,还是存在各种问题:
- 数据库兼容性问题: KingbaseES数据库在兼容PostgreSQL模式时,仍存在一些SQL语法不支持的问题,导致业务系统运行异常。
- 中间件适配问题: 集成业务流程引擎Flowable框架时,Flowable无法识别KingbaseES数据库类型,导致应用连接报错。
- 数据迁移问题: 使用KDTS工具进行数据迁移时,需要正确配置源数据库和目标数据库的连接信息,并选择合适的迁移模式和对象。
- 数据源配置问题: 在更换数据源配置时,需要更新SpringBoot项目的application-druid. yml配置文件,并引入KingbaseES的JDBC驱动包。
若想要解决这些上述这些问题,我很难相信在不通过应用厂商和数据库原厂的协作下就能完成系统的信创迁移改造工作,当然万事皆有可能 Who knows 。




