编辑器加载中jbpm 入门实例(4)—— JBPM简单实例入门收藏到手机 转发 评论 2009-06-15 20:54 1、新建项目 主菜单“文件->新建->项目”,在弹出的对话框里,选中JBoss jbpm节点下的“ Process Project ”项。输入项目名:JbpmProject,项目建好后结构如图所示: 这个项目和通常 Eclipse 的项目结构有点不同,不过 这是一个现在非常流行的项目结构, src/java 存放源文件, test/java 存放相应的 JUnit 单元测试代码。 下面介绍一下各个文件: l MessageActionHandler ,自动生成的一个 ActionHandler 。不想要可以删掉。 l ehcache.xml cache 的配置文件,里面有很详解的英文说明。没有必要可以不用改它。 l hibernate.cfg.xml jBPM 是用 Hibernate 进行工作流的数据存储的,这个就是 Hibernate 的配置文件。后面我们将讲到如何配置这个文件。 l jbpm.cfg.xml jbpm 本身的配置文件。现在是空的,它用的是缺省配置,你想知道有哪些配置就去看这个文件E:\software\jbpm-starters-kit-3.1.2\jbpm\src\java.jbpm\org\jbpm\default.jbpm.cfg.xml l log4j.properties 这个是日志 API 包 log4j 的配置文件,用过 log4j 的都知道。 l SimpleProcessTest.java 这个是对最重要的流程配置文件的 processdefinition.xml 单元测试代码。这里表扬一点, jBPM 的优良设计使得它的可测试性非常之高,喜欢写 t 单元测试的人有福了。 l gpd.xml 用于生成流程图的定义文件。都是一些方框的坐标和长宽 l processdefinition.xml 这个是对最重要的流程配置文件,以后写流程要经常和它打交道。 l processimage.jpg 一个流程图 2、修改hibernate.cfg.cml配置文件 hibernate.cfg.xml 的默认设置是用 HSQL ,这是一个内存数据库,这种内存数据库用来代替项目实际所用的数据库来做单元测试挺不错的。 注:配置值可参考jbpm-starters-kit-3.1.2\jbpm-db 对应子目录下的 hibernate.properties 文件。 一、网上朋友给我mysql与oracle的配置如下: 1 、 MySQL 的更改如下: org.hibernate.dialect.MySQLDialect com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/jbpm root 123456 2 、 Oracle 的更改如下: org.hibernate.dialect.OracleDialect oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@192.168.123.10:1521:wxxrDB chengang chengang 如果你装了 Oracle 的客户端,并且 D:\oracle\ora92\network\ADMIN\tnsnames.ora 里做了如下的设置 WXXRDB_192.168.123.10 = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.123.10)(PORT = 1521)) ) (CONNECT_DATA = (SID = wxxrDB) (SERVER = DEDICATED) ) ) 则 Oracle 的 hibernate.connection.url 项也可以设为: jdbc:oracle:oci:@WXXRDB_192.168.123.10 二、我的配置修改 1、sqlserver
org.hibernate.dialect.SQLServerDialect
net.sourceforge.jtds.jdbc.Driver jdbc:jtds:sqlserver://localhost:1433/jbpm sa admin
3、完善库引用 主要是把你所用的数据库的 JDBC 库引用进来,如果你是下载的jbpm-starters-kit-3.1.2可能还要把 Hibernate 的 hibernate3.jar 加入到项目的库引用中。 4、开始流程 这里是一个很简单的请假流程,请假人提交假单给经理审批,经理审批后结束。 1 、定义流程 流程的定义文件是 processdefinition.xml ,这个是一个关键文件, jBPM 的很大一部份内容都是关于它的。在这里我们把原来自动生成的内容,稍做改动:
<?xml version="1.0" encoding="UTF-8"?>
我要请假
说明: 流程的名称改成了 helloworld 。(呵呵,也就是这里和 helloworld 有关了) 标签定义了三个数据:姓名、请假天数、说明。 标签定了 request 节点的一个流程转向,这里是转到 confirm 节点。 标签定义了流程由一个节点转到另一个节点时,所要执行的动作,动作封装在一个 ActionHandler 类中。比如这里当 request 到 confirm 结点时将执行 RequestAction 类的 execute 方法。 RequestAction下面还有一个 (请假理由),它对应于RequestAction 的属性 String reason 。 5、编写 ActionHandler 在上面 processdefinition.xml 里我们定义了两个 ActionHandler : RequestAction 、 ConfirmAction 。其代码如下: package com.stt.jbpm; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.exe.ExecutionContext; public class RequestAction implements ActionHandler { private static final long serialVersionUID = 1L; private String reason; @Override public void execute(ExecutionContext context) throws Exception { context.getContextInstance().setVariable("note", reason); } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } } 说明: ExecutionContext 是一个贯通流程的容器。它是个大宝箱,里面啥玩意都有,后面将更深入的提到。这里的 reasion 就是 processdefinition.xml 中的 ” 我要请假 ” package com.stt.jbpm; import org.jbpm.graph.def.ActionHandler; import org.jbpm.graph.exe.ExecutionContext; public class ConfirmAction implements ActionHandler { private static final long serialVersionUID = 1L; @Override public void execute(ExecutionContext context) throws Exception { context.getContextInstance().setVariable("note", "准假"); } } 6、部署processdefinition.xml 我们要把 processdefinition.xml 的流程定义的数据部署到数据库中,因为 jBPM 在正式运行的时候不是去读 processdefinition.xml 文件,而是去读数据库中的流程定义。 这里写了一个个 JUnit 程序来部署 processdefinition.xml ,当然你用普通的 Java Main 也可以。 package com.stt.jbpm; import junit.framework.TestCase; import org.jbpm.JbpmConfiguration; import org.jbpm.JbpmContext; import org.jbpm.graph.def.ProcessDefinition; /** * 流程部署 * @author USER * */ public class DeployProcessTest extends TestCase { /** * 本方法执行完毕后,检查jbpm_processdefinition表会多了一条记录 * @throws Exception */ public void testDeployProcess() throws Exception { //从jbpm.cfg.xml取得jbpm的配置 JbpmConfiguration config = JbpmConfiguration.getInstance(); //创建一个jbpm容器 JbpmContext jbpmContext = config.createJbpmContext(); //由processdifinition.xml生成相对应的流程定义类ProcessDefinition ProcessDefinition processDefinition = ProcessDefinition.parseXmlResource("simple/processdefinition.xml"); //利用容器的方法将流程定义数据部署到数据库上 jbpmContext.deployProcessDefinition(processDefinition); //关闭jbpmContext jbpmContext.close(); } } 运行此程序,在控制台打印了一些日志,通过。如果出错,仔佃阅读出错信息以判断错误原因,并确定你按照前面两节:“修改 hibernate.cfg.xml ”和“完善库引用”的内容做好了设置。 报错: 14:44:50,921 [main] INFO JpdlXmlReader : process xml information: no swimlane or assignment specified for task '
' 14:44:55,765 [main] ERROR JDBCExceptionReporter : Table not found in statement [select top ? processdef0_.ID_ as ID1_0_, processdef0_.NAME_ as NAME3_0_, processdef0_.DESCRIPTION_ as DESCRIPT4_0_, processdef0_.VERSION_ as VERSION5_0_, processdef0_.ISTERMINATIONIMPLICIT_ as ISTERMIN6_0_, processdef0_.STARTSTATE_ as STARTSTATE7_0_ from JBPM_PROCESSDEFINITION processdef0_ where processdef0_.NAME_=? order by processdef0_.VERSION_ desc] 14:44:55,765 [main] ERROR GraphSession : org.hibernate.exception.SQLGrammarException: could not execute query 咋回事呢?查看hibernate.cfg.xml配置,原来中午修改的配置忘了保存。重新修改,再此运行,OK搞定。 到底成功了没呢? 7、从数据库中的查看部署效果 查询以下各表: jbpm_processdefinition 一个流程定义文件对应一条记录,可记录多个流程定义文件,可记录一个流程定义文件的对个版本。 jbpm_action 记录 ActionHandler 的对象实例(以名称为标识) jbpm_delegation 记录了 ActionHandler 全类名,以便于用反射方式来加载 jbpm_envent 它的 transition 引用了 Jbpm_transition 表的 id ,再看其它字段,估计此表是表示流程转向事件的一个实例,或者是一个各表之间的联接表。 jbpm_node 流程结点 jbpm_transition 流程的转向定义 jbpm_variableaccess 流程中携带的变量。 ACCESS 字段是这些变量的读写权限 例如:查询 jbpm_processdefinition 表,你会发现多了一条记录,查询结果如下: 字段名 字段值 ID_ 1 CLASS_ p NAME_ helloworld DESCRIPTION_ NULL VERSION_ 1 ISTERMINATIONIMPLICIT_ 0 STARTSTATE_ 1 表中各字段的作用由字段名也能知晓一二。其他表的查询下面寄不再一一列出。 select * from jbpm_processdefinition select * from jbpm_action select * from jbpm_delegation select * from jbpm_event select * from jbpm_node select * from jbpm_transition select * from jbpm_variableaccess 可以看到数据库中中文“我要请假”全部为“???”,这中文乱码该如何解决呢?下面我贴一下网上其他朋友的经验: l JBPM在Mysql 4.0以下运行有问题,主要是select语句的问题。 l JBPM 数据库默认的字符段是255个字符,有时需要修改,比如存储文件的时候。JBPM数据库默认的字段类型的定义有时候不一定适合需要,要手工进行修改。 l 对于工作流定义文件-processdifinition.xml的中文问题解决方案如下: 1) processdefinition.xml的Encoding设定成”GBK“
<?xml version="1.0" encoding="GBK"?>2) 对于MySQL,hibernate相应的hibernate.connection.url设定成:jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=GBK 对于Sqlserver设定为: jdbc:jtds:sqlserver://localhost:1433;SelectMethod=cursor;characterEncoding=GBK;DatabaseName=jbpm 对于Oracle来讲,Hibernate使用的C3P0连接池有问题,可用最新的C3P0代替。 3) 文字在显示的时候要用toGBK转换才能正确显示。 如果XML定义文件采用UTF8定义,显示时可不用做GBK转换。 SQLSERVER 2005 中文乱码问题: 本人用的是sqlserver 2005,当保存到数据库中后,表中全部为乱码。按照上面的方法还是无法解决。Hibernate配置文件里里关于解决乱码问题所采用的统一编码方式也尝试过,数据库里依然是“???”,究竟是哪里出了问题? 我想是不是数据库根本就无法保存中文,在数据库乱码字段中的值修改为中文,输完后发现马上变成了“???”,嘿,还真是数据库本身出了问题。 由于在安装sqlserver 2005时帮助那些安装失败,所以只好查看机子上原来安装的sqlserver 2000中的帮助,输入“汉字”里面有一篇:使用 Unicode 数据 Unicode 规格通过采用两个字节编码每个字符使这个问题迎刃而解。转换最通用商业语言的单一规格具有足够多的 2 字节的模式 (65,536)。因为所有的 Unicode 系统均一致地采用同样的位模式来代表所有的字符,所以当从一个系统转到另一个系统时,将不会存在未正确转换字符的问题。通过在整个系统中使用 Unicode 数据类型,可尽量减少字符转换问题。 在 Microsoft SQL Server 中,以下数据类型支持 Unicode 数据: nchar , nvarchar , ntext 把乱码对应字段类型varchar(4000)改为nvarchar(4000) 再次在乱码字段输入中文,输完后发现正常,“???”没再回来。^_^ 趁热打铁,运行程序,o(∩_∩)o…哈哈 ,ok!一切正常! 把原来的建表语句中的varchar全改为nvarchar,重新建。 8、开发客户端测试 本文不写 JSP ,而改采用 JUnit 的形式,输出则用 System.out.println。 package com.stt.jbpm; import org.jbpm.JbpmConfiguration; import org.jbpm.JbpmContext; import org.jbpm.context.exe.ContextInstance; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.ProcessInstance; import junit.framework.TestCase; public class ClientProcessTest extends TestCase { private JbpmConfiguration config = JbpmConfiguration.getInstance(); private JbpmContext ctx = config.createJbpmContext(); //helloworld对应于jbpm_processdefinition表的name字段,也即processdefinition.xml的name //这个值得取比较耗时,实际项目里最好和"数据库的JDBC连接"一样,让它共享,不要频繁打开关闭。 private ProcessDefinition processDefinition = ctx.getGraphSession().findLatestProcessDefinition("helloworld"); /** * 测试方法入口 */ public void testNewRequest(){ long id = newRequest(); System.out.println("id="+id); checkNewRequest(id); confirmRequest(id); checkconfirmRequest(id); ctx.close(); //关闭jbpm容器 } /** * 创建一个请假单 * @return 本次请假流程ID */ private long newRequest() { //创建一个新流程 ProcessInstance pi = processDefinition.createProcessInstance(); //取得流程的数据环境 ContextInstance ci = pi.getContextInstance(); //创建一张请假单 ci.setVariable("name", "婷婷"); ci.setVariable("day", 2); //请假申请结束,转到下一个流程节点 pi.signal(); return pi.getId(); } /** * 检测请假单的数据 * @param id */ private void checkNewRequest(long id) { //从数据库提取数据源 ProcessInstance pi = ctx.loadProcessInstance(id); //取得流程的数据环境 ContextInstance ci = pi.getContextInstance(); //创建一张请假单 assertEquals("婷婷",ci.getVariable("name")); assertEquals(Integer.valueOf(2),ci.getVariable("day")); assertEquals("我要请假",ci.getVariable("note")); //当前是结点为confirm assertEquals(pi.getRootToken().getNode().getName(),"confirm"); //流程还未结束 assertFalse(pi.hasEnded()); } /** * 审批请假申请 * @param id */ private void confirmRequest(long id) { //从数据库提取数据源 ProcessInstance pi = ctx.loadProcessInstance(id); //取得流程的数据环境 ContextInstance ci = pi.getContextInstance(); //假如,审批不通过 ci.setVariable("note", "不准请假"); //审批结束,到下一个流程结点 pi.signal(); } /** * 申请者取得最后审批结果 * @param id */ private void checkconfirmRequest(long id) { //从数据库提取数据源 ProcessInstance pi = ctx.loadProcessInstance(id); //取得流程的数据环境 ContextInstance ci = pi.getContextInstance(); //ConfirmAction类在signal后执行,所以覆盖了经理的审批意见(注意这就是为何在此不会报错的原因) assertEquals("准假",ci.getVariable("note")); //流程结束了 assertTrue(pi.hasEnded()); } } 查看表JBPM_VARIABLEINSTANCE中将有三条记录分别是name的值为“婷婷”,day为2,note的值为“准假”。 表JBPM_PROCESSINSTANCE中添加了一条记录可知道该流程的开始日期(START_字段指定)和结束日期(END_字段指定)。 这个简单的入门实例就到此结束。后面将开始一点点进入与业务相关的流程学习开发。 ...