工作流Jpbm4.4工作流知识点总结(工作流开发宝典) 第一章 工作流框架的搭建 第二章 工作流框架的流程开发 2. 执行流程实例 第三章  jbpm与spring整合

Jbpm工作流开发过程中的一些知识点总结,方便以后开发使用!

目录:

一、工作流框架的搭建

二、工作流框架的流程开发

  1、管理流程定义

    ①部署流程定义

    ②查询流程定义

    ③删除流程定义

    ④获取部署对象中的文件资源内容

    ⑤ 获取流程图中某活动的坐标

  2、执行流程实例

    ①启动流程实例

    ②向后执行一步

    ③查询任务

    ④完成任务

    ⑤拾取任务

    ⑥获取流程中的变量

三、Jbpm和spring整合

1.1. 准备jBPM4.4的开发环境

1.1.1. 添加jBPM4.4jar

  1. ${JBPM_HOME}/jbpm.jar(核心包)
  2. JBPM_HOME/lib/*.jar,不添加以下jar包:servlet-api.jar, junit.jar。其中junit.jar一定不要添加,因为是3.8.2版本,与我们使用的junit4有冲突。
  3. 所使用的数据库对应的驱动的jar包(第2步所添加的jar包中已包含mysqljdbc驱动jar包)。
  4. 配置文件可以从JBPM_HOME/examples/src/中拷贝:
    jbpm.cfg.xml
    logging.properties//要注意版本的问题,接口和实现类要相同版本
    jbpm.hibernate.cfg.xml
  5. 修改logging.properties中的日志输出级别WARNING: java.util.logging.ConsoleHandler.level=WARNING
  6. 修改jbpm.hibernate.cfg.xml中的数据库连接信息。如果使用MySQL,使用的方言一定要是org.hibernate.dialect.MySQL5InnoDBDialect。
  7. 数据库连接编码一定要是UTF-8。否则可能会在部署含有中文字符的流程定义时会抛异常,说sql语法错误。

第二章 工作流框架的流程开发

1.1. 部署流程定义

注意区分Deployment与ProcessDefinition

1.1.1. 示例代码1:流程定义有关文件在classpath

String deploymentId = processEngine.getRepositoryService()

.createDeployment()

.addResourceFromClasspath("process/test.jpdl.xml")

.addResourceFromClasspath("process/test.png")

.deploy();

1.1.2. 示例代码2:一次添加多个流程定义有关文件(要先打成zip包)

String deploymentId = processEngine.getRepositoryService()

.createDeployment()

.addResourcesFromZipInputStream(zipInputStream)

.deploy();

1.1.3. 说明

1, .addResourceFromClasspath(resource); 可以调用多次以添加多个文件。文件重复添加也不会报错。

2, .addResourceFromInputStream(resourceName, inputStream)添加一个文件(使用InputStream

3, .addResourcesFromZipInputStream(zipInputStream)添加多个文件,里面也可以有文件夹。

4, 以上方法可以在一起调用。

 

1.2. 删除流程定义

1.2.1. 示例代码1:删除流程定义,如果有关联的流程实例信息则报错

repositoryService.deleteDeployment(deploymentId);

1.2.2. ?示例代码2:删除流程定义,并删除关联的流程实例与历史信息

repositoryService.deleteDeploymentCascade(deploymentId);

关联:关联的流程实例,就是流程定义中执行过的那些实例【已经放到了历史表中,级联删除的就是同时删除那些执行过了的实例】,

1.3. 查询流程定义

1.3.1. 相关查询API说明:ProcessDefinitionQuery

RepositoryService.createProcessDefinitionQuery()

 

1.3.2. 示例代码1:查询所有流程定义

// 1,构建查询

ProcessDefinitionQuery pdQuery = processEngine.getRepositoryService() 

.createProcessDefinitionQuery()// 

.orderAsc(ProcessDefinitionQuery.PROPERTY_NAME)//

.orderDesc(ProcessDefinitionQuery.PROPERTY_VERSION);

 

// 2,查询出总数量与数据列表

long count = pdQuery.count();

List<ProcessDefinition> list = pdQuery.page(0, 100).list();// 分页:取出前100条记录

 

// 3,显示结果

System.out.println(count);

for (ProcessDefinition pd : list) {

System.out.println("id=" + pd.getId()//

+ ",deploymentId=" + pd.getDeploymentId()//

+ ",name=" + pd.getName()//

+ ",version=" + pd.getVersion()//

+ ",key=" + pd.getKey()); //

}

1.3.3. 示例代码2:查询所有最新版本的流程定义列表

// 1,查询,按version升序排序,则最大版本排在最后

List<ProcessDefinition> all = processEngine.getRepositoryService()//

.createProcessDefinitionQuery()//

.orderAsc(ProcessDefinitionQuery.PROPERTY_VERSION)

.list();

// 2,过滤出所有不同Key的最新版本(因为最大版本在最后面)

Map<String, ProcessDefinition> map = new HashMap<String, ProcessDefinition>(); // mapkey是流程定义的keymapvlaue是流程定义对象

for (ProcessDefinition pd : all) {

map.put(pd.getKey(), pd);

}

Collection<ProcessDefinition> result = map.values();

// 3,显示结果

for (ProcessDefinition pd : result) {

System.out.println("deploymentId=" + pd.getDeploymentId()// 

+ ",  >{key}-{version}

+ ",  name=" + pd.getName()

+ ",  key=" + pd.getKey()

+ ",  version=" + pd.getVersion());

}

1.4. 获取部署对象中的文件资源内容

// 资源的名称,就是 jbpm4_lob 表中的 NAME_ 列表值

String deploymentId = "90001";

String resourceName = "test.png"; 

InputStream in = processEngine.getRepositoryService()

.getResourceAsStream(deploymentId, resourceName);

1.5. 获取流程图中某活动的坐标

String processDefinitionId = "test-1"; // 流程定义的id

String activityName = "start1"; // 活动的名称

 

ActivityCoordinates c = processEngine.getRepositoryService()

.getActivityCoordinates(processDefinitionId, activityName);

System.out.println("x=" + c.getX() 

+ ",y=" + c.getY() 

+ ",width=" + c.getWidth() 

+ ",height=" + c.getHeight());

 

2. 执行流程实例

2.1. 启动流程实例

说明:流程实例创建后,直接就到开始活动后的第一个活动,不会在开始活动停留。

2.1.1. 示例代码1:使用指定key的最新版本的流程定义启动流程实例

ProcessInstance pi = processEngine.getExecutionService()

.startProcessInstanceByKey(processDefinitionKey);

2.1.2. 示例代码2:使用指定key的最新版本的流程定义启动流程实例,并设置一些流程变量

// 准备流程变量

Map<String, Object> variables = new HashMap<String, Object>();

variables.put("申请人", "张三");

variables.put("报销金额", 1000.00);

 

// 启动流程实例,并设置一些流程变量

ProcessInstance pi = processEngine.getExecutionService()

.startProcessInstanceByKey(processDefinitionKey, variables);

2.2. 向后执行一步(Signal

2.2.1. 示例代码1:向后执行一步,使用唯一的outcome离开活动

processEngine.getExecutionService().signalExecutionById(executionId);

2.2.2. 示例代码2:向后执行一步,使用唯一的outcome离开活动,并设置一些流程变量

Map<String, Object> variables = new HashMap<String, Object>();

variables.put("审批结果", "同意");

 

processEngine.getExecutionService()

.signalExecutionById(executionId, variables);

 

2.2.3. 示例代码3:向后执行一步,使用指定的outcome离开活动

String outcome= "to end1";

processEngine.getExecutionService()

.signalExecutionById(executionId, outcome);

2.2.4. 示例代码4:向后执行一步,使用指定的outcome离开活动,并设置一些流程变量

String outcome= "to end1";

Map<String, Object> variables = new HashMap<String, Object>();

variables.put("审批结果", "同意");

 

processEngine.getExecutionService()

.signalExecutionById(executionId, outcome, variables);

 

2.3. 查询任务

2.3.1. 查询个人任务列表

方式1TaskService.findPersonalTasks(userId);

 

方式2List<Task> list = taskService.createTaskQuery()

.assignee(userId)

.list();

 

// 显示任务信息

for (Task task : taskList) {

System.out.println(">id

+ ",name=" + task.getName()// 任务的名称

+ ",assignee=" + task.getAssignee()// 任务的办理人

+ ",createTime=" + task.getCreateTime() // 任务的创建(生成)的时间

+ ",executionId=" + task.getExecutionId());// 任务所属流程实例的id

}

2.3.2. 查询组任务列表

方式1 taskService.findGroupTasks(userId);

 

方式2 List<Task> list = processEngine.getTaskService()//

.createTaskQuery()//

.candidate(userId)//

.list();

2.4. 完成任务

2.4.1. 正常完成任务(也可以同时设置一些流程变量)

String taskId = "420001";

processEngine.getTaskService().completeTask(taskId);

 

processEngine.getTaskService().completeTask(taskId, outcome);

 

processEngine.getTaskService().completeTask(taskId, outcome, variables);

 

2.4.2. 自行控制任务完成后是否可向后流转

String taskId = "420001";

 

// 1设置为false代表:办理完任务后不向后移动(默认为true

TaskImpl taskImpl = (TaskImpl) processEngine

.getTaskService().getTask(taskId);

taskImpl.setSignalling(false); 

 

// 2,办理完任务

processEngine.getTaskService().completeTask(taskId);

2.5. 拾取任务

1, TaskService.takeTask(taskId, userId),拾取组任务到个人任务列表中,如果任务有assignee,则会抛异常。

2, processEngine.getTaskService().assignTask(taskId, userId),转交任务给其他人,(如果任务有assignee,则执行这个方法代表重新分配。也可以把assignee设为null表示组任务没有人办理了)

2.6. 设置与获取流程变量

2.6.1. 设置流程变量

2.6.1.1. 方式1:根据 executionId 设置或获取流程变量

ExecutionService.setVariable(executionId, name, value);

 

Object obj = executionService.getVariable(executionId, "请假人");

2.6.1.2. 方式2:根据 taskId 设置或获取流程变量

TaskService.setVariables(taskId, variables); // 一次设置多个变量

 

Object obj = executionService.getVariable(executionId, "请假人");

2.6.1.3. 流程变量所支持的值的类型(jBPM User Guide,7.2. Variable types)

7.2. Variable types

jBPM supports following Java types as process variables:

 java.lang.String 

 java.lang.Long 

 java.lang.Double 

 java.util.Date 

 java.lang.Boolean 

 java.lang.Character 

 java.lang.Byte 

 java.lang.Short 

 java.lang.Integer 

 java.lang.Float 

 byte[] (byte array) 

 char[] (char array) 

 hibernate entity with a long id 

 hibernate entity with a string id 

 serializable

For persistence of these variable, the type of the variable is checked in the order of this list. The first match will determine how the variable is stored. 

2.7. 直接结束流程实例(自己手工结束)

String processInstanceId =  "test.10001";

processEngine.getExecutionService()

.endProcessInstance(processInstanceId, ProcessInstance.STATE_ENDED);

 

第三章  jbpm与spring整合

1.1. 与Spring集成(jBPM4.4 Developers Guide, Chapter 17. Spring Integration

1.1.1. 在jbpm.cfg.xml

1,删除配置:<import resource="jbpm.tx.hibernate.cfg.xml" />

2,增加配置:<import resource="jbpm.tx.spring.cfg.xml" />

1.1.2. 在applicationContext.xml中配置

<!-- 配置ProcessEngine(整合jBPM4) -->

<!-- jbpmCfg是相对于classpath的相对路径,默认值为jbpm.cfg.xml -->

<bean  

class="org.jbpm.pvm.internal.processengine.SpringHelper">

<property name="jbpmCfg" value="jbpm.cfg.xml"></property>

</bean>

<bean  

factory-method="createProcessEngine" />

1.1.3. 测试

@Test // 测试ProcessEngine

public void testProcessEngine() {

ProcessEngine processEngine = (ProcessEngine)ac.getBean("processEngine");

Assert.assertNotNull(processEngine);

}

 

1.1.4. 注意事项

如果做了JBPM4.4Spring整合(使用了jbpm.tx.spring.cfg.xml),则在程序中就一定要使用Spring注入ProcessEngine,千万不能使用Configuration.getProcessEngine()生成ProcessEngine,因为这时内部的代码有以下逻辑:如果整合了Spring但没有ApplicationContext,就默认读取applicationContext.xml创建ApplicationContext实例并从中获取名为”ProcessEngine”的对象。而这时如果把pe = Configuration.getProcessEngine()写成某Spring中管理的bean的初始化代码,就会有无限循环,不停的创建ApplicationContext了!

1.2. 自行控制事务

1, 修改 jbpm.tx.hibernate.cfg.xml

a) 不让jBPM自行管理事务:去掉<standard-transaction-interceptor />

b) 让Jbpm使用SessionFactory.getCurrentSession():修改为 <hibernate-session current="true" />

2, 配置可以使用SessionFactory.getCurrentSession(),在jbpm.hibernate.cfg.xml 中配置:<property name="hibernate.current_session_context_class">thread</property>

3, 要使用同一个SessionFactory,且都要使用 SessionFactory.getCurrentSession() 获取 Session

a) 同一个SessionFactorySessionFactory sf = processEngine.get(SessionFactory.class)

b) 在 BaseDaoImpl 中增加:

  1. getSession() { return HibernateUtils.getSessionFactory().getCurrentSession(); }
  2. getProcessEngine(){ return org.jbpm.api.Configuration.getProcessEngine(); }

4, 统一的打开与提交或回滚事务:使用 OpenSessionInViewFilter 控制事务。

 

1.3. 启动Tomcat后,访问JSP时(使用的是MyEclipse自带的Tomcat,是6.0的版本),报错:   Caused by: java.lang.LinkageError: loader constraints violated when linking javax/el/ExpressionFactory class

at org.apache.jsp.WEB_002dINF.jsp.UserAction.loginUI_jsp._jspInit(loginUI_jsp.java:39)

at org.apache.jasper.runtime.HttpJspBase.init(HttpJspBase.java:52)

at org.apache.jasper.servlet.JspServletWrapper.getServlet(JspServletWrapper.java:159)

at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:329)

at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)

at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)

... 40 more

 

说明:原因是Jbpmjuel.jar, juel-engine.jar, juel-impl.jar包和Tomcat6.0中的el-api.jar包冲突了。

有三个解决办法:

1,方法一:在MyEclipsePreferences -> MyEclipse -> Application Servers -> Tomcat -> .. -> Paths 中配置 Append to classpath,选中 juel.jar, juel-engine.jar, juel-impl.jar 这三个jar包就可以了。

2,方法二:将 juel.jar, juel-engine.jar, juel-impl.jar 这三个包复制到tomcat6下 lib/ 中,并删除原来的el-api.jar

切记还要把工程中 WEB-INFlib 下的 juel.jar, juel-engine.jar, juel-impl.jar 删除,不然还是要冲突。

3,方法三:换成tomcat5.5,就没有问题了。

 

1.4. 完成流程实例中的最后一个任务时报错(任务实例结束时),或删除流程定义级联删除流程实例时,报错如下:

com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`itcastoa_20100909/jbpm4_execution`, CONSTRAINT `FK_EXEC_INSTANCE` FOREIGN KEY (`INSTANCE_`) REFERENCES `jbpm4_execution` (`DBID_`))

 

解决办法:把方言设为 MySQL5InnoDBDialect,不能是 MySQLDialect