一、JBPM4对于流程版本的管理
JBPM4对于流程版本的管理有着清晰、完整的定义和安全、良好的运行机制。根据OA开发中的测试可以看出,流程设计器每发布一次,即JBPM4每部署一次,即会在jbpm4_deployment(一条记录)、jbpm4_deployprop(4条记录,存储一次发布的相关属性)、jbpm4_lob(一条记录,存储jpdl文件)增加记录。并通过jbpm4_task(jbpm4_hist_task)中的DBVERSION_、jbpm4_execution(jbpm4_hist_procinst)以及其他相关表中的PROCDEFID和DB VERSION等控制不同版本流程的正常运转。
以下是三张版本控制的核心表:
1.1Jbpm4_lob
字段释义:
DBID_:数据库标识;
DBVERSION_:流程版本号;每发布一次递增1;
BLOB_VALUE_:流程定义文件;
DEPLOYMENT_:部署id;全局唯一;
NAME_:流程名;
1.2Jbpm4_deployment
字段释义:
DBID_:数据库标识;
DBVERSION_:流程版本号;
TIMESTAMP_:部署时间;
STATE_:是否有效;
NAME_:流程名;
1.3Jbpm_deployprop
字段释义:
DBID_:数据库标识;
DEPLOYMENT_:流程部署id;
OBJNAME_:流程名称;
KEY_:属性名:langid(jpdl版本);pdkey(流程名);pdversion(流程版本);pdid(流程唯一标识:流程名-流程版本)
STRINGVAL_:属性值;
在jbpm4的规范中流程定义不应该改变,因为预测流程变化带来的所有可能的影响是非常困难的(或者说是不可能的)。JBPM4不支持修改已经部署的流程定义,这也是不能提供的操作,试想几种情况之后就不能发现这是很明智的选择:如果当前有流程实例,原流程有5个步骤要去,走到一半,流程被修改成了7个步骤,中间的活动可能已经被改变了,那么就可能出现问题;或者走到了第4步的时候,流程被修改成了只有3步,那怎么办?所以不支持修改流程定义不仅没有对工作造成影响,还很大程度地保障了原先流程实例的正确执行,以及不对历史中的流程实例造成影响.
但流程的更新、优化或迭代在日常应用中非常常见,如果解决相同流程(实际是名称相同,而定义不同;定义之间的区别可以是微小的,甚至是完全不同的,比如定义中一个是请假单,一个是加班单,如果名称统一为请假单,那么在JBPM认为二者是相同流程)不同版本之间的协调、统一和安全、健壮的运行成为突出而重要的问题。围绕这个问题,JBPM4有一个明智的流程版本机制。版本机制允许在数据库中多个同名流程定义共存,流程实例以当时的最新版本来启动(默认情况下,如指定deployId或流程名+version则以新版本启动),并且在它的整个生命周期中将保持以相同的流程定义执行。当一个新的版本被部署,新的流程实例以新版本启动,而老的流程实例则以老的流程定义继续执行。
当一个流程被部署时,将在JBPM数据库中创建一个流程定义,流程定义基于流程定义的名称被版本化。当一个被命名的流程被部署,部署器将分配一个版本号。为了分配版本号,部署器将查询同名流程定义的最高版本号,并且在其上加1,未命名的流程定义其版本号总是-1。同一流程名只能重新部署一个流程,如果和已有的流程的名字有冲突,将自动把版本号加1,以视区别.在这种情况下,就要保证以后开始的流程实例都是最新的流程实例。
由以上描述可以看出,jbpm4提供了相应的版本管理机制。当某一流程的最新版本发布后,新的流程实例默认以最新版本流程发起,而老的流程实例仍以老的流程版本运行。
二、目前OA对于版本管理的现状
2.1OA版本管理现状
根据以上分析,jbpm4的自有机制已充分保证了版本之间的协调和统一。在jbpm4的管理中流程名(而非OA中的processDefineId)与deploymentId可以唯一确定一个流程定义,每一流程的发起、运行和任务绑定其deployId或流程名+version可以有效区分相同流程的不同版本。在OA目前应用中,在流程的发起、运行和任务分配时均未绑定deployId,有个别需要使用deployId的代码则采用processDefinService.queryById(String processDefineId).getDeploymentId的方式获取,而这方式获取的始终是流程设计器最新版本的deploymentId。即便OA中存储了版本信息,流程流转过程中仍然完全抛弃了版本控制,在发起、运行的页面只存储了processDefineId、taskId甚至有formDefineId、realTableName等,但唯独没有deployId,在FlowRunTime(流程运行中所需参数的封装类型)中也没有为deployId预留。存在这样一种可能,通过某种方法发起了老版本流程,在运行过程中仍然采用新版本流程控制其流转,这样必然会产生错误。这是OA开发早期工作流设计需求不清晰(未考虑优化、迭代更新版本)和对jbpm4版本管理理解不够(对版本概念不了解或不重视)造成的。
但在流程设置的开发中,我们已经有了流程定义id+deployId唯一确定一个流程的意识,所以在流程设置(除表单权限)中都增加了deployId这一字段。
2.2转正申请问题分析
根据对转正单问题在员工自评与直接主管评价之间循环的调试发现,在对流程流转过程中的择路使用了老流程(版本3)的版本号(先根据运行id,取出版本名+version,根据版本名+version取出版本,根据版本取其deployId),但老流程的版本号deployId在decision及userassign中并未配置(表中已存的deployId对应2和4),根据handler里的逻辑,在找不到对应出路的情况下,会将所有出路取出,然后取排序在前的第一个,从而将员工自评取出; 而对应取人的地方我们使用的版本号是由流程定义中取得的,而流程定义中的deployId对应最新的deployId,而这个deployId与老流程的对应节点(新老流程该节点恰好无变化)查询出新版本的decision和userassign配置,从而选出了正确的人。
代码1如下:
ProcessEngine processEngine = (ProcessEngine) SpringContextUtil.getApplicationContext().getBean("processEngine"); IDecisionRuleService decisionRuleService= (IDecisionRuleService)SpringContextUtil.getApplicationContext().getBean("decisionRuleService"); String processDefinitionId = openExecution.getProcessDefinitionId(); ProcessDefinition processDefinition = processEngine.getRepositoryService() .createProcessDefinitionQuery() .processDefinitionId(processDefinitionId) .uniqueResult(); String deploymentId = processDefinition.getDeploymentId();
代码2如下:
String deploymentId = processDefineService.queryById(flowRunTime.getProcessDefineId()).getDeploymentId();
三、解决方案
基于对工作流代码的走读和对jbpm4版本管理的学习,提出以下两种方案:
3.1简单方案
允许在有工作的情况下使用流程设计器,且不同processDefine允许对应同一formDefine,在设计器中对现有流程进行优化后,保存并发布,在t_bpm_process_define中生成一个全新实例(除processDefineId与deploymentId、version不同外,其他属性与原流程完全一致)。
优点:
1.需要调整的代码量最小,可以实现版本管理的部分功能;
2.做到了表单的重用,实现了表单数据源的一致;
缺点:
1.在OA中新老流程(在新建工作、高级查询)仍是作为单独流程对待(在OA架构下为不同流程,因processDefineId不同,但jbpm下已经是相同流程的不同版本,因流程名相同,仅deployId不同);
2.由于这种方案在OA构架下并非相同流程的不同版本,而是不同流程,而我的工作、高级查询、新建工作均基于OA架构下的版本,即数据来自OA中的t_bpm打头的流程实例和任务表,基于processDefineId和formDefineId以及process_execution和process_task,所以从OA角度只实现了部分版本管理的功能。
3.2完整方案
允许在有工作的情况下使用流程设计器,且不同processDefine允许对应同一formDefine,在设计器中对现有流程进行优化后,保存并发布后processDefineId、name不变的情况下更新deploymentId、version,同时将老的deploymentId及version存储在其他表中供老流程中未运行结束的实例使用(也可能不需要存储。因为jbpm4中已经存储了完整版本信息,包括版本定义与运行中的版本识别。而t_bpm的运行和任务只是OA中展示所用,并不控制流转,所以可能不需要版本信息,而jbpm4的运行和任务的版本信息全部来自其本身),并允许流程管理员在不同版本之间切换,即可以选定新建工作时该流程是哪一版本(这一功能则需要存储每一次部署后的版本信息)。
同时流程在发起和运行过程中均需将depolyId带入,用于发起和运行过程中的版本判定以及选人、择路,判断权限等。
优点:在OA构架下视为同一流程的不同版本,新建工作、我的工作特别是高级查询中保证了数据来源的一致(流程定义与表单定义完全相同,而deployId则在查询时透明),可以基本实现版本管理的功能;
缺点:
1.改动较大,如设计不周或测试不充分可能影响现有流程审批(包括选人、表单权限、条件设置等);
2.如老单子运转过程中,除经办人、表单权限、择路和前后置外还需要老单子的deployId,则方案可能无效;
3.需经详细走读代码、设计、编码;
4.需要严格全面测试,避免更新后现有流程出现流转不畅的现象。
3.3其他方案
仿照JBPM4的表设计模式,即实体标识与数据库标识分离,即便实体标识是全局唯一的,仍另外设置数据库标识。根据这一模式,我们可以将t_bpm_process_define表进行重构,processDefineId即现有id为processDefine的实体标识(是流程标识,非版本标识),另外设置DBID_作为其数据库标识。流程设计器优化后,即插入一条与原流程在id(此时id在本表不唯一,唯一的是DBID_)、name、formDefineId相同,而version、deployId不同的数据。同时整理流程设计页面流程树和新建工作页面流程发起入口的展示逻辑,相同流程的不同版本在流程树和新建工作页面均作为独立流程(目前应该就是这样的,展示时流程名后加deployId做区分),同时需要修改相关逻辑,即流程id+deployId唯一确定一个流程,原来调用processDefneService.queryByI(String deployId)的均需改为queryByIdAndDeployId(String id, String deployId),所以相关页面、调用需要增加获得且增加相应参数。如调用处无法提供deployId,这一方案是失败的。
在高级查询页面,相同processDefineId只显示一条,即可实现高级查询的效果,不会漏掉数据。
优点:
1.可以实现完整版本控制;
缺点:
1.改动可能比3.2更多;
2.如运转过程中需要获得流程信息而deployId无法获得(可能性较小,因为发起时可以将deployId同processDefineId一样始终带入),则方案失败;
3.缺点同3.2;
3.4建议方案:
因提出版本概念,主要是为了新老流程之间的无缝对接,特别是在高级查询这一块。建议选用3.2。