一.设计流程定义文档
1.流程图
BPMN 2.0根节点是definitions节点。 这个元素中,可以定义多个流程定义(不过我们建议每个文件只包含一个流程定义, 可以简化开发过程中的维护难度)。 一个空的流程定义看起来像下面这样。注意,definitions元素 最少也要包含xmlns 和 targetNamespace的声明。 targetNamespace可以是任意值,它用来对流程实例进行分类。
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"> <!-- 流程定义 --> <process id="leave" name="请假流程" isExecutable="true"> <!-- 开始节点 --> <startEvent id="startevent1" name="Start"></startEvent> <!-- 结束节点 --> <endEvent id="endevent1" name="End"></endEvent> <!-- 任务定义(assignee:业务办理人)--> <userTask id="usertask1" name="提交申请" activiti:assignee="张三"></userTask> <userTask id="usertask2" name="审批【部门经理】" activiti:assignee="李四"></userTask> <userTask id="usertask3" name="审批【总经理】" activiti:assignee="王五"></userTask> <!-- 任务连线 --> <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow> <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow> <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow> <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow> </process> <!-- 流程定义相关坐标定义 --> <bpmndi:BPMNDiagram id="BPMNDiagram_helloworld"> <bpmndi:BPMNPlane bpmnElement="helloworld" id="BPMNPlane_helloworld"> <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1"> <omgdc:Bounds height="35.0" width="35.0" x="330.0" y="20.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1"> <omgdc:Bounds height="35.0" width="35.0" x="330.0" y="380.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1"> <omgdc:Bounds height="55.0" width="105.0" x="295.0" y="100.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2"> <omgdc:Bounds height="55.0" width="105.0" x="295.0" y="200.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3"> <omgdc:Bounds height="55.0" width="105.0" x="295.0" y="290.0"></omgdc:Bounds> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1"> <omgdi:waypoint x="347.0" y="55.0"></omgdi:waypoint> <omgdi:waypoint x="347.0" y="100.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2"> <omgdi:waypoint x="347.0" y="155.0"></omgdi:waypoint> <omgdi:waypoint x="347.0" y="200.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3"> <omgdi:waypoint x="347.0" y="255.0"></omgdi:waypoint> <omgdi:waypoint x="347.0" y="290.0"></omgdi:waypoint> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4"> <omgdi:waypoint x="347.0" y="345.0"></omgdi:waypoint> <omgdi:waypoint x="347.0" y="380.0"></omgdi:waypoint> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </definitions>
说明:流程定义文档有两部分组成:
1)bpmn文件
流程规则文件。在部署后,每次系统启动时都会被解析,把内容封装成流程定义放入项目缓存中。Activiti框架结合这个xml文件自动管理流程,流程的执行就是按照bpmn文件定义的规则执行的,bpmn文件是给计算机执行用的。
2)展示流程图的图片
在系统里需要展示流程的进展图片,图片是给用户看的。
二、部署流程定义(classpath路径加载文件)
/**部署流程定义:信息写入 * act_re_deployment表(部署信息表), * act_re_procdef表(流程定义数据表) * act_ge_bytearray(资源文件表),存储流程资源 文件 * act_ge_property(主键生成策略表),初始化时默认插入三条数据 * next.dbid:下一个dbid * schema.history:方案历史 * schema.version:方案版本 */ @Test public void deployeementProcessDefinition() { /**Deployment:部署对象*/ /**这里使用RepositoryService部署流程定义 */ Deployment deployement = processEngine.getRepositoryService() /** 创建一个部署对象*/ .createDeployment() /**添加部署的名称*/ .name("请假流程") /**从classpath的资源中加载,一次只能加载一个文件*/ .addClasspathResource("diagrams/leave.bpmn") .addClasspathResource("diagrams/leave.png") /**完成部署*/ .deploy(); /** * 部署ID:1 * 部署名称:请假流程 */ System.out.println("部署ID:" + deployement.getId()); System.out.println("部署名称:" + deployement.getName()); }
说明:
1)先获取流程引擎对象:在创建时会自动加载classpath下的activiti.cfg.xml
/**
* 获取流程引擎
* ProcessEngine:在Activiti中最核心的类,其他的类都是由他而来
*/
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
2)首先获得默认的流程引擎,通过流程引擎获取了一个RepositoryService对象(仓库对象)
3)由仓库的服务对象产生一个部署对象配置对象,用来封装部署操作的相关配置。
4)这是一个链式编程,在部署配置对象中设置显示名,上传流程定义规则文件
5)向数据库表中存放流程定义的规则信息。
6)这一步在数据库中将操作三张表:
a)act_re_deployment(部署对象表)
存放流程定义的显示名和部署时间,每部署一次增加一条记录
b)act_re_procdef(流程定义表)
存放流程定义的属性信息,部署每个新的流程定义都会在这张表中增加一条记录。
注意:当流程定义的key相同的情况下,使用的是版本升级
c)act_ge_bytearray(资源文件表)
存储流程定义相关的部署信息。即流程定义文档的存放地。每部署一次就会增加两条记录,一条是关于bpmn规则文件的,一条是图片的(如果部署时只指定了bpmn一个文件,activiti会在部署时解析bpmn文件内容自动生成流程图)。两个文件不是很大,都是以二进制形式存储在数据库中。
三、部署流程定义(zip格式文件)
/**部署流程定义(从zip)*/ @Test public void deploymentProcessDefinition_zip(){ InputStream in = this.getClass().getClassLoader().getResourceAsStream("diagrams/leave.zip"); ZipInputStream zipInputStream = new ZipInputStream(in); Deployment deployment = processEngine.getRepositoryService()/**与流程定义和部署对象相关的Service*/ .createDeployment()/**创建一个部署对象*/ .name("流程定义")/**添加部署的名称*/ .addZipInputStream(zipInputStream)/**指定zip格式的文件完成部署*/ .deploy();/**完成部署*/ /** * 部署ID:101 * 部署名称:流程定义 */ System.out.println("部署ID:"+deployment.getId());// System.out.println("部署名称:"+deployment.getName());// }
将
压缩成zip格式的文件,使用zip的输入流用作部署流程定义
四、查看流程定义
查询流程定义的信息
<!--EndFragment-->
/**查询流程定义*/ /** * #re表示与流程定义相关的表 #部署对象和流程定义相关的表 select * from act_re_deployment;#部署对象表 select * from act_re_procdef;#流程定义表 select * from act_ge_bytearray;#资源文件表 select * from act_ge_property;#主键生成策略表 */ @Test public void findProcessDefinition(){ /** * 流程定义:解析.bpmn后得到的流程定义规则的信息,工作流信息就是按照流程定义的规则执行的 * ProcessDefinition:流程定义对象*/ List<ProcessDefinition> list = processEngine.getRepositoryService()/**与流程定义和部署对象相关的Service*/ .createProcessDefinitionQuery()/**创建一个流程定义的查询*/ /**指定查询条件,where条件*/ // .deploymentId(deploymentId)/**使用部署对象ID查询*/ // .processDefinitionId(processDefinitionId)/**使用流程定义ID查询*/ // .processDefinitionKey(processDefinitionKey)/**使用流程定义的key查询*/ // .processDefinitionNameLike(processDefinitionNameLike)/**使用流程定义的名称模糊查询*/ /**排序*/ .orderByProcessDefinitionVersion().asc()/**按照版本的升序排列*/ // .orderByProcessDefinitionName().desc()/**按照流程定义的名称降序排列*/ /**返回的结果集*/ .list();/**返回一个集合列表,封装流程定义*/ // .singleResult();/**返回惟一结果集*/ // .count();/**返回结果集数量*/ // .listPage(firstResult, maxResults);/**分页查询*/ if(list!=null && list.size()>0){ for(ProcessDefinition pd:list){ System.out.println("流程定义ID:"+pd.getId());/**流程定义的key+版本+随机生成数*/ System.out.println("流程定义的名称:"+pd.getName());/**对应leave.bpmn文件中的name属性值*/ System.out.println("流程定义的key:"+pd.getKey());/**对应leave.bpmn文件中的id属性值*/ System.out.println("流程定义的版本:"+pd.getVersion());/**当流程定义的key值相同的相同下,版本升级,默认1*/ System.out.println("资源名称bpmn文件:"+pd.getResourceName()); System.out.println("资源名称png文件:"+pd.getDiagramResourceName()); System.out.println("部署对象ID:"+pd.getDeploymentId()); System.out.println("#########################################################"); } } }
结果:
流程定义ID:leave:1:4
流程定义的名称:请假流程
流程定义的key:leave
流程定义的版本:1
资源名称bpmn文件:diagrams/leave.bpmn
资源名称png文件:diagrams/leave.png
部署对象ID:1
#########################################################
再部署一次运行结果
流程定义ID:leave:1:4
流程定义的名称:请假流程
流程定义的key:leave
流程定义的版本:1
资源名称bpmn文件:diagrams/leave.bpmn
资源名称png文件:diagrams/leave.png
部署对象ID:1
#########################################################
流程定义ID:leave:2:104
流程定义的名称:请假流程
流程定义的key:leave
流程定义的版本:2
资源名称bpmn文件:leave.bpmn
资源名称png文件:leave.png
部署对象ID:101
#########################################################
可以看到流程定义的key值相同的情况下,版本是从1开始逐次升级的
流程定义的Id是【key:版本:生成ID】
说明:
1)流程定义和部署对象相关的Service都是RepositoryService。
2)创建流程定义查询对象,可以在ProcessDefinitionQuery上设置查询的相关参数
3)调用ProcessDefinitionQuery对象的list方法,执行查询,获得符合条件的流程定义列表
4)由运行结果可以看出:
Key和Name的值为:bpmn文件process节点的id和name的属性值
<process id="leave" name="请假流程" isExecutable="true">
5)key属性被用来区别不同的流程定义。
6)带有特定key的流程定义第一次部署时,version为1。之后每次部署都会在当前最高版本号上加1
7)Id的值的生成规则为:{processDefinitionKey}:{processDefinitionVersion}:{generated-id}, 这里的generated-id是一个自动生成的唯一的数字
8)重复部署一次,deploymentId的值以一定的形式变化
规则act_ge_property表生成
五、 删除流程定义
删除部署到activiti中的流程定义。
/**删除流程定义*/ @Test public void deleteProcessDefinition(){ /**使用部署ID,完成删除*/ String deploymentId = "201"; /** * 不带级联的删除 * 只能删除没有启动的流程,如果流程启动,就会抛出异常 */ // processEngine.getRepositoryService()// // .deleteDeployment(deploymentId); /** * 级联删除 * 不管流程是否启动,都能可以删除 */ processEngine.getRepositoryService() .deleteDeployment(deploymentId, true); System.out.println("删除成功!"); }
说明:
1)因为删除的是流程定义,而流程定义的部署是属于仓库服务的,所以应该先得到RepositoryService
2)如果该流程定义下没有正在运行的流程,则可以用普通删除。如果是有关联的信息,用级联删除。项目开发中使用级联删除的情况比较多,删除操作一般只开放给超级管理员使用。
六、获取流程定义文档的资源(查看流程图附件)
查询出流程定义文档。主要查的是图片,用于显示流程用。
/**查看流程图 * @throws IOException */ @Test public void viewPic() throws IOException{ /**将生成图片放到文件夹下*/ String deploymentId = "101"; /**获取图片资源名称*/ List<String> list = processEngine.getRepositoryService() .getDeploymentResourceNames(deploymentId); /**定义图片资源的名称*/ String resourceName = ""; if(list!=null && list.size()>0){ for(String name:list){ if(name.indexOf(".png")>=0){ resourceName = name; } } } /**获取图片的输入流*/ InputStream in = processEngine.getRepositoryService()// .getResourceAsStream(deploymentId, resourceName); /**将图片生成到D盘的目录下*/ File file = new File("D:/"+resourceName); /**将输入流的图片写到D盘下*/ FileUtils.copyInputStreamToFile(in, file); }
说明:
1)deploymentId为流程部署ID
2)resourceName为act_ge_bytearray表中NAME_列的值
3)使用repositoryService的getDeploymentResourceNames方法可以获取指定部署下得所有文件的名称
4)使用repositoryService的getResourceAsStream方法传入部署ID和资源图片名称可以获取部署下指定名称文件的输入流
5)最后的有关IO流的操作,使用FileUtils工具的copyInputStreamToFile方法完成流程流程到文件的拷贝,将资源文件以流的形式输出到指定文件夹下
七、附加功能:查询最新版本的流程定义
/***附加功能:查询最新版本的流程定义*/ @Test public void findLastVersionProcessDefinition(){ List<ProcessDefinition> list = processEngine.getRepositoryService()// .createProcessDefinitionQuery() .orderByProcessDefinitionVersion().asc()/**使用流程定义的版本升序排列*/ .list(); /** * Map<String,ProcessDefinition> map集合的key:流程定义的key map集合的value:流程定义的对象 map集合的特点:当map集合key值相同的情况下,后一次的值将替换前一次的值 */ Map<String, ProcessDefinition> map = new LinkedHashMap<String, ProcessDefinition>(); if(list!=null && list.size()>0){ /**如果key值相同,后一次的值将替换前一次的值*/ for(ProcessDefinition pd:list){ map.put(pd.getKey(), pd); } } /**最后版本流程定义集合,把LinkedHashMap集合Values()放到此集合中*/ List<ProcessDefinition> pdList = new ArrayList<ProcessDefinition>(map.values()); if(pdList!=null && pdList.size()>0){ for(ProcessDefinition pd:pdList){ System.out.println("流程定义ID:"+pd.getId());/**流程定义的key+版本+随机生成数*/ System.out.println("流程定义的名称:"+pd.getName());/**对应leave.bpmn文件中的name属性值*/ System.out.println("流程定义的key:"+pd.getKey());/**对应leave.bpmn文件中的id属性值*/ System.out.println("流程定义的版本:"+pd.getVersion());/**当流程定义的key值相同的相同下,版本升级,默认1*/ System.out.println("资源名称bpmn文件:"+pd.getResourceName()); System.out.println("资源名称png文件:"+pd.getDiagramResourceName()); System.out.println("部署对象ID:"+pd.getDeploymentId()); System.out.println("#########################################################"); } } }
八、附加功能:删除流程定义(删除key相同的所有不同版本的流程定义)
/**附加功能:删除流程定义(删除key相同的所有不同版本的流程定义)*/ @Test public void deleteProcessDefinitionByKey(){ /**流程定义的key*/ String processDefinitionKey = "leave"; /**先使用流程定义的key查询流程定义,查询出所有的版本*/ List<ProcessDefinition> list = processEngine.getRepositoryService()// .createProcessDefinitionQuery() .processDefinitionKey(processDefinitionKey)/**使用流程定义的key查询*/ .list(); /**遍历,获取每个流程定义的部署ID*/ if(list!=null && list.size()>0){ for(ProcessDefinition pd:list){ /**获取部署ID*/ String deploymentId = pd.getDeploymentId(); processEngine.getRepositoryService() .deleteDeployment(deploymentId, true); } } }
九、附加功能:删除流程定义(删除key相同的所有不同版本的流程定义,保留最后版本)
/**附加功能:删除流程定义(删除key相同的所有不同版本的流程定义,保留最后版本)*/ @Test public void deleteProcessPersistLastVersionByKey(){ /**流程定义的key*/ String processDefinitionKey = "leave"; /**先使用流程定义的key查询流程定义,查询出所有的版本*/ List<ProcessDefinition> list = processEngine.getRepositoryService()// .createProcessDefinitionQuery() .processDefinitionKey(processDefinitionKey)/**使用流程定义的key查询*/ .orderByProcessDefinitionVersion().asc()/**按照版本的升序排列*/ .list(); /**遍历,获取每个流程定义的部署ID*/ if(list!=null && list.size()>0){ /**保留List<(list.size()-1)>*/ for(int i=0;i<list.size()-1;i++){ ProcessDefinition pd=list.get(i); /**获取部署ID*/ String deploymentId = pd.getDeploymentId(); processEngine.getRepositoryService() .deleteDeployment(deploymentId, true); } } }
十:总结
Deployment 部署对象
1、一次部署的多个文件的信息。对于不需要的流程可以删除和修改。
2、对应的表:
act_re_deployment:部署对象表
act_re_procdef:流程定义表
act_ge_bytearray:资源文件表
act_ge_property:主键生成策略表
ProcessDefinition 流程定义
1、解析.bpmn后得到的流程定义规则的信息,工作流系统就是按照流程定义的规则执行的。<!--EndFragment--><!--EndFragment-->