重点:
1、 复习 JBPM 工作流开发流程
2、 完成 中转配送流程
3、 流程实例监控 模块
4、 异常处理
5、 二级缓存
导入jar包 (使用 maven坐标导入 )
配置文件 jbpm.cfg.xml 核心配置文件, 默认引入 hibernate 配置文件 jbpm.hibernate.cfg.xml , 如果整合Spring ,引入
将hibernate 配置权力,交给Spring
引入hbm
jbpm.repository.hbm.xml
jbpm.execution.hbm.xml
jbpm.history.hbm.xml
jbpm.task.hbm.xml
jbpm.identity.hbm.xml
Spring 整合 JBPM ,需要通过 SpringHelper 工厂类,创建 ProcessEngine 流程引擎
<bean id="springHelper" class="org.jbpm.pvm.internal.processengine.SpringHelper">
<property name="jbpmCfg" value="jbpm.cfg.xml">property>
bean>
<bean id="processEngine" factory-bean="springHelper"
factory-method="createProcessEngine" />
注意问题 :
1、 MySQ整合,需要使用
org.hibernate.dialect.MySQL5InnoDBDialect 方言
2、 JBPM 依赖 juel jar包, 会和 tomcat发布/lib/el-api.jar 冲突, 解决将三个jar放入tomcat/lib,不要放入项目WEB-INF/lib
在线设计器 和 线下设计器 ,通过设计 生成 jpdl.xml 和 png ,在线设计器直接部署,线下设计器,需要上传zip压缩包!
不同业务流程,启动时,方式不同
中转配送流程,通过 工作单审批 功能, 审批工作单,启动中转流程
通过 key 启动 ,默认启动相同 key 最高版本的流程
在流程实例,关联流程变量 ZhongZhuanInfo , 关联流程中 所有任务节点对应 数据
任务节点,使用 candidate-groups 组任务办理方式 (要根据业务使用 swimlanes)
将系统用户角色管理 和 JBPM用户和组关联 起来!
添加系统用户,添加JBPM的用户
添加系统角色,添加JBPM的用户组
为用户授予角色, 完成JBPM 用户 和 组关系建立
组任务查看 —- 拾取组任务查看 — 个人任务 — 个人任务 办理
服务器查询任务列表 List
,但是Task接口没有获取流程变量方法,使用 TaskImpl 实现类 API ,显示流程业务数据 !
通过
节点 form 属性,执行任务办理表单(页面), 通过 Task对象获得form页面, 点击办理时,跳转到不同页面办理
将业务数据,关联PO对象,进行持久化
将业务数据,关联流程实例上
办理任务,流转自动流转
在办理中转环节任务时,使用自由流转技术(动态Transition), 通过当前节点,流向任何节点
面试题 : 如果流程已经写好了,需要在流程中 新增一个节点,如何做 ?
方案一: 修改 流程图,重新发布,版本+1 ,再次启动该流程 使用新流程定义 (问题, 原来流程 无法使用 新流程定义 )
方案二 : 修改已经发布的流程定义 ,数据保存 jbpm4_lob 表,是二进制blob ,先通过数据 读取blob,成为 InputStream , 使用 dom4j 加载到内存 ,使用dom4j 为流程添加新节点 ,将内存数据回显 lob表,流程被修改
入库、出库、配送签收 三个任务节点 办理
当办理入库任务,跳转 instore_complete.jsp
当办理出库任务,跳转 outstore_complete.jsp
当办理配送签收任务时,跳转 receiveinfo_complete.jsp
编写 三个任务节点 办理的业务代码
入库, TaskAction 提供 instorecomplete 方法
出库: TaskAction 提供 outstorecomplete 方法
配送签收 : TaskAction 提供 receiveinfocomplete 方法
在 BaseService 注入 inStoreDAO、outStoreDAO、receiveGoodsInfoDAO
办理入库任务示例代码:
配置结果集
<result name="instorecompleteSUCCESS" type="redirectAction">task_findpersonaltaskresult>
<result name="outstorecompleteSUCCESS" type="redirectAction">task_findpersonaltaskresult>
<result name="receiveinfocompleteSUCCESS" type="redirectAction">task_findpersonaltaskresult>
问题: 如何查看正在的运行实例? 如何查看已经完成流程? 如何查看流程实例变量? 如何流程实例运行到了哪个节点 ?
查看正在运行流程实例信息,jbpm4_execution 表 —- ExecutionService
查看已经完成流程实例信息,jbpm4_hist_procinst表 — HistoryService
/WEB-INF/pages/workflow/processinstance.jsp 流程实例列表页面
修改 admin.jsp 系统菜单
{ "id":"1005", "pId":"100", "name":"查看正在运行的任务",
"t":"","page":"processinstance_list.action"}
编写服务器代码 ProcessInstanceAction
public class ProcessInstanceAction extends BaseAction {
}
编写list方法,查看所有正在运行流程实例信息
// 获得ExecutionService
ExecutionService executionService = processEngine.getExecutionService();
ProcessInstanceQuery query = executionService.createProcessInstanceQuery();
List processInstances = query.list();
配置结果集
<action name="processinstance_*" class="processInstanceAction" method="{1}">
<result name="listSUCCESS">/WEB-INF/pages/workflow/processinstance.jspresult>
action>
问题: 在显示流程实例列表时,能否显示业务数据呢?
ProcessInstance 接口并没有提供 业务数据显示方法, 可以看 ExecutionImpl 实现类 API
显示业务数据时,控制datagrid 自动换行
除了显示流程图之外,绘制红色标记框,标记当前节点
function show(processInstacneId){
// 弹出页面
window.showModalDialog("${pageContext.request.contextPath}/processinstance_showpng.action?processInstacneId="+processInstacneId);
}
先根据 实例id ,查询 发布id 和图片name
在 ProcessInstanceAction 添加 showpng 方法
跳转 viewpng.jsp
<result name="showpngSUCCESS">/WEB-INF/pages/workflow/viewpng.jspresult>
"${pageContext.request.contextPath }/processdefinition_viewpng.action
?deploymentId=${deploymentId}&imageResourceName=${imageResourceName}" />
从 JBPM中获得当前任务节点坐标,绘制红框
RepositoryService 提供
问题: 一个流程有几个当前活动节点 ?
不一定是一个 ,因为 fork/join
获得活动名称
<error-page>
<error-code>404error-code>
<location>/404.jsplocation>
error-page>
<global-exception-mappings>
<exception-mapping result="error" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>
<global-results>
<result name="error">/error.jspresult>
global-results>
问题: 只是使用 友好页面,控制用户感受,进行异常处理,不够灵活和细粒度
public class MyExceptionInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
String result = null;
try {
result = invocation.invoke();
} catch (Exception e) {
// 进行异常的捕获和处理
result = "error";
}
return result;
}
}
<interceptor-stack name="privilegeStack">
<interceptor-ref name="myexception">interceptor-ref>
<interceptor-ref name="defaultStack">interceptor-ref>
<interceptor-ref name="login">interceptor-ref>
<interceptor-ref name="privilege">interceptor-ref>
interceptor-stack>
在异常拦截器,发生异常后,进行细节处理 (记录日志、发送邮件)
能够解决异常,进行捕获解决
如果异常无法解决,是否直接抛出? 否定
举例 DAO层发生SQL异常, 抛给Service 层,Service层根本无能力解决
企业会 先catch 这个异常, 转换为一个业务运行异常 抛出
MoneyNotEnoughException 余额不足异常
当抛出具体业务异常, 上层更知道如何处理 !
非ajax请求 ,返回 错误页面
ajax 请求,返回 json 异常信息
根据 请求头信息
来分辨是否为 ajax 请求
// 判断是否为ajax请求
if (ServletActionContext.getRequest().getHeader("X-Requested-With") != null) {
// ajax 请求
Map<String, Object> map = new HashMap<String, Object>();
map.put("result", "failure");
map.put("msg", "修改密码失败," + e.getMessage());
ActionContext.getContext().put("map", map);
result = "errorjson";
} else {
result = "error";
}
配置 json 结果页面
<result name="errorjson" type="json">
<param name="root">mapparam>
result>
问题: 什么是二级缓存 ? 二级缓存是如何存储的? 二级缓存如何使用 ?
Hibernate 提供一级缓存(Session 范围)和二级缓存(SessionFactory 范围), 一级缓存内置,
SessionFactory 缓存两部分内容 ,一部分 cfg文件、hbm文件配置内容 (命名查询) , 另一部分 数据缓存 (需要配置,引用外部框架 )
二级缓存, 支持 EhCache 、OSCache 、JBOSS Cache
Session 的缓存,只能在一个线程中使用 ,在开发中,将Session与线程绑定, 一个线程对应一个Session ,
Session 中数据不能在多次用户请求,不同用户请求 不能共享, 使用SessionFactory 缓存,实现多个用户,多次请求之间 共享数据 。
二级缓存,存储以对象散装数据 存储的,访问二级缓存的数据,查询条件 是 id !
查询缓存 和 二级缓存区别? 二级缓存缓存整个对象属性数据,查询条件是id, 查询缓存可以缓存任何查询结果,查询条件 是 SQL语句
二级缓存使用 :
1、 引入 jar包
2、引入配置文件
3、 在hibernate开启二级缓存
4、 配置二级缓存提供商
5、配置缓存策略
BOS项目 应用场景: 每个用户登录,需要将用户具有角色和权限查询出,保存到Session中 ,用户具有角色信息会重复, 角色对应权限信息 也会重复
以EhCache 为例
使用 maven 导入 ehcache的jar 包
<dependency>
<groupId>net.sf.ehcachegroupId>
<artifactId>ehcacheartifactId>
<version>1.5.0version>
dependency>
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-ehcacheartifactId>
<version>3.6.10.Finalversion>
dependency>
从 jar 包中找
将 jar 中 ehcache-failsafe.xml 复制 项目 resources 目录, 改名 ehcache.xml
配置 applicationContext-common.xml
<prop key="hibernate.cache.use_second_level_cache">trueprop>
<prop key="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
prop>
配置数据 缓存并发策略
可以在 cfg文件统一配置,可以在hbm文件配置
缓存分为类级别和集合级别的缓存
配置 Role.hbm.xml
<set name="functions" table="role_function">
<cache usage="read-write"/>
<key column="role_id">key>
<many-to-many class="cn.itcast.bos.domain.auth.Function" column="function_id">many-to-many>
set>
集合级别的缓存,依赖类级别的缓存
配置Function.hbm.xml
<class name="cn.itcast.bos.domain.auth.Function" table="auth_function">
<cache usage="read-write"/>
两个角色 role1 role2 ,三个用户 aaa bbb ccc
aaa 和 bbb 属于 role1,ccc 属于role2
role1 和 role2 权限由交叉
log4j.rootLogger=OFF, stdout
log4j.logger.org.hibernate.cache=debug
5.5. 二级缓存的性能监控
<prop key="hibernate.generate_statistics">trueprop>
通过SessionFactory API 获得 监控参数
// 统计对象
Statistics statistics = sessionFactory.getStatistics();
System.out.println("命中次数:" + statistics.getSecondLevelCacheHitCount() + ", 丢失次数:" + statistics.getSecondLevelCacheMissCount());
里面有大量相关技术的资料