1. 从一次紧急的国产化适配需求说起
去年,我们团队接到一个硬性任务:一个运行了多年的核心审批系统,需要从原有的MySQL数据库,整体迁移到国产的达梦数据库。这个系统底层用的是Flowable工作流引擎,版本是6.7.2。当时,项目组里弥漫着一种“技术栈不兼容”的焦虑感。Flowable官方文档里,对Oracle、MySQL、PostgreSQL的支持写得明明白白,但关于达梦,几乎只字未提。网上零散的资料,要么是几年前的旧版本尝试,要么就是一句“理论上支持,需要自己适配”,让人心里完全没底。
这个场景,我相信很多面临信创改造或国产化替代的团队都遇到过。Flowable作为一款优秀的开源BPMN工作流引擎,其流程定义、运行时实例、历史数据、身份信息等核心数据都依赖数据库存储。数据库的更换,绝非修改一个jdbc.url那么简单,它涉及到驱动兼容性、SQL方言适配、建表脚本、事务隔离级别等一系列底层细节。如果适配不当,轻则流程引擎启动失败,重则运行时出现各种诡异的SQL异常和数据一致性问题。
经过近一个月的摸索、踩坑和反复验证,我们最终成功地将Flowable 6.7.2稳定运行在了达梦数据库8(DM8)上。整个过程,远不止是“换个驱动”那么简单,更像是一次对Flowable数据库抽象层和达梦数据库特性的深度探索。今天,我就把这次实战中的核心要点、适配步骤、遇到的“坑”以及最终的解决方案,系统地梳理出来。无论你用的是Flowable 6.x还是7.x,无论你面对的是达梦还是其他国产数据库,这篇文章提供的思路和具体操作,都能为你扫清障碍。
2. 理解Flowable的数据库抽象层:适配的基石
在动手改配置之前,我们必须先搞清楚Flowable是如何与数据库打交道的。这是后续所有适配工作的理论基础,也能帮你理解为什么某些配置是必须的。
2.1 核心接口:DatabaseType 与 SqlSessionFactory
Flowable设计了一套良好的数据库抽象层,其核心是org.flowable.common.engine.api.db.DatabaseType枚举和一系列DatabaseMetaData的实现。当Flowable启动时,它会通过JDBC连接获取数据库的元信息(如产品名称、版本号),然后映射到内部的DatabaseType。例如,对于MySQL,它会识别为DatabaseType.MYSQL;对于Oracle,则是DatabaseType.ORACLE。
这个DatabaseType至关重要,因为它决定了:
- 使用哪一套SQL映射文件(MyBatis Mapper XML):Flowable为每种数据库类型都准备了一套优化的SQL语句,存放在各个模块的
/resources/org/flowable/*/db/mapping目录下。比如,flowable-engine模块下就有mapping/mysql、mapping/oracle等目录。 - 使用哪个
DatabaseMetaData实现:这个实现类负责处理数据库特有的行为,比如分页查询的语法、Blob/Clob类型处理、数据库对象(表、序列)的命名规则等。 - 决定如何执行建表/升级脚本:Flowable使用Liquibase进行数据库版本管理。
DatabaseType决定了使用哪个目录下的Liquibase changelog文件(通常是/resources/org/flowable/*/db/liquibase下的子目录)。
达梦数据库(DM)与Oracle有很深的渊源,其SQL语法、数据类型、序列机制等都高度相似。因此,Flowable社区和达梦官方通常建议将达梦配置为DatabaseType.ORACLE。这是一个关键的战略选择,意味着我们将利用Flowable对Oracle的现有支持作为桥梁,来适配达梦。
2.2 达梦与Oracle的“同”与“不同”
虽然配置为Oracle模式能解决大部分问题,但达梦并非Oracle的完全克隆,存在一些细微但关键的差异,这些差异正是我们踩坑的地方:
- 驱动类名:达梦的JDBC驱动类是
dm.jdbc.driver.DmDriver,连接URL格式为jdbc:dm://host:port?schema=SCHEMA_NAME,这与Oracle的oracle.jdbc.OracleDriver和jdbc:oracle:thin:@host:port:sid完全不同。 - 模式(Schema)与用户:在达梦中,每个用户(User)默认关联一个同名的模式(Schema)。当使用一个用户连接时,其默认操作的就是该用户同名的Schema。这一点与Oracle类似,但与MySQL的“Database”概念有区别。在Flowable配置中,正确处理Schema是关键。
- 分页查询语法:Oracle使用
ROWNUM进行分页,而达梦虽然兼容ROWNUM,但在某些复杂子查询或联合查询中,其优化器行为可能与Oracle有差异。不过,由于我们告诉Flowable这是“Oracle”,它会使用Oracle风格的分页SQL,这在达梦上通常是可用的。 - 数据类型映射:达梦的
CLOB、BLOB类型与Oracle的对应类型在JDBC驱动层面的处理方式需要验证。特别是Flowable会将一些大文本(如流程变量值)存入CLOB。 - 序列(Sequence):两者都支持序列,语法也兼容。Flowable为Oracle生成的建表语句中包含创建序列的SQL,这在达梦上可以直接运行。
理解了这些,我们的适配路径就清晰了:首先,将达梦“伪装”成Oracle,让Flowable的基础框架能跑起来;然后,逐一解决因“伪装”而产生的驱动、连接、Schema等具体问题。
3. 实战适配:从零开始让Flowable跑在达梦上
假设我们有一个基于Spring Boot 2.7 和 Flowable 6.7.2 的项目,现在要将其数据源从MySQL切换到DM8。
3.1 环境与依赖准备
首先,确保你的达梦数据库服务已经安装并启动。这里以DM8为例。
1. 引入达梦JDBC驱动Flowable官方依赖中不包含达梦驱动,需要手动引入。有两种方式:
- 方式一:直接引入Jar包:从达梦官网下载
DmJdbcDriver18.jar(对应JDK 1.8),将其放入项目的lib目录,并在构建工具中引入。 - 方式二(推荐):使用Maven仓库:如果你公司有私有仓库,可以将达梦驱动部署上去。或者,在
pom.xml中通过system路径引用本地jar(不推荐用于生产)。
这里以Maven为例,假设已将jar包安装到本地仓库:
<dependency> <groupId>com.dameng</groupId> <artifactId>DmJdbcDriver18</artifactId> <version>8.1.2.141</version> <!-- 请替换为你的实际版本 --> </dependency>2. 移除或排除原有数据库驱动如果你之前用的是MySQL,记得在pom.xml中移除mysql-connector-java依赖,或者在Flowable的starter中排除掉它,避免驱动冲突。
3. 确认Flowable版本我们以flowable-spring-boot-starter为例。确保你的pom.xml中相关依赖版本一致。
3.2 核心配置:application.yml/application.properties
这是整个适配的核心步骤,配置文件需要做如下关键修改:
spring: datasource: # 1. 达梦驱动类 driver-class-name: dm.jdbc.driver.DmDriver # 2. 达梦连接URL,注意格式。SERVER=服务器名, SCHEMA=你的模式名(通常等于用户名) url: jdbc:dm://192.168.1.100:5236?schema=FLOWABLE_DEMO&server=DEMO_SERVER username: FLOWABLE_USER # 建议为Flowable创建专属用户 password: YourStrongPassword123! hikari: # 3. 连接池额外配置,非常重要! connection-test-query: SELECT 1 FROM DUAL # 达梦的测试查询语句 # 达梦的默认事务隔离级别是 READ_COMMITTED,与Oracle一致,通常无需修改 # transaction-isolation: TRANSACTION_READ_COMMITTED # 4. 强制指定Flowable使用的数据库类型为 ORACLE flowable: database-schema-update: true # 启动时自动更新数据库结构(首次使用) db-history-used: true # 这是最关键的一步!告诉Flowable引擎,将当前数据库视为Oracle类型。 database-type: oracle配置项深度解析:
driver-class-name和url:这是连接达梦的基础,格式必须正确。schema参数指定了连接后默认的模式,Flowable创建的所有表都会在这个模式下。connection-test-query:对于HikariCP等连接池,需要一个简单的SQL来检测连接是否有效。达梦兼容Oracle的DUAL表,所以使用SELECT 1 FROM DUAL是标准做法。如果没有配置,连接池可能在获取空闲连接时报错。flowable.database-type: oracle:这是灵魂配置。它覆盖了Flowable自动检测数据库类型的行为,强制其使用针对Oracle的SQL映射文件、建表脚本和元数据处理逻辑。
3.3 处理建表与Schema初始化
当flowable.database-schema-update设置为true时,Flowable会在启动时使用Liquibase检查并创建所需的表结构。
1. 首次启动的观察启动你的Spring Boot应用,观察日志。你应该能看到类似以下的输出:
INFO o.f.s.b.a.FlowableDatabaseConfiguration - Database type: oracle INFO o.f.s.b.a.FlowableDatabaseConfiguration - Updating database schema for Flowable engine 'default' INFO l.lockservice.StandardLockService - Successfully acquired change log lock ... INFO l.c.StandardChangeLogHistoryService - Reading from `FLOWABLE_USER`.`DATABASECHANGELOG` # 注意这里的模式名 INFO o.f.s.b.a.FlowableDatabaseConfiguration - Flowable schema created如果看到Database type: oracle和Flowable schema created,恭喜你,最基础的一关已经过了。此时,连接到你的达梦数据库,在FLOWABLE_USER模式下,应该能看到一大批以ACT_开头的表(如ACT_RE_PROCDEF,ACT_RU_TASK等)。
2. 关于Schema权限的坑与解决这里最容易出问题的地方是用户权限。达梦数据库的用户权限体系比较严格。用于连接Flowable的数据库用户(如FLOWABLE_USER)必须拥有足够的权限在其自身的Schema下创建表、序列、索引,并且需要有对SYS系统表(如DUAL)的查询权限。
如果启动时报错“权限不足”或“对象不存在”,你需要用DBA账号(如SYSDBA)登录,执行如下授权SQL:
-- 1. 确保用户存在并有权登录 CREATE USER FLOWABLE_USER IDENTIFIED BY "YourStrongPassword123!"; -- 2. 授予其在自己Schema下的基本资源权限(包含建表、建序列等) GRANT RESOURCE TO FLOWABLE_USER; -- 3. 授予连接权限 GRANT PUBLIC TO FLOWABLE_USER; -- 或者使用 GRANT CREATE SESSION TO FLOWABLE_USER; (视版本而定) -- 4. 授予查询DUAL表的权限(关键!) GRANT SELECT ON SYS.DUAL TO FLOWABLE_USER; -- 5. 如果涉及跨Schema操作(通常不需要),可能还需要额外授权实操心得:我们曾在测试环境遇到一个诡异问题,应用启动成功,但执行第一个流程实例时,在插入历史记录时报错“表不存在”。排查后发现,是Liquibase的
DATABASECHANGELOG表虽然创建了,但部分ACT_表因权限问题未能成功创建,而Flowable的启动日志却没有明确报错。因此,启动后务必手动检查一下核心表(如ACT_RU_TASK,ACT_HI_PROCINST)是否真的存在,不要完全依赖日志。
3.4 应对可能的SQL兼容性问题
即使配置为Oracle类型,在极端复杂的流程场景或特定查询中,仍可能遇到SQL语法或函数不兼容的情况。Flowable的SQL是写在MyBatis Mapper XML文件中的。
1. 如何定位问题SQL当引擎执行某个操作(如分页查询任务、查询历史变量)报错时,日志中会打印出执行的SQL语句和绑定参数。你需要仔细查看这个SQL。例如,你可能会看到一条使用了Oracle特有函数(如NVL)的语句,而达梦的对应函数可能是IFNULL或NVL(达梦通常兼容)。
2. 自定义SQL映射(高级)如果确实存在不兼容的SQL,你可以通过覆盖Flowable默认的Mapper来实现自定义。这是比较高级的用法,需要:
- 在你的项目中,创建与Flowable内部Mapper相同包名和类名的接口(或继承它)。
- 在
resources目录下,按照相同的目录结构放置你的Mapper XML文件,重写其中的SQL语句。 - 确保你的Spring配置扫描到了你的Mapper。
例如,你想修改任务查询的分页SQL,可以尝试定位到org.flowable.task.service.impl.persistence.entity.TaskEntityImpl相关的Mapper文件。但强烈建议优先在社区或官方渠道确认是否有现成的解决方案,因为覆盖内部Mapper会带来未来的升级和维护成本。
踩坑记录:我们在使用
flowable-spring-boot-starter时,曾遇到一个与ACT_HI_VARINST表查询相关的性能问题。Flowable为Oracle生成的分页SQL在达梦某个版本上执行计划不佳。我们的临时解决方案是,通过flowable.custom-mybatis-mappers配置项,注入一个自定义的Mapper,将分页逻辑简化。但这只是权宜之计,最终在升级达梦JDBC驱动后问题消失。这说明,驱动版本和数据库版本本身也可能影响兼容性。
4. 进阶议题与生产环境考量
基础适配完成后,要让系统在生产环境稳定运行,还需要考虑以下问题。
4.1 多数据源与分布式事务
很多业务系统并非只有一个Flowable数据源,通常还会有业务数据库。在国产化环境中,你可能需要让Flowable连接达梦,而业务系统连接另一个国产数据库(或仍用MySQL)。这就引入了多数据源和分布式事务问题。
方案一:独立数据源,放弃强一致性事务这是最常见的方案。将Flowable引擎的数据源与业务数据源完全隔离。流程引擎的运行时操作(如完成任务、更新变量)与业务操作(如更新订单状态)放在两个独立的事务中。通过业务逻辑或补偿机制(如Saga模式)来保证最终一致性。这种方案架构简单,但需要业务层处理中间状态。
方案二:使用JTA分布式事务管理器如果你必须保证流程操作与业务操作的原子性,就需要引入JTA管理器,如Atomikos或Narayana。Spring Boot可以通过spring-boot-starter-jta-atomikos来支持。你需要将两个数据源都配置为XADataSource。关键点在于:达梦的JDBC驱动是否提供并正确实现了XADataSource接口。你需要查阅达梦官方文档或进行验证。配置会变得复杂,性能也会有一定损耗。
4.2 数据库连接池配置优化
达梦数据库在一些默认参数上可能与MySQL/Oracle不同,需要针对性地优化连接池(如HikariCP)配置。
spring: datasource: hikari: connection-timeout: 30000 # 连接超时时间,单位毫秒 maximum-pool-size: 20 # 根据实际压力调整 minimum-idle: 5 idle-timeout: 600000 # 连接空闲超时时间 max-lifetime: 1800000 # 连接最大生命周期 connection-test-query: SELECT 1 FROM DUAL # 再次强调,必须配置 # 达梦可能对默认的‘auto-commit’状态敏感,建议明确设置 auto-commit: false # Flowable通常自己管理事务,设为false更安全建议在压力测试中监控数据库连接数和应用性能,调整maximum-pool-size等参数。
4.3 版本升级与数据迁移
当你需要升级Flowable版本(如从6.7.2到7.0)或达梦数据库版本时,数据迁移是需要谨慎规划的过程。
Flowable版本升级:Flowable使用Liquibase管理DDL变更。当你升级flowable-spring-boot-starter依赖版本并启动应用时,Liquibase会自动检测DATABASECHANGELOG表中的记录,并执行新版本所需的changelog文件来修改表结构。在正式环境操作前,必须在隔离的测试环境进行完整验证,因为某些版本升级可能包含不兼容的数据迁移逻辑。
达梦数据库升级:通常是小版本升级(如DM8 1.1.xx到1.2.xx),兼容性较好。但仍需在升级后,用你的应用进行全面回归测试,重点测试流程的发起、流转、变量操作、历史查询等核心功能。大版本升级(如DM7到DM8)则需要更严格的评估和迁移测试。
4.4 监控与排查工具
系统上线后,监控是必不可少的。
- Flowable自带Admin应用:部署
flowable-ui应用,可以直观地监控流程实例、任务、作业等信息。确保其数据源配置指向同一个达梦数据库。 - 数据库层面监控:关注达梦数据库的慢查询日志、锁等待情况。Flowable某些历史查询(如按变量查询历史实例)可能产生复杂SQL,需要确保相关表上有合适的索引。你可以使用达梦的管理工具(如管理控制台)或通过
v$sessions、v$sql_history等动态性能视图进行监控。 - 应用日志:确保Flowable的日志级别(如
logging.level.org.flowable=DEBUG)在测试环境适当调高,便于排查问题。但在生产环境要谨慎,避免日志量过大。
5. 总结与核心建议
回顾整个Flowable适配达梦数据库的过程,其核心思想是“借道Oracle,补齐差异”。技术路径是清晰的,但魔鬼藏在细节里。
给后来者的几条核心建议:
- 测试驱动,循序渐进:不要试图一次性在生产环境完成切换。建立完整的测试环境,从单元测试(流程引擎操作)到集成测试(完整业务流程),再到性能压测,逐步验证。
- 权限问题是第一道拦路虎:至少80%的启动失败都与数据库用户权限不足有关。严格按照达梦的权限体系,授予连接用户(Schema)RESOURCE权限和SELECT ON SYS.DUAL权限,这是基础中的基础。
- 驱动版本至关重要:始终使用达梦官方推荐的最新稳定版JDBC驱动。不同版本的驱动在兼容性、性能和Bug修复上差异很大。我们曾因使用旧版驱动遭遇过一个连接泄露的疑难问题,升级后迎刃而解。
- 不要忽视连接池配置:
connection-test-query必须正确设置。根据应用负载仔细调优连接池参数,避免连接数不足或泄露。 - 做好SQL审计:在测试阶段,开启Flowable的DEBUG级别SQL日志,观察所有执行的SQL语句。一旦发现报错,能快速定位到是哪个Mapper的哪条SQL出了问题,这是判断是适配问题还是业务逻辑问题的关键。
- 社区与官方资源:遇到问题时,除了搜索,可以查看Flowable的GitHub仓库Issue,看是否有其他人遇到过类似问题。达梦官方也会提供一些与主流开源框架的适配指南,值得参考。
最后,我想说的是,国产化适配不仅仅是技术任务,更是对团队工程能力和耐心的考验。它要求我们不仅会“用”框架,还要在一定程度上“懂”框架。通过这次Flowable适配达梦的经历,我们团队对Flowable的数据库抽象层、事务管理有了更深的理解,这种收获远超出了完成一个迁移任务本身。当你看到自己熟悉的业务流程在全新的国产基础软件上顺畅运行时,那种成就感,就是对所有折腾最好的回报。