Activiti核心表ACT_RE_PROCDEF拆解:流程定义如何驱动工作流

📅 发布时间:2026/10/17 4:32:07
Activiti核心表ACT_RE_PROCDEF拆解:流程定义如何驱动工作流
做流程引擎集成的人第一关基本都是被Activiti那25张表绕晕。ACT_GE_BYTEARRAY、ACT_HI_ACTINST、ACT_RU_IDENTITYLINK……光看前缀就够喝一壶。我的建议始终是别急着背全表先抓三张主表ACT_RE_PROCDEF看流程模板ACT_RU_EXECUTION看运行现场ACT_HI_PROCINST看历史轨迹。把这三张串起来整个工作流引擎的骨架就立住了。今天这一篇先把第一张ACT_RE_PROCDEF流程定义表彻底拆开。ACT_RE_PROCDEF属于RERepository仓库模块记录的是流程定义的元数据。你在设计器里画好的BPMN流程图部署进引擎后会被解析然后把“这个流程叫什么、key是什么、是第几个版本、从哪个文件来的、当前能不能启动”这些信息写进这张表。后续每次调用runtimeService.startProcessInstanceByKey启动流程引擎都要先到这张表查出对应记录再据此创建流程实例。所以这张表就是流程模板的“登记簿”没有它一切流程都跑不起来。这篇文章适合三类人细读第一次集成Activiti、部署后对着空表发愁的新手生产环境流程启动报错、急着定位是版本问题还是挂起问题的排障者以及对工作流引擎做二次开发、想理清内部数据模型的进阶开发者。我会先交代25张表的整体分类再把ACT_RE_PROCDEF的字段、生命周期、常用SQL和排障经验逐个讲透尽量讲清楚每个设计背后的原因而不是只给你一张字段表。1. 先摸清Activiti 25张表的家底1.1 按前缀分组整套表就没那么吓人Activiti所有表都遵循“ACT_前缀 业务域”的命名规则。前缀拆开就是含义前缀全称含义代表表核心作用ACT_GE_*General 通用ACT_GE_BYTEARRAY、ACT_GE_PROPERTY二进制内容、引擎全局属性ACT_RE_*Repository 仓库ACT_RE_DEPLOYMENT、ACT_RE_MODEL、ACT_RE_PROCDEF部署、模型、流程定义等静态资源ACT_RU_*Runtime 运行时ACT_RU_EXECUTION、ACT_RU_TASK、ACT_RU_VARIABLE、ACT_RU_JOB等流程实例运行期的执行流、任务、变量、作业ACT_HI_*History 历史ACT_HI_PROCINST、ACT_HI_TASKINST、ACT_HI_ACTINST、ACT_HI_VARINST等已结束或进行中实例的审计数据ACT_ID_*Identity 身份ACT_ID_USER、ACT_ID_GROUP、ACT_ID_MEMBERSHIP、ACT_ID_INFO用户、组、成员关系、账号信息这套命名不是随便起的。Java端源码里每个域都有对应的EntityManager比如ProcessDefinitionEntityManager对应ACT_RE_PROCDEFExecutionEntityManager对应ACT_RU_EXECUTION。看到前缀就能猜出表的大致职责这是最快的记忆方式。这里多说一句不同小版本的25张表会有点出入。比如Activiti 5.x里作业表通常就是ACT_RU_JOB一张到6.x以后才拆出ACT_RU_TIMER_JOB、ACT_RU_DEADLETTER_JOB、ACT_RU_SUSPENDED_JOB、ACT_RU_HISTORY_JOB这些表。你拿着5.x的文档去查6.x的库少几张表很正常先认前缀再看版本。之所以先强调RE组是因为所有流程实例都是从定义来的而ACT_RE_PROCDEF就是RE组里最核心的那张表。把这张表研究透后面看运行表和历史表都会顺很多。1.2 ACT_RE_PROCDEF 在RE组里的定位RE组内部一共三张表职责差别很大。ACT_RE_DEPLOYMENT记录一次部署动作你上传单个BPMN文件也好、上传zip包也好都会生成一个全局部署ID挂在它下面。ACT_RE_MODEL存的是设计器里保存的模型数据偏向“设计态”。ACT_RE_PROCDEF则是在部署动作完成以后引擎解析BPMN得到的“运行态模板”。用一个生活化的类比BPMN文件相当于一份户型图纸ACT_RE_DEPLOYMENT是“图纸归档记录”ACT_RE_PROCDEF是根据图纸烧制好的模型。以后每个流程实例都是按这个模型“打印”出来的成品。一次部署可以包含多张图纸所以一个DEPLOYMENT_ID下可能解析出多条流程定义记录同一个流程key反复部署又会生成多个版本的PROCDEF记录。理解了这层关系再看字段就不容易糊涂。2. ACT_RE_PROCDEF 字段逐个拆解2.1 ID_ 和 REV_主键和乐观锁决定并发安全ID_是表主键默认形如leave:1:125004由流程key、版本号和引擎内部生成的ID拼接而成。为什么这么设计因为这个主键人眼可读性极强——看到leave:3:17502不用join任何表就知道这是请假流程第3版排查问题非常省事。而且“key:version”天然唯一再拼一个内部序号基本不存在重复的可能。实际SQL里经常能看到直接用ID_字符串做关联条件这也是Activiti生态里的常见写法。REV_是乐观锁字段初始值为1每次UPDATE都会把旧值带上再自增。引擎执行更新时实际SQL是UPDATE ACT_RE_PROCDEF SET ... WHERE ID_? AND REV_?如果影响行数为0就说明这条记录已被其他事务改过直接抛OptimisticLockingException。这个机制避免了两边同时挂起同一个流程定义导致状态互相覆盖的问题。所以看到这类异常别慌先排查是不是两个管理端窗口在同时对同一条流程定义做操作业务代码里加互斥锁或者失败重试比手工改数据库REV_靠谱得多。2.2 KEY_、NAME_、VERSION_、CATEGORY_流程定义的身份证KEY_是流程定义的核心业务标识对应BPMN文件里process idleave的id不是文件名。这一点很多人栽过跟头文件名可能叫leave.bpmn20.xml但里面process元素的id是leaveProcess那KEY_就是leaveProcess。启动流程时调startProcessInstanceByKey(leave)引擎找不到定义直接报“流程定义不存在”。所以拿到一个流程文件先打开看process的id别凭文件名猜KEY_。NAME_是流程展示名称取自process的name属性比如“请假流程”。这个字段不参与引擎逻辑主要是给人看的。VERSION_是版本号同一KEY下第一次部署是1第二次部署是2依次递增。引擎不会去比较两次部署的文件内容是否一致只要你调用了deploy()就会在ACT_RE_PROCDEF里新插一条更高版本的记录。这是Activiti版本机制的核心背后是“不可变历史”的设计原则已经跑起来的流程实例必须能完整追溯它启动时用的定义绝不能把旧定义覆盖掉。CATEGORY_默认取BPMN根节点targetNamespace的值很多公司拿它做流程分类或应用归属统计。如果你部署时通过DeploymentBuilder设置了category实际存储以设置值为准。但要注意模板的targetNamespace如果都是同一个地址那么表里所有流程定义的CATEGORY_都会是同一个值做统计报表时要留意这个坑必要时在部署前统一改写。2.3 DEPLOYMENT_ID_、RESOURCE_NAME_、DGRM_RESOURCE_NAME_从定义追溯到文件DEPLOYMENT_ID_指向ACT_RE_DEPLOYMENT.ID_。一次部署包含多个流程文件时多个流程定义的DEPLOYMENT_ID_相同反过来一个流程定义必然只属于一次部署。排查问题的时候从PROCDEF出发找到DEPLOYMENT_ID_再顺着部署ID去ACT_GE_BYTEARRAY里捞原始文件这是一条非常顺的链路。RESOURCE_NAME_是被解析的BPMN资源名通常是classpath下的相对路径比如processes/leave.bpmn20.xml。DGRM_RESOURCE_NAME_是配套的流程图资源名比如processes/leave.leave.png。如果部署包没带图片DGRM_RESOURCE_NAME_一般为NULL。注意这两个字段只存名字不存文件内容。文件内容的二进制字节都存在ACT_GE_BYTEARRAY表里靠DEPLOYMENT_ID_和NAME_关联去取。实际开发里要下载某条流程定义的原始XML或者把流程图展示到前端页面上靠的就是这组字段。2.4 描述与状态辅助字段DESCRIPTION_、HAS_START_FORM_KEY_、HAS_GRAPHICAL_NOTATION_、SUSPENSION_STATE_、TENANT_ID_、ENGINE_VERSION_DESCRIPTION_存流程描述一般从BPMN文档元素提取。这个字段有长度限制别往里塞大段JSON短描述放这里没问题复杂扩展信息建议放到业务表。HAS_START_FORM_KEY_标记流程是否配置了启动表单1表示startEvent设置了formKey引擎在启动流程前可能先拉取表单定义0表示直接启动即可。判断一个流程是否要“先弹表单再发起”看这个字段最直接。HAS_GRAPHICAL_NOTATION_标记定义是否包含图形坐标信息。部署包带PNG或者BPMN文件里嵌了DIDiagram Interchange信息这个值才是1。想判断能不能给用户展示流程图先看这个字段省得每次都去读字节再尝试转图片。SUSPENSION_STATE_是流程定义状态1为激活2为挂起。挂起后的定义不能用来启动新实例但是已经跑起来的实例不受影响。TENANT_ID_是多租户标识默认是NULL。如果你做SaaS平台部署时通过tenantId区分租户那所有查询都必须在API上传租户ID否则很可能查不到定义。ENGINE_VERSION_是引擎内部版本标记一般不用碰。字段在不同版本里有细微出入。比如早期版本可能没有TENANT_ID_和ENGINE_VERSION_升级时会由官方脚本自动补列。3. 流程定义的生命周期从部署到被引用3.1 部署一个流程时引擎在表里到底做了什么以最常见的单文件部署为例代码就三行repositoryService.createDeployment() .name(请假流程) .addClasspathResource(processes/leave.bpmn20.xml) .deploy();就这么一句deploy()引擎内部做了几件大事。第一步读取XML并解析成BpmnModel校验节点、连线、事件定义是否合法。第二步把原始XML字节写入ACT_GE_BYTEARRAYNAME_是processes/leave.bpmn20.xmlDEPLOYMENT_ID_联到新生成的部署ID。第三步在ACT_RE_DEPLOYMENT插一条部署记录。第四步遍历BpmnModel里所有process元素逐个写入ACT_RE_PROCDEF——首次部署VERSION_1第二次部署同key就变成2、3、4这么叠上去。第五步触发流程定义相关的监听器。部署完成后你可以直接查表验证SELECT ID_, KEY_, NAME_, VERSION_, DEPLOYMENT_ID_, RESOURCE_NAME_, SUSPENSION_STATE_ FROM ACT_RE_PROCDEF WHERE KEY_ leave ORDER BY VERSION_ DESC;我习惯在测试环境每次部署后都跑一遍这条SQL确认版本号和资源名都对了再继续。这里要特别强调一个理念流程定义记录是“只读模板”。想改流程逻辑不能直接UPDATE这条记录只能重新部署一份新版本BPMN。这个设计深远地影响了后续所有操作——版本会越积越多旧实例要留定义快照生产环境不能随随便便物理删除PROCDEF。3.2 设计态模型与运行态定义的关系ACT_RE_MODEL放的是设计器里保存的模型数据模型本身的JSON/XML源文件也一样塞在ACT_GE_BYTEARRAY里。模型通过一次“部署”动作才会生成对应的ACT_RE_PROCDEF记录。同一个模型可以被反复部署每部署一次PROCDEF就多一个高版本。所以模型和定义不是一一对应而是“一对多”的关系反过来直接从XML部署的流程可能压根没有MODEL记录。做流程平台时经常遇到“模型修改后重新部署”的操作。正确的做法是先更新模型元数据再调一次repositoryService完成部署让引擎生成新版本定义。旧版本定义不建议立刻物理删除除非你已经确认所有关联实例都结束且不需要历史追溯。实际项目里我见过有人图省事直接在数据库里删PROCDEF记录结果历史流程详情页打不开审批记录里的流程轨迹全部断链。这个代价比留着几行旧数据大得多。3.3 版本机制一个KEY、多个VERSION启动到底用哪个默认情况下startProcessInstanceByKey会使用该KEY下版本号最大的那条定义。引擎内部的查询逻辑大致是按KEY_匹配按VERSION_降序取第一条同时SUSPENSION_STATE_必须是激活。但有个容易被忽视的细节如果你把最新版本挂起了启动流程会直接报“流程定义被挂起”引擎不会自动回退到上一个激活版本。所以线上升级流程时想保留旧版本供紧急回退业务代码里一定要显式指定processDefinitionId而不是依赖默认最新版。我在实际项目里会在业务单据表增加一个processDefinitionId字段用户发起流程时用这个ID启动。这样就算新版本发布后出了严重问题存量单据还能按原版本继续走不用全员卡死。3.4 挂起与激活的实战场景挂起和激活通过repositoryService操作// 挂起指定key的所有版本定义cascadetrue会级联挂起运行中的实例 repositoryService.suspendProcessDefinitionByKey(leave, true, null); // 激活 repositoryService.activateProcessDefinitionByKey(leave, true, null);挂起定义本身已存在的运行实例不受影响新实例进不来。cascade参数一旦传true正在跑的实例也会被挂起用户提交任务直接报错这个操作相当危险。生产环境常见的做法是先只挂起定义把新入口堵住再人工评估存量实例能正常走完的让它走完走不完的单独处理。等确认没问题了再激活定义恢复入口。SUSPENSION_STATE_就在1和2之间切换查询可用定义时别忘了过滤这个字段。4. 典型场景实操用SQL和API操作ACT_RE_PROCDEF4.1 查询当前可用的最新版本定义日常工作里最常用的就是“查某个key的当前版本”。SQL写法SELECT p.ID_, p.KEY_, p.NAME_, p.VERSION_, p.DEPLOYMENT_ID_, p.RESOURCE_NAME_, p.DGRM_RESOURCE_NAME_, p.SUSPENSION_STATE_ FROM ACT_RE_PROCDEF p WHERE p.KEY_ leave AND p.SUSPENSION_STATE_ 1 ORDER BY p.VERSION_ DESC LIMIT 1;用API更直观ProcessDefinition pd repositoryService.createProcessDefinitionQuery() .processDefinitionKey(leave) .latestVersion() .singleResult();这里有个坑latestVersion()只按版本号最大取一条不会自动过滤挂起状态。如果最新版被挂起了你还能查到它但启动时会报错。所以业务代码里最好再补一个.suspended()条件或者干脆自己指定processDefinitionId。4.2 根据部署ID捞BPMN原始内容要下载流程定义的原始XML光查PROCDEF不够得join部署表和字节数组表SELECT p.ID_ AS PROC_DEF_ID, p.KEY_, p.VERSION_, d.ID_ AS DEPLOYMENT_ID, b.NAME_ AS RESOURCE_NAME, b.BYTES_ FROM ACT_RE_PROCDEF p LEFT JOIN ACT_RE_DEPLOYMENT d ON p.DEPLOYMENT_ID_ d.ID_ LEFT JOIN ACT_GE_BYTEARRAY b ON b.DEPLOYMENT_ID_ d.ID_ WHERE p.ID_ leave:3:17502;ACT_GE_BYTEARRAY里的BYTES_字段是blob不同数据库驱动返回的类型不一样。要在Java里读直接走API更省事InputStream is repositoryService.getResourceAsStream(deploymentId, resourceName);只要拿到DEPLOYMENT_ID_和RESOURCE_NAME_两行代码就能把原始文件读出来。我通常把这段逻辑封装成“流程定义详情查询”的接口前端点开某个版本就能看到XML和高亮流程图。4.3 通过API动态部署并设置分类、租户实际项目中更多是上传zip包动态部署DeploymentBuilder builder repositoryService.createDeployment() .name(月度发布) .addZipInputStream(new ZipInputStream(file.getInputStream())) .tenantId(租户A); if (StringUtils.isNotBlank(category)) { builder.category(category); } Deployment deployment builder.deploy();addZipInputStream会把zip里的多个BPMN和PNG一起解压注册。这个场景下ACT_RE_PROCDEF会为zip里每个流程文件分别生成一条记录共用一个DEPLOYMENT_ID。设置tenantId后PROCDEF的TENANT_ID_字段就有值了。后面所有查询要是忘了带租户条件大概率查不到任何流程定义。我建议把租户ID设计成必传参数而不是默认查全部这样才能避免SAAS环境下串数据。4.4 读流程图HAS_GRAPHICAL_NOTATION_与BYTEARRAY的配合展示流程图是工作流系统的标配功能。很多部署包没带PNGActiviti引擎会基于BPMN里的DI坐标自动生成一张图。自动生成的图能用但长得很简陋审批人节点、网关颜色都不可控。所以我的建议是凡是需要给用户看流程图的业务部署包一定要带上设计器导出的PNG并且保证DGRM_RESOURCE_NAME_字段有值。读取图片的推荐顺序是先看HAS_GRAPHICAL_NOTATION_是否为1再去ACT_GE_BYTEARRAY按资源名取字节最后转成图片流输出。不要每次请求都现查现读然后解析图片那样数据库开销和IO开销都大可以把解析结果缓存到本地或Redis流程定义不变就复用缓存。5. 常见问题与排查技巧实录5.1 部署成功但ACT_RE_PROCDEF查不到数据这是新手最高频的疑问。明明代码没报错控制台还打印了部署成功结果一查PROCDEF是空的。按顺序排查首先查ACT_RE_DEPLOYMENT有没有记录没有就说明部署动作压根没落库。其次查ACT_GE_BYTEARRAY有没有字节数据可能是文件没被扫描到比如资源路径写错、zip包内嵌目录不对。再检查是不是多租户模式API查询没传tenantId默认过滤掉了。还要确认你连的是不是同一个库我见过本地配置连了测试库、拿生产库去查的哭笑不得案例。另外一个隐蔽原因BPMN解析失败。如果流程文件不是标准bpmn20.xml或者包含了引擎无法识别的扩展元素部署阶段可能抛异常并整体回滚表面上看没报错实际事务什么都没写。这时候要打开引擎日志找“Error while parsing process definition”之类的关键行。5.2 启动流程报“process definition does not exist”这个报错信息在英文环境里可能是“no process definition found with key”。按优先级排查以下原因第一KEY_写错了。KEY_取自BPMN里process的id不是文件名也不是流程显示名。第二流程定义被挂起。挂起的定义虽然还能查到但启动会被拒绝。第三多租户下没传tenantId引擎把租户条件加上以后匹配不到。第四定义被删除了。如果部署记录被级联删除PROCDEF也会跟着消失。排查时先跑这条SQLSELECT ID_, KEY_, VERSION_, SUSPENSION_STATE_, TENANT_ID_ FROM ACT_RE_PROCDEF WHERE KEY_ 你填的key;有数据再看SUSPENSION_STATE_和TENANT_ID_十有八九问题就浮出水面了。5.3 删除部署的连带影响repositoryService.deleteDeployment(deploymentId, true)第二个参数传true表示级联删除。级联删除会连带删掉PROCDEF、BYTEARRAY以及与该部署相关的运行实例数据操作非常危险。即使不传true只删部署PROCDEF记录通常也会被一并清掉因为流程定义不能脱离部署独立存在。但在删除之前必须确认没有正在运行的流程实例SELECT COUNT(*) FROM ACT_HI_PROCINST WHERE PROC_DEF_ID_ leave:1:125004 AND END_TIME_ IS NULL;这个统计口径用ACT_HI_PROCINST更稳妥。ACT_RU_EXECUTION里既有主执行流也有子执行流直接用PROC_DEF_ID_统计很容易把同一个实例重复算好几遍误导判断。确认结果大于0的情况下别删。5.4 版本不断累积表膨胀怎么办长时间运行的流程平台ACT_RE_PROCDEF会随着每次发布不断变多。有些公司一年发几百次版两年下来这张表几千条是少的几十万条也不稀奇。我见过一张PROCDEF膨胀到几百万行的库直接拖垮了流程定义查询接口。合理的清理策略是保留每个KEY最近的三到五个版本把更老的版本归档。第一步查老版本有没有运行中的实例第二步把老版本的部署包和XML内容存到对象存储做离线归档第三步删除对应的ACT_RE_PROCDEF记录、ACT_RE_DEPLOYMENT记录、ACT_GE_BYTEARRAY字节。这套流程建议做成定时任务每个月跑一次而不是等线上卡到不行再手工清。归档时注意保留历史实例的轨迹数据历史表ACT_HI_PROCINST仍然需要能查到流程轨迹只是不能再依赖PROCDEF这张表做实时联动。5.5 版本升级导致表结构兼容问题Activiti从5.x升到6.xACT_RE_PROCDEF会新增字段比如ENGINE_VERSION_可能还会调整部分字段长度。如果在生产库上直接拿新版本引擎跑旧表结构最常见的报错是“Unknown column”或者“Column not found”。反过来旧版本代码连新库也可能出现查询时找不到列的问题。处理办法只有一个按官方升级脚本逐版本执行不能跳版本更不能图省事手工ALTER TABLE。升级前先在测试环境完整跑一遍所有版本的脚本模拟全量数据迁移确认查询性能和数据兼容性都没问题再上生产。我见过有人跳版本直接改库结果历史数据里的ID_格式对不上导致流程详情无法关联那个排查过程比写业务代码痛苦多了。最后再分享一点个人体会。这套表我前前后后查了很多次真正让技术方案变清晰的不是把字段背下来而是想通ACT_RE_PROCDEF和业务系统之间的关系。很多项目一开始接入流程引擎出现启动找不到定义、版本混乱的问题本质上都是没理解这张表是“只读模板”更新流程只能走新版本部署。把这条主线想通后面看ACT_RU_EXECUTION、ACT_HI_PROCINST这些表都会顺很多。如果你还在研究阶段建议直接在测试库部署一个最简单的Demo反复对ACT_RE_PROCDEF做部署、挂起、激活、删除操作配合本文的SQL看数据变化比自己闷头翻文档快十倍。