一、Activiti 的 25 张表的两种创建方式
/**
* 生成Activiti需要的25表
*/
@Test
public void testCreateTable(){
ProcessEngineConfiguration pec=ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration(); // 获取流程引擎配置
pec.setJdbcDriver("com.mysql.jdbc.Driver"); // 配置驱动
pec.setJdbcUrl("jdbc:mysql://localhost:3306/db_activiti"); // 配置连接地址
pec.setJdbcUsername("root"); // 用户名
pec.setJdbcPassword("root"); // 密码
/**
* 配置模式 true 自动创建和更新表
*/
pec.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// 获取流程引擎对象
ProcessEngine pe=pec.buildProcessEngine();
}
/**
* 生成Activiti需要的25表 使用配置文件
*/
在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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db_activiti" />
<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="root" />
<property name="databaseSchemaUpdate" value="true" />
</bean>
</beans>
@Test
public void testCreateTableWithXml(){
// 引擎配置
ProcessEngineConfiguration pec=ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
// 获取流程引擎对象
ProcessEngine processEngine=pec.buildProcessEngine();
}
二、在 Eclipse 上安装 Activiti 插件
1、 Help → Install New Software→Add.
2、Name:Activiti BPMN 2.0 designer
Location:http://activiti.org/designer/update/
这个要勾选上 否则创建图片不能保存
三、第一个activiti 程序
public class HelloWorldProcess {
/**
* 获取默认流程引擎实例,会自动读取activiti.cfg.xml文件
*/
private ProcessEngine processEngine=ProcessEngines.getDefaultProcessEngine();
部署流程定义
/**
* 部署流程定义
*/
@Test
public void deploy(){
Deployment deployment=processEngine.getRepositoryService() // 获取部署相关Service
.createDeployment() // 创建部署
.addClasspathResource("diagrams/HelloWorld.bpmn") // 加载资源文件
.addClasspathResource("diagrams/HelloWorld.png") // 加载资源文件
.name("HelloWorld流程") // 流程名称
.deploy(); // 部署
System.out.println("流程部署ID:"+deployment.getId());
System.out.println("流程部署Name:"+deployment.getName());
}
启动流程实例
/**
* 启动流程实例
*/
@Test
public void start(){
ProcessInstance pi=processEngine.getRuntimeService() // 运行时Service
// .startProcessInstanceByKey("myFirstProcess" "变量是一个map集合") 流程启动的时候就设置个变量 相 当于用 processEngine.getRuntimeService().setVariable(taskId, "date", new Date()) 相当于给这个流程初始化数据
.startProcessInstanceByKey("myFirstProcess"); // 流程定义 act_re_procdef表的KEY字段值
System.out.println("流程实例ID:"+pi.getId());
System.out.println("流程定义ID:"+pi.getProcessDefinitionId());
}
查看任务
/**
* 查看任务
*/
@Test
public void findTask(){
List<Task> taskList=processEngine.getTaskService() // 任务相关Service
.createTaskQuery() // 创建任务查询
.taskAssignee("java1234_小锋") // 指定某个人 act_ru_task这个表里的 assignee_字段
.list();
for(Task task:taskList){
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());
}
}
完成任务
/**
* 完成任务
*/
@Test
public void completeTask(){
processEngine.getTaskService() // 任务相关Service
.complete("30004"); //30004表示正在运行的任务id task.getId() 表里的act_ru_task id_字段
}
查询流程定义
/**
* 查询流程定义 返回流程定义集合 对应表 act_re_procdef
*/
@Test
public void list(){
String processDefinitionKey="myFirstProcess";
List<ProcessDefinition> pdList=processEngine.getRepositoryService() // 获取service
.createProcessDefinitionQuery() // 创建流程定义查询
.processDefinitionKey(processDefinitionKey) // 通过key查询 act_re_procdef 表里的key_字段
.list(); // 返回一个集合
for(ProcessDefinition pd:pdList){
System.out.println("ID_"+pd.getId());
System.out.println("NAME_"+pd.getName());
System.out.println("KEY_"+pd.getKey());
System.out.println("VERSION_"+pd.getVersion());
System.out.println("=========");
}
}
通过ID查询某个流程定义
/**
* 通过ID查询某个流程定义
*/
@Test
public void getById(){
String processDefinitionId="myFirstProcess:2:7504";
ProcessDefinition pd=processEngine.getRepositoryService() // 获取service
.createProcessDefinitionQuery() // 创建流程定义查询
.processDefinitionId(processDefinitionId) // 通过id查询 act_re_procdef 中的 id_字段
.singleResult();
System.out.println("ID_"+pd.getId());
System.out.println("NAME_"+pd.getName());
System.out.println("KEY_"+pd.getKey());
System.out.println("VERSION_"+pd.getVersion());
}
查询最新版本的流程定义
//查询最新版本的流程定义
@Test
public void queryPng(){
RepositoryService service = engine.getRepositoryService();
List<ProcessDefinition> version = service.createProcessDefinitionQuery().latestVersion().list();
for (ProcessDefinition processDefinition : version) {
System.out.println(processDefinition.getName());
}
}
/**
* 根据流程部署id和资源文件名称来查询流程图片
* @throws Exception
*/
@Test
public void getImageById()throws Exception{
InputStream inputStream=processEngine.getRepositoryService() // 获取sevice
.getResourceAsStream("10001", "helloWorld/HelloWorld.png");
FileUtils.copyInputStreamToFile(inputStream,new File("c:/helloWorld.png"));
}
/**
* 查询最新版本的流程定义
* @throws Exception
*/
@Test
public void listLastVersion()throws Exception{
List<ProcessDefinition> listAll=processEngine.getRepositoryService() // 获取service
.createProcessDefinitionQuery() // 创建流程定义查询
.orderByProcessDefinitionVersion().asc() // 根据流程定义版本升序
.list(); // 返回一个集合
// 定义有序Map,相同的Key,假如添加map的值 后者的值会覆盖前面相同的key的值
Map<String,ProcessDefinition> map=new LinkedHashMap<String,ProcessDefinition>();
// 遍历集合,根据key来覆盖前面的值,来保证最新的key覆盖前面所有老的key的值
for(ProcessDefinition pd:listAll){
map.put(pd.getKey(), pd);
}
List<ProcessDefinition> pdList=new LinkedList<ProcessDefinition>(map.values());
for(ProcessDefinition pd:pdList){
System.out.println("ID_"+pd.getId());
System.out.println("NAME_"+pd.getName());
System.out.println("KEY_"+pd.getKey());
System.out.println("VERSION_"+pd.getVersion());
System.out.println("=========");
}
}
删除所有key相同的流程定义
/**
* 删除所有key相同的流程定义
* @throws Exception
*/
@Test
public void deleteByKey()throws Exception{
String processDefinitionKey="helloWorld2";
List<ProcessDefinition> pdList=processEngine.getRepositoryService() // 获取service
.createProcessDefinitionQuery() // 创建流程定义查询
.processDefinitionKey(processDefinitionKey) // 根据key查询
.list(); // 返回一个集合
for(ProcessDefinition pd:pdList){
processEngine.getRepositoryService()//按照部署流程id删除 true表示级联删除
.deleteDeployment(pd.getDeploymentId(),true); act_re_deployment这个表的 id_
}
查询流程状态
/**
* 查询流程状态(正在执行 or 已经执行结束)
*/
@Test
public void processState(){
ProcessInstance pi=processEngine.getRuntimeService() // 获取运行时Service
.createProcessInstanceQuery() // 创建流程实例查询
.processInstanceId("35001") // 用流程实例id查询 act_ru_execution 表中的proc_inst_id_
.singleResult();
if(pi!=null){
System.out.println("流程正在执行!");
}else{
System.out.println("流程已经执行结束!");
}
}
历史任务查询
/**
* 历史任务查询 在act_hi_taskinst表里 只有任务的节点 不包括开始节点和结束节点
*/
@Test
public void historyTaskList(){
List<HistoricTaskInstance> list=processEngine.getHistoryService() // 历史相关Service
.createHistoricTaskInstanceQuery() // 创建历史任务实例查询
.processInstanceId("35001") // 用流程实例id查询 act_hi_taskinst 表里的proc_inst_id_ 字段
.finished() // 查询已经完成的任务
.list();
for(HistoricTaskInstance hti:list){
System.out.println("任务ID:"+hti.getId());
System.out.println("流程实例ID:"+hti.getProcessInstanceId());
System.out.println("任务名称:"+hti.getName());
System.out.println("办理人:"+hti.getAssignee());
System.out.println("开始时间:"+hti.getStartTime());
System.out.println("结束时间:"+hti.getEndTime());
System.out.println("=================================");
}
}
历史活动查询
/**
* 历史活动查询 在act_hi_actinst表里 包括开始节点和结束节点
*/
@Test
public void historyActInstanceList(){
List<HistoricActivityInstance> list=processEngine.getHistoryService() // 历史相关Service
.createHistoricActivityInstanceQuery() // 创建历史活动实例查询
.processInstanceId("35001") // 执行流程实例id act_hi_actinst里的proc_inst_id_ 字段
.finished() //unfinished表示没完成的
.list();
for(HistoricActivityInstance hai:list){
System.out.println("活动ID:"+hai.getId());
System.out.println("流程实例ID:"+hai.getProcessInstanceId());
System.out.println("活动名称:"+hai.getActivityName());
System.out.println("办理人:"+hai.getAssignee());
System.out.println("开始时间:"+hai.getStartTime());
System.out.println("结束时间:"+hai.getEndTime());
System.out.println("=================================");
}
}
/**
* 设置流程变量数据
*/
@Test
public void setVariableValues(){
TaskService taskService=processEngine.getTaskService(); // 任务Service
String taskId="72504";
taskService.setVariable(taskId, "days", 2);
// taskService.setVariable(taskId, "date", new Date());
taskService.setVariableLocal(taskId,"date", new Date());
taskService.setVariable(taskId, "reason", "发烧");
Student student=new Student();
student.setId(1);
student.setName("张三");
taskService.setVariable(taskId, "student", student); // 存序列化对象
}
/**
* 获取流程变量数据
*/
@Test
public void getVariableValues(){
TaskService taskService=processEngine.getTaskService(); // 任务Service
String taskId="80002";
Integer days=(Integer) taskService.getVariable(taskId, "days");
// Date date=(Date) taskService.getVariable(taskId, "date");
Date date=(Date) taskService.getVariableLocal(taskId, "date");
String reason=(String) taskService.getVariable(taskId, "reason");
Student student=(Student) taskService.getVariable(taskId, "student");
System.out.println("请假天数:"+days);
System.out.println("请假日期:"+date);
System.out.println("请假原因:"+reason);
System.out.println("请假对象:"+student.getId()+","+student.getName());
}
用TaskService 设置流程变量数据
/**
* 设置流程变量数据
*/
@Test
public void setVariableValue(){
TaskService taskService=processEngine.getTaskService(); // 任务Service
String taskId="60004"; //正在执行的任务id
Student student=new Student(); //如果传递的是实体对象 该对象必须要实现序列化
student.setId(1);
student.setName("张三");
Map<String, Object> variables=new HashMap<String,Object>();
variables.put("days", 2);
variables.put("date", new Date());
variables.put("reason", "回家");
variables.put("student", student);
taskService.setVariables(taskId, variables);
// service.setVariable(taskId, "days", 5); 一次可以设置一个变量 也可以把变量放到map集合里 设置变量
// service.setVariable(taskId, "reason", "回家");
// service.setVariable(taskId, "student", s);
// service.setVariableLocal(taskId, "key", "value"); 设置局部变量 用的较少
}
用TaskService获取流程变量数据
/**
* 获取流程变量数据
*/
@Test
public void getVariableValues(){
TaskService taskService=processEngine.getTaskService(); // 任务Service
String taskId="65002";
Map<String,Object> variables=taskService.getVariables(taskId);
Integer days=(Integer) variables.get("days");
Date date=(Date) variables.get("date");
String reason=(String) variables.get("reason");
Student student=(Student)variables.get("student");
//service.getVariableLocal(taskId, "key") 根据key获取局部变量 用的较少
// Integer days = (Integer)service.getVariable(taskId, "days");
// String reason = (String) service.getVariable(taskId, "reason");
// Student student = (Student) service.getVariable(taskId, "student");
System.out.println("请假天数:"+days);
System.out.println("请假日期:"+date);
System.out.println("请假原因:"+reason);
System.out.println("请假对象:"+student.getId()+","+student.getName());
}
用RuntimeService 设置流程变量数据 流程启动的时候就可以设置变量和taskservice差不多
/**
* 设置流程变量数据
*/
@Test
public void setVariableValues(){
RuntimeService runtimeService=processEngine.getRuntimeService(); // 任务Service
String executionId="90001";
runtimeService.setVariable(executionId, "days", 2);
runtimeService.setVariable(executionId, "date", new Date());
runtimeService.setVariable(executionId, "reason", "发烧");
Student student=new Student();
student.setId(1);
student.setName("张三");
runtimeService.setVariable(executionId, "student", student); // 存序列化对象
}
用RuntimeService 获取流程变量数据
/**
* 获取流程变量数据
*/
@Test
public void getVariableValues(){
RuntimeService runtimeService=processEngine.getRuntimeService(); // 任务Service
String executionId="90001"; //ac_ru_execution 表中的id_
Integer days=(Integer) runtimeService.getVariable(executionId, "days");
Date date=(Date) runtimeService.getVariable(executionId, "date");
String reason=(String) runtimeService.getVariable(executionId, "reason");
Student student=(Student) runtimeService.getVariable(executionId, "student");
System.out.println("请假天数:"+days);
System.out.println("请假日期:"+date);
System.out.println("请假原因:"+reason);
System.out.println("请假对象:"+student.getId()+","+student.getName());
}
启动流程实例时设置变量
/**
* 启动流程实例
*/
@Test
public void start(){
Student student=new Student();
student.setId(1);
student.setName("张三");
Map<String, Object> variables=new HashMap<String,Object>();
variables.put("days", 2);
variables.put("date", new Date());
variables.put("reason", "回家");
variables.put("student", student);
ProcessInstance pi=processEngine.getRuntimeService() // 运行时Service
.startProcessInstanceByKey("studentLevaeProcess", variables); // 启动流程的时候,设置流程变量
System.out.println("流程实例ID:"+pi.getId());
System.out.println("流程定义ID:"+pi.getProcessDefinitionId());
}
完成任务的时候设置流程变量
/**
* 完成任务
*/
@Test
public void completeTask(){
Student student=new Student();
student.setId(1);
student.setName("张三");
Map<String, Object> variables=new HashMap<String,Object>();
variables.put("days", 2);
variables.put("date", new Date());
variables.put("reason", "发烧");
variables.put("student", student);
processEngine.getTaskService() // 任务相关Service
.complete("112504", variables); //完成任务的时候,设置流程变量
}
}
四、流程部署加载的zip压缩包的方式
/**
* 部署流程定义使用zip方式
*/
public void deployWithZip(){
InputStream inputStream=this.getClass() // 取得当前class对象
.getClassLoader() // 获取类加载器
.getResourceAsStream("diagrams/helloWorld.zip"); // 获取指定文件资源流
ZipInputStream zipInputStream=new ZipInputStream(inputStream); // 实例化zip输入流
Deployment deployment=processEngine.getRepositoryService() // 获取部署相关Service
.createDeployment() // 创建部署
.addZipInputStream(zipInputStream) // 添加zip输入流
.name("HelloWorld流程") // 流程名称
.deploy(); // 部署
System.out.println("流程部署ID:"+deployment.getId());
System.out.println("流程部署Name:"+deployment.getName());
}
五、数据库表的变化
################################
# 部署流程定义涉及到的表
# 流程部署表
SELECT * FROM `act_re_deployment`
# 流程定义表
SELECT * FROM `act_re_procdef`
# 资源文件表
SELECT * FROM `act_ge_bytearray`
# 系统配置表
SELECT * FROM `act_ge_property`
################################
# 启动流程实例涉及到的表
# 流程实例运行时 执行对象表
SELECT * FROM `act_ru_execution`
# 流程实例运行时 身份联系表
SELECT * FROM `act_ru_identitylink`
# 流程实例运行时 用户任务表
SELECT * FROM `act_ru_task`
# 活动节点历史表
SELECT * FROM `act_hi_actinst`
# 身份联系表 历史
SELECT * FROM `act_hi_identitylink`
# 流程实例表 历史
SELECT * FROM `act_hi_procinst`
# 历史任务表
SELECT * FROM `act_hi_taskinst`
################################
# 结束流程实例涉及到的表
# 运行时 表数据全部清空
# 历史表 表数据修改 或者增加了数据
################################
# 设置流程变量涉及到的表
# 运行时流程变量表
SELECT * FROM `act_ru_variable`
# 历史流程变量表
SELECT * FROM `act_hi_varinst`
六、多种线路流程执行情况
连线
流程图如下
当请假天数小于10天班长审批完直接结束
当请假天数大于10天时班长审批完班主任审批完才能结束
流程线表达式如下
//完成任务
@Test
public void compelet(){
TaskService service = engine.getTaskService();
Map<String,Object> map=new HashMap<String,Object>();
map.put("days", 18);
service.complete("105002",map); //在请假完成的时候带参数 或者设置一个全局变量 key的值一定要是days和表达式中的值要一致
}
排它网关 排它网关和连线的区别 是排它网关如果没有匹配的条件时可以默认执行某条流程
流程图如下
当请假天数小于3天时班长审批
当请假天数大于3天小于7天班主任审批
当请假天数条件不满足前两个(也就是大于7天)的是校长审批
设置默认走校长审批 流程 配置如下 大于7天的那个条线是flow5 默认的flow5 这个一定不要设置大于几天的那个表达式 (condition) 因为前两个如果不满足的情况下回默认走flow5那个流程
做法和上面的连线是一样的 在请假完成的时候带参数 或者设置一个全局变量 key的值一定要是days和表达式中的值要一致
并行网关 并行网关 是多个流程一起走 当多个流程都走完时在继续向下执行
流程图如下
班长和班主任都审批通过后才可以在往下执行
七、用户和组的创建删除
创建用户
@Test
public void testSaveUser(){
IdentityService indentityService=processEngine.getIdentityService();
User user=new UserEntity(); // 实例化用户实体 activiti提供的一个用户接口
user.setId("wangwu");
user.setPassword("123");
user.setEmail("1234@qq.com");
indentityService.saveUser(user);
}
删除用户
@Test
public void testDeleteUser(){
IdentityService indentityService=processEngine.getIdentityService();
indentityService.deleteUser("wangwu"); //根据 act_id_user id_字段删除
}
创建组
@Test
public void testSaveGroup(){
IdentityService indentityService=processEngine.getIdentityService();
Group group=new GroupEntity();
group.setId("test");
indentityService.saveGroup(group);
}
删除组
@Test
public void testDeleteGroup(){
IdentityService indentityService=processEngine.getIdentityService();
indentityService.deleteGroup("test"); //根据 act_id_group id_字段删除
}
把用户添加到组里
@Test
public void testSaveMembership(){
IdentityService indentityService=processEngine.getIdentityService();
indentityService.createMembership("wangwu", "test"); //第一个表示act_id_user中的id_字段第二个表示act_id_group中的id_字段
}
删除用户和组的关联关系
@Test
public void testDeleteMembership(){
IdentityService indentityService=processEngine.getIdentityService();
indentityService.deleteMembership("wangwu", "test"); /第一个表示act_id_user中的id_字段第二个表示act_id_group中的id_字段
}
八、任务分配
个人任务分配
1、在流程图的Main config -> Assignee写个固定的值 (此方式不常用)
2、在启动或者完成任务的时候设置办理人 在配置表达式 如Main config -> Assignee ${userId}
这种方式变量的key值一定要和assignee 配置的表达式的值一致
3、TaskListener 监听实现
新建一个类 实现 TaskListener接口 代码如下
public class MyTaskListener implements TaskListener{
private static final long serialVersionUID = 1L;
public void notify(DelegateTask delegateTask) {
delegateTask.setAssignee("赵六"); // 指定办理人
}
}
下图中的listeners中配置上 上面自定义的监听器类TaskListener 走到此流程的时候会执行这个类中notify方法的内容
多用户任务分配 只要是配置的多个用户其中的一个完成任务即可
1、在流程图的Main config -> Candidate users(comma speparated)写个固定的值 (此方式不常用)
在流程图中配置方式如下
/**
* 查看任务
*/
@Test
public void findTask(){
List<Task> taskList=processEngine.getTaskService() // 任务相关Service
.createTaskQuery() // 创建任务查询
//.taskAssignee("李四") // 指定某个人
.taskCandidateUser("赵六") // 指定候选人
.list();
for(Task task:taskList){
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());
}
}
2、在启动或者完成任务的时候设置办理人 在配置表达式 如Main config -> Candidate users(comma speparated) ${userIds}
这种方式变量的key值一定要和Candidate users(comma speparated)配置的表达式的值一致
//完成任务
@Test
public void compelet(){
TaskService service = engine.getTaskService();
Map<String,Object> map=new HashMap<String, Object>();
map.put("userIds", "bb,aa,cc"); //设置处理任务的候选人
service.complete("247504",map);
}
3、TaskListener 监听实现 和个人任务分配中的监听器一样
public class MyTaskListener implements TaskListener{
private static final long serialVersionUID = 1L;
public void notify(DelegateTask delegateTask) {
// delegateTask.setAssignee("赵六"); // 指定办理人
delegateTask.addCandidateUser("张三");
delegateTask.addCandidateUser("李四");
delegateTask.addCandidateUser("王五");
}
}
组任务分配
租任务分配和个人 任务分配 多人任务分配是一样的
只要在这个组下的某一个用户都可以查看任务 并且处理任务
1、在流程图的Main config -> Candidate groups(comma speparated)写个固定的值 (此方式不常用)
配置如下
2、使用流程变量 Main config -> Candidate groups(comma speparated)
配置如下 这种方式变量的key值一定要和Candidate groups(comma speparated)配置的表达式的值一致
3、TaskListener 监听实现 和个人任务分配中的监听器一样
public void notify(DelegateTask delegateTask) {
delegateTask.addCandidateGroup("1");
delegateTask.addCandidateGroup("3");
}
九、审批被打回来流程图
如果请假天数超过3天 班主任审批不给通过 这里用的是排他网关