Activiti工作流学习之流程图应用详解
了解Activiti工作流是怎样应用流程图的。
jdk版本:Jdk1.7及以上
IDE:eclipse
数据库:mysql及navicat for mysql
下载demo:kft-activiti-demo-no-maven
(网址https://github.com/henryyan/kft-activiti-demo/tree/no-maven)
eclipse打开help->install new software,单击add:
Name: Activiti BPMN2.0 designer
Location:http://activiti.org/designer/update/
进行安装即可。(如下图,我的已经安装过了)
新建工程Act_test,将kft-activiti-demo-no-maven\WebContent\WEB-INF\lib(activiti相关包)导入工程lib目录下
这里我们使用配置文件来创建activiti的表
(1)在类路径resources下创建Activiti.cfg.xml文件,文件内容为:
xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/bea
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test1?
createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="jdbcUsername" value="root"/>
<property name="jdbcPassword" value="root"/>
<property name="databaseSchemaUpdate" value="true"/>
bean>
beans>
说明:
a. #useUnicode 是否使用unicode输出,true/false缺省为false
b. #characterEncoding如果useUnicode,该参数制定encoding类型,建议使用utf8缺省为无
c. #createDatabaseIfNotExist当JDBC连接指定数据库,如果此数据库不存在,此参数值为true时,则自动创建此数据库
(2)通过测试代码来加载配置文件中内容:(自动建表)
//使用配置文件来创建数据库中的表
@Test
public void testDBByProperties() throws Exception{
//通过让工作流引擎的全部配置对象来执行配置文件中的内容来创建流程引擎对象
ProcessEngine processEngine = ProcessEngineConfiguration.
createProcessEngineConfigurationFromResource("activiti.cfg.xml").buildProcessEngine();
System.out.println(processEngine);
}
执行后,打开navicat for mysql,可看到test1及23张表创建成功
1) Deployment:流程部署对象,部署一个流程时创建。
2) ProcessDefinitions:流程定义,部署成功后自动创建。
3) ProcessInstances:流程实例,启动流程时创建。
4) Task:任务,在Activiti中的Task仅指有角色参与的任务,即定义中的UserTask。
5) Execution:执行计划,流程实例和流程执行中的所有节点都是Execution,如UserTask、ServiceTask等。
1) ProcessEngine:流程引擎的抽象,通过它我们可以获得我们需要的一切服务。
2) RepositoryService:Activiti中每一个不同版本的业务流程的定义都需要使用一些定义文件,部署文件和支持数据(例如BPMN2.0 XML文件,表单定义文件,流程定义图像文件等),这些文件都存储在Activiti内建的Repository中。RepositoryService提供对 repository的存取服务。
3) RuntimeService:在Activiti中,每当一个流程定义被启动一次之后,都会生成一个相应的流程对象实例。RuntimeService提供了启动流程、查询流程实例、设置获取流程实例变量等功能。此外它还提供了对流程部署,流程定义和流程实例的存取服务。
4) TaskService: 在Activiti中业务流程定义中的每一个执行节点被称为一个Task,对流程中的数据存取,状态变更等操作均需要在Task中完成。TaskService提供了对用户Task 和Form相关的操作。它提供了运行时任务查询、领取、完成、删除以及变量设置等功能。
5) IdentityService: Activiti中内置了用户以及组管理的功能,必须使用这些用户和组的信息才能获取到相应的Task。IdentityService提供了对Activiti 系统中的用户和组的管理功能。
6) ManagementService: ManagementService提供了对Activiti流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti系统的日常维护。
7) HistoryService: HistoryService用于获取正在运行或已经完成的流程实例的信息,与RuntimeService中获取的流程信息不同,历史信息包含已经持久化存储的永久信息,并已经被针对查询优化。
主要包括:(1)测试类helloWorld
(2)配置文件activiti.cfg.xml
(3)流程图文件MyProcess.bpmn和MyProcess.png
我们通过流程设计器(Activiti插件)就可以拖拖拽拽出我们的流程,同时设置相关属性,保存后会生成两个文件,分别是MyProcess.bpmn和MyProcess.png
(1)新建Activiti Diagram,命名为MyProcess
(2)进行相关操作,得到流程图:
(3)MyProcess.bpmn(实际是一个xml文件)部分代码:
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="myProcess" name="My process" isExecutable="true">
<startEvent id="startevent1" name="Start">startEvent>
<userTask id="提交申请" name="提交申请" activiti:assignee="张三">userTask>
<userTask id="usertask2" name="审批【部门经理】" activiti:assignee="李四">userTask>
<userTask id="usertask3" name="审批【总经理】" activiti:assignee="王五">userTask>
<endEvent id="endevent1" name="End">endEvent>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="提交申请">sequenceFlow>
<sequenceFlow id="flow2" sourceRef="提交申请" targetRef="usertask2">sequenceFlow>
<sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3">sequenceFlow>
<sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1">sequenceFlow>
process>
......
definitions>
package test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
public class HelloWorld {
@Test
public void testDBByProperties() throws Exception{
// 加载配置文件,使用配置文件来创建数据库中的表
ProcessEngine processEngine = ProcessEngineConfiguration.
createProcessEngineConfigurationFromResource("activiti.cfg.xml").buildProcessEngine();
System.out.println(processEngine);
}
@Test
public void Process() throws Exception{
//获取流程引擎
ProcessEngine processEngine = ProcessEngineConfiguration.
createProcessEngineConfigurationFromResource("activiti.cfg.xml").buildProcessEngine();
/**
* 1.流程定义
* 这里使用RepositoryService部署流程定义
* addClasspathResource表示从类路径下加载资源文件,一次只能加载一个文件
*/
//获取服务实例
Deployment deployment=processEngine.getRepositoryService()// 与流程定义和部署对象相关的service
.createDeployment()// 创建一个部署对象
.name("流程定义")// 添加部署的名称
.addClasspathResource("MyProcess.bpmn")// 从classpath的资源中加载,一次只能加载一个文件
.addClasspathResource("MyProcess.png")// 从classpath的资源中加载,一次只能加载一个文件
.deploy();// 完成部署
System.out.println("部署ID:" + deployment.getId());
System.out.println("部署名称:" + deployment.getName());
System.out.println("****************************");
/**
* 2.启动流程
* 这里使用RuntimeService启动流程实例
*/
//使用流程定义的key即流程图MyProcess的id启动流程实例
String processDefinitionKey ="myProcess";
// 创建流程变量
Map
variables.put("applyname", "wangwu");
// 在启动时设置流程变量
// 使用指定key的最新版本的流程定义启动流程实例,并设置一些流程变量
//ProcessInstance pi =processEngine.getRuntimeService().startProcessInstanceByKey("myProcess");
ProcessInstance pi =processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey , variables );
System.out.println("流程实例ID:"+pi.getId());
System.out.println("流程定义ID:" + pi.getProcessDefinitionId());
System.out.println("****************************");
/**
* 3.查看任务
* 这里使用TaskService完成任务的查询
*/
//指定任务办理者
String assignee="张三";
//查询任务列表
List
.createTaskQuery() //创建任务查询对象
.taskAssignee(assignee) //指定个人任务办理人
.orderByTaskCreateTime().asc()
.list();
//遍历结合查看内容
for(Task task:tasks){
System.out.println("任务ID:" + task.getId());
System.out.println("任务名称:" + task.getName());
System.out.println("任务的创建时间:" + task.getCreateTime());
System.out.println("任务的办理人:" + task.getAssignee());
System.out.println("流程实例ID:" + task.getProcessInstanceId());
System.out.println("执行对象ID:" + task.getExecutionId());
System.out.println("流程定义ID:" + task.getProcessDefinitionId());
System.out.println("****************************");
/**
* 4.办理任务
* 这里使用TaskService完成任务的办理
*/
//办理任务
//获得任务ID
String taskId = task.getId();
processEngine.getTaskService().complete(taskId);
System.out.println("完成任务");
System.out.println("任务ID:" + taskId);
}
}
}
1.流程定义
1)先获取流程引擎对象:在创建时会自动加载classpath下的activiti.cfg.xml
2)通过获取的流程引擎对象,通过流程引擎对象获取一个RepositoryService对象(仓库对象)
3)由仓库的服务对象产生一个部署对象配置对象,用来封装部署操作的相关配置
4)这是一个链式编程,在部署配置对象中设置显示名字,上传流程定义规则文件
5)向数据库表中存放流程定义的规则信息
这些表都是跟部署对象和流程定义相关的表:
act_re_deployment存放流程定义的显示名和部署时间,每部署一次增加一条记录;
act_re_procdef(存放流程定义的属性信息,部署每个新的流程定义都会在这张表中增加一条记录,需要注意一下的当流程定义的key相同的情况下,使用的是版本升级;
act_ge_bytearray存储流程定义相关的部署信息。即流程定义文档的存放地。每部署一次就会增加两条记录,一条是关于bpmn规则文件的,一条是图片的(如果部署时只指定了bpmn一个文件,activiti会在部署时解析bpmn文件内容自动生成流程图)。两个文件不是很大,都是以二进制形式存储在数据库中。
2.启动流程
1)在数据库的act_ru_execution正在执行的执行对象表中插入一条记录
2)在数据库的act_hi_procinst程实例的历史表中插入一条记录
3)在数据库的act_hi_actinst活动节点的历史表中插入一条记录
4)我们图中节点都是任务节点,所以同时也会在act_ru_task流程实例的历史表添加一条记录
5)在数据库的act_hi_taskinst任务历史表中也插入一条记录。
3.查看任务
1)因为是任务查询,所以从processEngine中应该得到TaskService
2)使用TaskService获取到任务查询对象TaskQuery
3)为查询对象添加查询过滤条件,使用taskAssignee指定任务的办理者(即查询指定用户的代办任务),同时可以添加分页排序等过滤条件
4)调用list方法执行查询,返回办理者为指定用户的任务列表
5)任务ID、名称、办理人、创建时间可以从act_ru_task表中查到。
6)在现在这种情况下,ProcessInstance相当于Execution
7) 一个Task节点和Execution节点是1对1的情况,在task对象中使用Execution_来表示他们之间的关系
8)任务ID在数据库表act_ru_task中对应“ID_”列
4.执行任务(完成任务)
1)是完成任务,所以从ProcessEngine得到的是TaskService。
2)当执行完这段代码,再以员工的身份去执行查询的时候,会发现这个时候已经没有数据了,因为正在执行的任务中没有数据。
3)对于执行完的任务,activiti将从act_ru_task表中删除该任务,下一个任务会被插入进来。
4)以”部门经理”的身份进行查询,可以查到结果。因为流程执行到部门经理审批这个节点了。
5)再执行办理任务代码,执行完以后以”部门经理”身份进行查询,没有结果。
6)重复第3和4步直到流程执行完。
注:详细数据可查看数据库test1,下面数据可参考
部署ID:1
部署名称:流程定义
****************************
流程实例ID:5
流程定义ID:myProcess:1:4
****************************
任务ID:9
任务名称:提交申请
任务的创建时间:Wed Dec 30 14:15:50 CST 2015
任务的办理人:张三
流程实例ID:5
执行对象ID:5
流程定义ID:myProcess:1:4
****************************
完成任务
任务ID:9
1.从上面步骤可知,Activiti工作流主要是通过方法startProcessInstanceByKey及流程实例ProcessInstance应用流程图的,即启动流程阶段:
在启动时使用指定key(即流程图的id)的流程定义启动流程实例,并设置一些流程变量。
String processDefinitionKey ="myProcess";
Map
variables.put("applyname", "wangwu");//申请人王五
ProcessInstance pi = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey ,
variables );
2.Activiti使用MyProcess流程实现其他具体流程(例如:请假流程)时,只需要改变variables的数据就行
3.网上资料:(详细解释)
3.1业务和流程的关联方式
RuntimeService有两个方法:
3.1.1 startProcessInstanceByKey
javadoc对其说明:
其中businessKey就是业务ID,例如要申请请假,那么先填写登记信息,然后(保存+启动流程),因为请假是单独设计
的数据表,所以保存后得到实体ID就可以把它传给processInstanceBusinessKey方法启动流程。当需要根据businessKey
查询流程的时候就可以通过API查询:
|
runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(processInstanceBusinessKey, processDefinitionKey) |
建议数据库冗余设计:在业务表设计的时候添加一列:PROCESS_INSTANCE_ID varchar2(64),在流程启动之后把
流程ID更新到业务表中,这样不管从业务还是流程都可以查询到对方!
特别说明: 此方法启动时自动选择最新版本的流程定义。
3.1.2 startProcessInstanceById
javadoc对其说明:
processDefinitionId:这个参数的值可以通过repositoryService.createProcessDefinitionQuery()方法查询,对应
数据库:ACT_RE_PROCDEF;每次部署一次流程定义就会添加一条数据,同名的版本号累加。
特别说明: 此可以指定不同版本的流程定义,让用户多一层选择。
3.1.3 如何选择
建议使用startProcessInstanceByKey,特殊情况需要使用以往的版本选择使用startProcessInstanceById。