Activiti——管理流程定义
1.设计流程定义文档
1.1.流程图
1.2.bpmn文件
<?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:xsd="http://www.w3.org/2001/XMLSchema" 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="helloworld" name="HelloWorldProcess" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<endEvent id="endevent1" name="End"></endEvent>
<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="250.0" y="70.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="250.0" y="400.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="215.0" y="140.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="215.0" y="220.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
<omgdc:Bounds height="55.0" width="105.0" x="215.0" y="296.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="267.0" y="105.0"></omgdi:waypoint>
<omgdi:waypoint x="267.0" y="140.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="267.0" y="195.0"></omgdi:waypoint>
<omgdi:waypoint x="267.0" y="220.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="267.0" y="275.0"></omgdi:waypoint>
<omgdi:waypoint x="267.0" y="296.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="267.0" y="351.0"></omgdi:waypoint>
<omgdi:waypoint x="267.0" y="400.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
说明:流程定义文档有两部分组成。
1)bpmn文件
流程规则文件。在部署后,每次系统启动时候都会被解析,把内容封装成流程定义放入项目缓存中。
Activiti框架结合这个xml文件自动管理流程,流程的执行就是按照bpmn文件定义的规则执行的,
bpmn文件是给计算机执行用的。
2)展示流程图的图片
在系统需要展示流程的进展图片,图片时给用户看的。
2.部署流程定义(classpath路径加载文件)
【代码片段】
/**
* 部署流程定义
*/
@Test
public void deploymentProcessDefinition() {
Deployment deployment = processEngine.getRepositoryService()// 与流程定义和部署对象相关的Service
.createDeployment()// 创建一个部署对象
.name("HelloWorld入门程序")// 添加部署的名称
.addClasspathResource("diagrans/helloword.bpmn")// 从类路径(classpath)下加载文件,一次只能加载一个文件
.addClasspathResource("diagrans/helloword.png")// 从类路径(classpath)下加载文件,一次只能加载一个文件
.deploy();// 完成部署
}
说明:
1)先获取流程引擎对象,在创建时会自动加载classpath下的activiti.cfg.xml
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
2)首先获得默认的流程引擎,通过流程引擎获取了一个RepositoryService对象(仓库对象)。
3)由仓库的服务对象产生一个部署对象配置对象,用来封装部署操作的相关配置。
4)这是一个链式编程,在部署配置对象中设置显示名,上传流程定义规则文件。
5)向数据库表中存放流程定义的规则信息。
6)这一步在数据库中将操作三张表:
act_re_deployment | 部署对象表 | 存放流程定义的显示名和部署时间,每部署一次增加一条记录。 |
act_re_procdef | 流程定义表 | 存放流程定义的属性信息,部署每个新的流程定义都会在这张表中增加一条记录 注意:当流程定义的key相同的情况下,使用的是版本升级。 |
act_ge_bytearray | 资源文件表 | 存储流程定义相关的部署信息。即在流程定义文档的存放地。每部署一次就会增加两条记录,一条是bpmn规则文件的,另一条是图片的(如果部署时只指定了bpmn一个文件,activiti会在部署时解析bpmn文件内容自动生成流程图),两个文件不是很大,都是二进制形式存储在数据库中。 |
3.部署流程定义(zip格式文件)
/**
* 部署流程定义(zip格式文件)
*/
@Test
public void deploymentProcessDefinitionZip() {
//加载zip资源文件
InputStream in = this.getClass().getClassLoader().getResourceAsStream("diagrans/helloword.zip");
ZipInputStream zipInputStream = new ZipInputStream(in);
Deployment deployment = processEngine.getRepositoryService()// 与流程定义和部署对象相关的Service
.createDeployment()// 创建一个部署对象
.name("程序定义(ZIP)")// 添加部署的名称
.addZipInputStream(zipInputStream)// 从类路径(classpath)下加载文件,一次只能加载一个文件
.deploy();// 完成部署
System.out.println("=======打印部署对象信息========");
System.out.println("流程ID:" + deployment.getId());// 流程ID:10001
System.out.println("流程部署名称:" + deployment.getName());// 流程部署名称:程序定义(ZIP)
System.out.println(deployment);// DeploymentEntity[id=10001, name=程序定义(ZIP)]
}
/**
* 查询流程定义
*/
@Test
public void findProcessDefinition(){
List<ProcessDefinition> list =processEngine.getRepositoryService()
.createProcessDefinitionQuery()//创建流程定义查询对象
/**指定查询条件 where条件*/
//.deploymentId(deploymentId)//使用部署对象的ID查询
//.processDefinitionId(processDefinitionId)//使用流程定义ID查询
//.processDefinitionKey(processDefinitionKey)//使用流程定义的Key查询
//.processDefinitionNameLike(processDefinitionNameLike)//使用流程定义名称进行模糊查询
/**排序*/
//.orderByProcessDefinitionVersion().asc()//安照版本的升序排列
.orderByProcessDefinitionName().desc()//按照流程定义名称降序排列
/**返回结果集*/
.list();//返回List结果集
//.listPage(firstResult, maxResults)//分页查询
//.singleResult();//返回单个记录结果集
//.count();//返回记录统计数
if(list!=null && list.size()>0){
for(ProcessDefinition processDef:list){
System.out.println("流程定义ID:"+processDef.getId());//流程定义的key+版本+随机数
System.out.println("流程定义名称:"+processDef.getName());//对应helloworld.bpmn文件中的name属性值
System.out.println("流程定义的Key:"+processDef.getKey());//对应helloworld.bpmn文件中的id属性值
System.out.println("流程定义的版本:"+processDef.getVersion());//当流程定义的key值相同的情况下,版本升级,默认1
System.out.println("资源名称bpmn文件:"+processDef.getResourceName());
System.out.println("资源名称png文件:"+processDef.getDiagramResourceName());
System.out.println("部署对象ID:"+processDef.getDeploymentId());
System.out.println("===========快乐分割线===============");
}
}
}
说明:
1)流程定义和部署对象相关的Service都是RepositoryService.
2)创建流程定义查询对象,可以在ProcessDefinitionQuery上设置查询的相关参数
3)调用ProccessDefinitionQuery对象的list方法,执行查询,获得符合条件的流程定义列表
4)由运行结果可以看出:
Key和Name的值为:bpmn文件 process节点的id和name属性值
<?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:xsd="http://www.w3.org/2001/XMLSchema" 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="helloworld" name="HelloWorldProcess" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<endEvent id="endevent1" name="End"></endEvent>
<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>
.........
5)key属性被用来区分不同的流程定义。
6)带有特定Key的流程定义第一次部署时,version为1,之后每次部署都会在当前最高版本号上加1
7)id的值的生成规则为(processDefinitionKey):(processDefinitionVersion):(generated-id),这里的generated-id是一个
自动生成的唯一的数字。
8)重复部署一次,deploymentid的值以一定的形式变化,规则act_ge_property表生成
5.删除流程定义
/**
* 删除流程定义
*/
@Test
public void deleteProcessDefinition(){
String deploymentId = "30001";//部署流程定义ID
processEngine.getRepositoryService()
.deleteDeployment(deploymentId);//不带级联的删除只能删除没用启动的流程,如果流程启动就会抛出异常
//.deleteDeployment(deploymentId, true);//级联删除(不管是有无启动的流程都能删除)
System.out.println("删除成功!");
}
说明:
1)因为删除的是流程定义,而流程定义的部署时属于仓库服务的,所以应该先得到RepositoryService
2)如果该流程定义下没有正在运行的流程,则可以用普通删除,如果是有关联的信息(启动的流程)可以使用级联删除。
项目开发中使用级联删除的情况比较多,删除操作一般只开放给超级管理员使用。
6.获取流程定义文档的资源(查看流程图附件)
/**
* 获取流程定义文档资源
* @throws IOException
*/
@Test
public void viewProcessPic() throws IOException {
//流程部署ID
String deploymentId = "40001";
//图片资源名称
String resourceName = null;
List<String> list =processEngine.getRepositoryService()
.getDeploymentResourceNames(deploymentId);
if(list!=null&& list.size()>0){
for(String val :list){
System.out.println(val);
if(val.endsWith(".png")){
resourceName = val;
}
}
}
if(resourceName == null){
throw new RuntimeException("resourceName is null");
}
InputStream in = processEngine.getRepositoryService()
.getResourceAsStream(deploymentId, resourceName);
FileUtils.copyInputStreamToFile(in, new File("F:/helloworld.png"));
}
说明:
1)deploymentId为流程部署ID。
2)resourceName 为 act_ge_bytearry表中NAME列的值。
3)使用repositoryService的getDeploymentResourceNames 方法可以获取指定部署下的所有文件名称。
4)使用repositoryService的getResouceAsStream 方法传入部署ID和资源图片名称可以获取部署下指定名称文件的输入流。
5)最后的有关IO流的操作,使用FileUtils工具的 copyInputStreamToFile方法(apache common-io包)完成流程到文件的拷贝,
将资源文件以流的形式输出到指定文件夹下。
7.附加功能:查询最新版的流程定义
/**
* 附加功能:查询最新版的流程定义
*/
@Test
public void findLastVersionProcessDefinition(){
//方式①
List<ProcessDefinition> list =processEngine.getRepositoryService()
.createProcessDefinitionQuery()
.orderByProcessDefinitionVersion().asc()
.list();
//使用key合并,保留最新版本
Map<String,ProcessDefinition> linkMap = new LinkedHashMap<String, ProcessDefinition>();
if(list!=null && list.size()>0){
for(ProcessDefinition processDef:list){
linkMap.put(processDef.getKey(), processDef);
}
}
List<ProcessDefinition> outList =new ArrayList<ProcessDefinition>(linkMap.values());
if(outList!=null && outList.size()>0){
for(ProcessDefinition processDef:outList){
System.out.println("流程定义ID:"+processDef.getId());//流程定义的key+版本+随机数
System.out.println("流程定义名称:"+processDef.getName());//对应helloworld.bpmn文件中的name属性值
System.out.println("流程定义的Key:"+processDef.getKey());//对应helloworld.bpmn文件中的id属性值
System.out.println("流程定义的版本:"+processDef.getVersion());//当流程定义的key值相同的情况下,版本升级,默认1
System.out.println("资源名称bpmn文件:"+processDef.getResourceName());
System.out.println("资源名称png文件:"+processDef.getDiagramResourceName());
System.out.println("部署对象ID:"+processDef.getDeploymentId());
System.out.println("===========快乐分割线===============");
}
}
//方式② 更简便的方式
ProcessDefinition lastVersionProcessDefinition = processEngine.getRepositoryService()
.createProcessDefinitionQuery()
.processDefinitionKey("helloworld")
.latestVersion()//设置获取最后一个版本的标记
.singleResult();
if(lastVersionProcessDefinition!=null){
System.out.println("流程定义ID:"+lastVersionProcessDefinition.getId());//流程定义的key+版本+随机数
System.out.println("流程定义名称:"+lastVersionProcessDefinition.getName());//对应helloworld.bpmn文件中的name属性值
System.out.println("流程定义的Key:"+lastVersionProcessDefinition.getKey());//对应helloworld.bpmn文件中的id属性值
System.out.println("流程定义的版本:"+lastVersionProcessDefinition.getVersion());//当流程定义的key值相同的情况下,版本升级,默认1
System.out.println("资源名称bpmn文件:"+lastVersionProcessDefinition.getResourceName());
System.out.println("资源名称png文件:"+lastVersionProcessDefinition.getDiagramResourceName());
System.out.println("部署对象ID:"+lastVersionProcessDefinition.getDeploymentId());
}
}
8.附加功能:删除流程定义(删除key相同的所有不同版本的流程定义)
/**
* 附加功能:删除流程定义(删除key相同的所有不同版本的流程定义)
*/
@Test
public void deleteProcessDefinitionByKey() {
// 流程定义的Key
String processDefinitionKey = "helloworld";
// 先使用流程定义的key查询流程定义,查询出 所有版本
List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery()
.processDefinitionKey(processDefinitionKey)// 使用流程定义的key查询
.list();
// 遍历,获取每个流程定义的部署ID
if (list != null && list.size() > 0) {
for (ProcessDefinition processDefinition : list) {
//根据迭代的每个部署ID进行级联删除
processEngine.getRepositoryService()
.deleteDeployment(processDefinition.getDeploymentId(), true);
System.out.println("删除部署定义:" + processDefinition.getDeploymentId());
}
}
System.out.println("删除成功!");
}
9.总结
Deployment 部署对象
1)一次部署的多个文件的信息,对于不需要的流程可以删除和修改。
2)对应的表:
act_re_deployment 部署对象表
act_re_procdef 流程定义表
act_ge_bytearray 资源文件表
act_ge_property 主键生成策略表
ProcessDefinition 流程定义
1)解析bpmn后得到的流程定义规则的信息,工作流系统就是按照流程定义的规则执行的。