先說明這個隻是一個例子而已,簡單的介紹了一些寫法,你真的了解了以後完全可以寫出比這個更好的代碼來。
網上随便找了個請假的流程圖,在此先謝謝提供圖檔的人:
使用jbpm工具畫出流程圖,中文好像是亂碼,是以改為英文:
leave.jpdl.xml内容:
<?xml version="1.0" encoding="UTF-8"?>
<process name="leave" xmlns="http://jbpm.org/4.3/jpdl">
<start g="159,47,48,48" name="start1">
<transition to="exclusive1"/>
</start>
<decision expr="#{job}" g="161,152,48,48" name="exclusive1">
<transition g="42,179:43,-27" name="isChief" to="boosApprove"/>
<transition g="316,175:-83,-23" name="isnotChief" to="chiefApprove"/>
</decision>
<state g="-3,220,92,52" name="boosApprove">
<transition g="47,340:" to="sendEmail"/>
</state>
<state g="270,214,92,52" name="chiefApprove">
<transition to="exclusive2"/>
</state>
<decision expr="#{day}" g="160,219,48,48" name="exclusive2">
<transition g="-2,-20" name="gt10" to="boosApprove"/>
<transition g="186,323:12,-47" name="le10" to="sendEmail"/>
</decision>
<end g="171,410,48,48" name="end"/>
<state g="146,313,92,52" name="sendEmail">
<transition to="end"/>
</state>
</process>
分析之後,有如下一些表:
使用者user_
角色role_(簡化到user_)
請假單leave_
假設有這麼幾個使用者:
陳均 --普通員工
唐平 --級别最高的,BOOS,老闆
胡傑 --級别比較高的,chief主管
張小 --普通員工
使用者測試資料:
INSERT INTO `user_` VALUES ('9', '陳均', '普通員工');
INSERT INTO `user_` VALUES ('10', '胡傑', '主管');
INSERT INTO `user_` VALUES ('11', '唐平', '老闆');
INSERT INTO `user_` VALUES ('12', '張小', '普通員工');
現在內建jbpm4.3,hibernate3,spring2.5,struts2.1.8。
系統初步設計如圖:
1.因為jbpm裡面帶有hibernate,是以建立web項目後,導入jbpm-4.3\lib下的所有包,導入jbpm-4.3\jbpm.jar,
把jbpm4.3\lib\下面得juel.jar,juel-engine.jar,juel-impl.jar放到tomcat的lib下面。導入spring2.5的jar,導入struts2.1.8所需jar包。以下jar包不是最簡,有些不是必須的。
spring2.5所需jar包清單:
aspectjrt.jar
aspectjweaver.jar
cglib-nodep-2.1_3.jar
common-annotations.jar
commons-logging.jar
log4j-1.2.15.jar
spring.jar
spring-webmvc-struts.jar
------------------------------------
struts2.1.8所需jar包清單:
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
freemarker-2.3.15.jar
ognl-2.7.3.jar
struts2-core-2.1.8.1.jar
struts2-dojo-plugin-2.1.8.1.jar
struts2-spring-plugin-2.1.8.1.jar
xwork-core-2.1.6.jar
-----------------------------------
資料庫和資料源所需jar包:
c3p0-0.9.1.2.jar
mysql-connector-java-5.1.7-bin.jar
找到jbpm-4.3\install\src\cfg\jbpm\下的spring.jbpm.cfg.xml檔案,放入項目的src處,改名為jbpm.cfg.xml.
在項目src下面建立applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 開啟注解配置 -->
<context:annotation-config />
<!-- 對指定的包進行元件掃描 -->
<context:component-scan base-package="org.forever.leave" />
<!-- 配置資料源,導入c3p0-0.9.1.2.jar,mysql-connector-java-5.1.7-bin.jar -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="jdbcUrl">
<value>jdbc:mysql://localhost:3306/jbpmdb</value>
</property>
<property name="user">
<value>root</value>
</property>
<property name="password">
<value>root</value>
</property>
</bean>
<!-- 內建hibernate配置 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="hibernateProperties" ref="hibernateProperties" />
<property name="mappingLocations">
<list>
<value>classpath*:org/forever/leave/entities/*.hbm.xml</value>
<value>classpath:jbpm.repository.hbm.xml</value>
<!-- 以下幾個jbpm.*.hbm.xml由jBPM自帶 -->
<value>classpath:jbpm.execution.hbm.xml</value>
<value>classpath:jbpm.history.hbm.xml</value>
<value>classpath:jbpm.task.hbm.xml</value>
<value>classpath:jbpm.identity.hbm.xml</value>
</list>
</property>
</bean>
<bean name="hibernateProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- jbpm配置 -->
<bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper" />
<bean id="processEngine" factory-bean="springHelper"
factory-method="createProcessEngine" />
<!-- 模闆配置自己寫的,不是必須的 -->
<bean id="jbpmTemplate" class="org.forever.leave.jbpm.JbpmTemplate">
<property name="processEngine" ref="processEngine"></property>
</bean>
<!-- 資料層用的模闆工具,不是必須的 -->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 事務配置,必須 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 切面配置 -->
<aop:config>
<aop:pointcut expression="execution(* org.forever.leave.biz..*.*(..))"
id="transactionPointcut" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut" />
</aop:config>
<!-- 通知配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED" />
<tx:method name="find*" read-only="true" propagation="NOT_SUPPORTED" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
</beans>
添加 log4j.properties檔案;
模闆類JbpmTemplate:
package org.forever.leave.jbpm;
import java.util.List;
import java.util.Map;
import org.jbpm.api.Execution;
import org.jbpm.api.ExecutionService;
import org.jbpm.api.HistoryService;
import org.jbpm.api.ManagementService;
import org.jbpm.api.ProcessEngine;
import org.jbpm.api.ProcessInstance;
import org.jbpm.api.RepositoryService;
import org.jbpm.api.TaskService;
import org.jbpm.api.task.Task;
/**
* jbpm模闆類(初步實作)
*
* @author Administrator
*
*/
public class JbpmTemplate {
/**
* 部署流程到資料庫
*
* @param resourceName
* 資源檔案名字 比如(org/forever/jbpm/jpdl/process.jpdl.xml)
* @return 傳回流程定義id(格式:key-version)
*/
public String Deploy(String resourceName) {
return repositoryService.createDeployment().addResourceFromClasspath(
resourceName).deploy();
}
/**
* 建立一個新的流程執行個體
*
* @param processDefinitionKey
* (process.jpdl.xml中process标簽的key)
* @param processInstanceKey
* (使用者給的key,比如一個請假單的id)
* @return 流程執行個體
*/
public ProcessInstance addProcessInstance(String processDefinitionKey,
String processInstanceKey) {
return executionService.startProcessInstanceByKey(processDefinitionKey,
processInstanceKey);
}
/**
* 建立一個新的流程執行個體
* @param processDefinitionKey(process.jpdl.xml中process标簽的key)
* @param variables 該流程執行個體要用到的變量
* @param processInstanceKey(使用者給定的業務key)
* @return
*/
public ProcessInstance addProcessInstance(
String processDefinitionKey,
Map<String, ?> variables,
String processInstanceKey){
return executionService.startProcessInstanceByKey(processDefinitionKey, variables, processInstanceKey);
}
/**
* 送出任務
* @param taskId 任務id
*/
public void completeTask(String taskId){
taskService.completeTask(taskId);
}
/**
* 将任務流轉到指定名字的流程中去
* @param taskId
* @param outcome
*/
public void completeTask(String taskId,String outcome){
taskService.completeTask(taskId, outcome);
}
/**
* 根據key擷取流程執行個體(這裡我使用的uuid)
*
* @param key
* (對應于資料庫表jbpm4_execution中的KEY_字段)
* @return 傳回查找到得流程執行個體,沒有傳回null
*/
public ProcessInstance getProcessInstance(String key) {
return executionService.createProcessInstanceQuery()
.processInstanceKey(key).uniqueResult();
}
/**
* 根據executionId擷取指定的變量值
* @param executionId
* @param variableName
* @return
*/
public Object getVariableByexecutionId(String executionId,String variableName){
return executionService.getVariable(executionId, variableName);
}
/**
* 根據任務id擷取指定變量值
* @param taskId
* @param variableName
* @return
*/
public Object getVariableByTaskId(String taskId,String variableName){
return taskService.getVariable(taskId, variableName);
}
/**
* 擷取指定使用者名字的任務
* @param userId
* @return
*/
public List<Task> findPersonalTasks(String userId){
return taskService.findPersonalTasks(userId);
}
/**
* 根據任務id擷取任務
* @param taskId
* @return
*/
public Task getTask(String taskId) {
return taskService.getTask(taskId);
}
/**
* 根據流程執行個體id擷取
* @param executionId
* @return
*/
public Execution findExecutionById(String executionId) {
return executionService.findExecutionById(executionId);
}
/**
* 徹底删除檔案的部署
*
* @param deploymentId流程定義id
*/
public void deleteDeploymentCascade(String deploymentId) {
repositoryService.deleteDeploymentCascade(deploymentId);
}
public JbpmTemplate() {
}
public JbpmTemplate(ProcessEngine processEngine) {
this.processEngine = processEngine;
repositoryService = processEngine.getRepositoryService();
executionService = processEngine.getExecutionService();
taskService = processEngine.getTaskService();
historyService = processEngine.getHistoryService();
managementService = processEngine.getManagementService();
}
private ProcessEngine processEngine;
private RepositoryService repositoryService = null;
private ExecutionService executionService = null;
private TaskService taskService = null;
private HistoryService historyService = null;
private ManagementService managementService = null;
public ProcessEngine getProcessEngine() {
return processEngine;
}
public void setProcessEngine(ProcessEngine processEngine) {
this.processEngine = processEngine;
System.out.println(processEngine);
repositoryService = processEngine.getRepositoryService();
executionService = processEngine.getExecutionService();
taskService = processEngine.getTaskService();
historyService = processEngine.getHistoryService();
managementService = processEngine.getManagementService();
}
//省略get和set方法
}
建立測試類Test:
import java.util.UUID;
import org.forever.leave.jbpm.JbpmTemplate;
import org.jbpm.api.ProcessInstance;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
JbpmTemplate jbpmTemplate = (JbpmTemplate) context
.getBean("jbpmTemplate");
System.out.println(jbpmTemplate);
jbpmTemplate.Deploy("org/forever/leave/jbpm/jpdl/leave.jpdl.xml");
UUID uuid = UUID.randomUUID();
}
}
控制台輸出沒有報錯說明jbpm和spring初步內建成功;
通路http://localhost:8080/leave/user/queryList.action擷取到使用者清單資訊,說明內建成功。
項目中需要修改mysql方言為org.hibernate.dialect.MySQLInnoDBDialect,事務service改為biz,剛發現的,呵呵
下一步進行業務的實作。
未完
因為朋友需要,是以對其進行了簡單的實作:
在實作中發現內建的時候,如果你用mysql資料庫,請設定你的方言為:org.hibernate.dialect.MySQLInnoDBDialect
先看一哈這個實作了簡單業務的一個圖,在此用的中文,也是網上找的,呵呵,我都變懶了哈:
對應xml檔案:
<?xml version="1.0" encoding="UTF-8"?>
<process key="leave" name="leave" xmlns="http://jbpm.org/4.3/jpdl">
<start g="201,14,48,48" name="開始">
<transition g="-42,-10" name="請假" to="填寫請假單"/>
</start>
<task assignee="writerForm" g="178,87,92,52" name="填寫請假單">
<transition g="-97,2" name="判斷是不是經理" to="是不是經理"/>
</task>
<decision expr="#{manager}" g="204,158,48,48" name="是不是經理">
<transition g="-23,-11" name="否" to="經理稽核"/>
<transition g="14,-11" name="是" to="老闆審批"/>
</decision>
<task assignee="#{username}" g="103,252,92,52" name="經理稽核">
<transition g="150,450:10,-21" name="經理準許" to="結束"/>
<transition g="-22,-22" name="請假天數>5" to="老闆審批"/>
<transition g="-61,-1" name="經理不準許" to="終止"/>
<transition g="149,114:-55,82" name="經理駁回" to="填寫請假單"/>
</task>
<!-- 這裡隻有一個老闆,是以寫死了,如果有多個老闆,寫法同上,業務就會改變 -->
<task assignee="張傑" g="278,251,92,52" name="老闆審批">
<transition g="326,450:-58,-24" name="老闆準許" to="結束"/>
<transition g="7,0" name="老闆不準許" to="終止"/>
<transition g="323,114:13,61" name="老闆駁回" to="填寫請假單"/>
</task>
<end g="219,429,48,48" name="結束" state="confirm"/>
<end g="220,360,48,48" name="終止" state="dissent"/>
</process>
寫了個經理審批的測試類過程:
package org.forever.leave.biz.test;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.forever.leave.entities.Leave;
import org.forever.leave.entities.User;
import org.jbpm.api.ProcessInstance;
import org.jbpm.api.task.Task;
import org.junit.Test;
//請假測試
//運作順序
//testDeploy()
//testCresteLeave()
//testStart()
//testGetTask()
//testGetLeave()
public class LeaveTest extends BaseTest{
//部署流程
@Test
public void testDeploy(){
String resourceName ="org/forever/leave/jbpm/jpdl/leave.jpdl.xml";
jbpmTemplate.Deploy(resourceName);
}
//建立請假單
@Test
public void testCresteLeave(){
//現假設使用者陳均登陸了系統,然後進行請假
String loginName = "cj";//登陸者
String password = "cj";//登陸密碼
User user = userDao.get(loginName,password);
//status=建立,就是未送出的
UUID uuid = UUID.randomUUID();
Leave leave = new Leave(user, 3,new Date(),"建立", "生病了");
leave.setLeaveId(uuid.toString());
//儲存到資料庫
leaveDao.save(leave);
}
// 啟動流程執行個體,送出請假申請
//注意(如果要對該測試類成功測試,首先确儲存在juel-engine.jar,juel-impl.jar,juel.jar)
//部署到tomcat裡面的時候就把juel.jar,juel-engine.jar,juel-impl.jar放到tomcat的lib下面
@Test
public void testStart() {
//進行業務流轉所需的變量
String loginName = "cj";//登陸者
String password = "cj";//登陸密碼
User user = userDao.get(loginName,password);
// 3.啟動流程執行個體,綁定業務key,key最好是唯一的
List<?> list = leaveDao.findByUserLeave(user.getUserId());//該使用者可能有多次請假的記錄
//假設使用者選擇的是index=0的那個請假單
Leave leave = (Leave)list.get(0);
String position = user.getPosition();//使用者的職位
Map<String, Object> variables = new HashMap<String, Object>();//流程中要用到的變量資訊
variables.put("leaveId",leave.getLeaveId());//存放該執行個體的請假單
if("員工".equals(position)){//如果是員工請假
variables.put("manager", "否");
variables.put("username","胡傑");//指定一個經理進行審批
}else if("經理".equals(position)){//如果是經理請假
variables.put("manager", "是");
//隻有一個boos,是以在xml中指定了,在此就不用指定了
}
//此時就擷取到了該請假單的id
//通過該leaveId來綁定一個流程執行個體
ProcessInstance processInstance = jbpmTemplate.addProcessInstance("leave",variables, leave.getLeaveId());
//該表單到時候是在web頁面進行申請時填寫好的
System.out.println("請假單已填寫:" + processInstance.isActive("填寫請假單"));
String taskId = jbpmTemplate.findPersonalTasks("writerForm").get(0).getId();
//讓任務向下流轉,送出任務
jbpmTemplate.completeTask(taskId);
}
//擷取任務集合
@Test
public void testGetTask(){
//經理登陸系統,擷取審批任務
String username = "胡傑";
List<Leave> leaves = new ArrayList<Leave>();//該經理可能對多個請假單審批,該集合提供給頁面使用的
List<Task> list = jbpmTemplate.findPersonalTasks(username);
if(list.size()==0){
System.out.println(username + "沒有任務.........");
}
else{
for (Task task : list) {
System.out.println("任務名字:" + task.getName());
System.out.println("任務參與者:" + task.getAssignee());
String taskId = task.getId();
String leaveId = (String) jbpmTemplate.getVariableByTaskId(taskId, "leaveId");
Leave leave = leaveDao.findbyIdLeave(leaveId);
leave.setTaskId(taskId);
leaves.add(leave);
}
}
//頁面顯示,并全部通過審批
for (Leave leave : leaves) {
System.out.println(leave);
//準許流程
ProcessInstance processInstance = jbpmTemplate.getProcessInstance(leave.getLeaveId());
String taskId = leave.getTaskId();
int day = leave.getDay();//請假天數
if(day>5 && true){//如果大于5天,并且經理準許,也要送出給boos稽核
jbpmTemplate.completeTask(taskId, "請假天數>5");
}else{//直接通過,既讓任務流轉到結束
jbpmTemplate.completeTask(taskId,"經理準許");
}
System.out.println("審批結果:" + processInstance.getState());
leave.setStatus("通過");
leaveDao.update(leave);//更新結果
}
}
//擷取指定使用者的請假單集合
@Test
public void testGetLeave(){
//進行業務流轉所需的變量
String loginName = "cj";//登陸者
String password = "cj";//登陸密碼
User user = userDao.get(loginName,password);
//頁面顯示用
List<?> list = leaveDao.findByUserLeave(user.getUserId());
for (Object object : list) {
System.out.println(object);
//是否已經申請
//已經送出的請假單不能進行删除操作(是以慎重,呵呵)
//建立狀态的請假單可以進行删除操作
}
}
}
web版的我截幾個效果圖,具體過程你們下載下傳來感受吧:
login.jsp登陸頁面http://localhost:90/leave/login.jsp:第一次需要部署一哈
普通員工登陸使用者名和密碼為cj:
經理使用者名和密碼為hj,登陸後如圖:
老闆的使用者名和密碼zxp,進去後如圖:
項目裡面帶有資料庫腳本。jar自己加哈,有什麼問題,歡迎交流