传智播客学习笔记6.4
错误时如何回显页面?
1/addUI中重新准备数据
2、单独写一个回显的action?(待试验)
使用两层架构:view + service(service+dao)
使用技术:struts1 + jstl1.1 + hibernate + jbpm
要完成的功能:
组织与人员管理
流程管理: 部署,删除,查看流程图片,查看流程定义文件,备份
流程监控: 用图形显示当前正在执行的节点。
流程: 请假的(审批)流程。
常见的是审批流程, 如经常提到的请假与报销等. 还有一些其它流程, 如开会的
流程为: 准备议题 --> 选择与会人员 --> 会议通知 --> 会议纪要 --> 发送会
议纪要给与会人员.
* 一、准备环境:
1,创建目录结构:
源码文件夹:src/java,src/process,src/config,test/java;
文档资料文件夹doc;
2,添加所使用的框架的环境:
a) 添加jar包:
struts(使用MyEclipse添加,这样他可以配置好配置文件);
jstl1.1;
jbpm3.2.2的jar包(包含了Hibernate3.2.3);
相应的jdbc驱动;
b) 添加配置文件:
jbpm.cfg.xml
hibernate.cfg.xml
log4j.properties
c) 解决struts1.2.9中的commons-beanutils.jar与hibernate3.2中的
commons-collections.jar版本不匹配问题。
3,创建所用的数据库,修改hibernate.cfg.xml中的数据库连接信息,并生成相
应的数据库表。
* 二、部门人员职务管理
主要完成部门人员职务等基础模块中的增删改查。
1,设计部门人员职务的实体:Department, User, Role. 并要求练习:1,搭建
环境。2,写映射文件并生成相应的数据库表。
2,分析部门有关的功能要求与显示页面,和表单的验证要求。然后由学员在自己
的工程中实现部门模块的功能。要做的工作有:1,使用view+serivce架构,
即在service 中直接使用Session;2,使用过滤器控制事务;3,完成分析的
部门有关的功能。
3,控制事务时实现,如果当前请求中没有用到session,就不会创建session和开
始事务,是通过ThreadLocal配合实现的。这时要注意在web容器中有一个线程
池的机制,就是说两次请求有可能是使用的同一个线程,这样就会出现第二次
请求使用的是第一次请求中已关闭的session,就会抛异常。可以在判断的时
候把条件改为当((session == null || !session.isOpen()) && create)成立
时才创建新的Session; 还要注意,在关闭session时一定要把当前线程对应
的session置为null。否则,后面使用同一个线程(此次请求中没有调用方法
getSession(true))的请求就有可能使用这个已关闭的session。如果在过滤
器中的chain.doFilter中没有用到session,因为getSession方法中的判断,
在chain.doFilter代码后的getSession(false)就可能会返回上一次请求(同
一个线程)中关闭的session。
4,使用xtree显示部门列表,可以参照以下步骤练习:
a) xTree的使用(通过js);
b) 递归显示部门与其子部门的信息;
c) 在Java中通过递归拼接出要在js中使用的显示tree的代码,要有正确的结构;
5,快速完成Role的管理(增删改查)。快速完成User的管理(增删改查),其中
添加人员时选择部门和多选职务与他们的错误回显。
6,人员添加或修改验证失败后不能再返回到页面(jsp),而应返回到xxUI(addUI
或editUI),这样就能再次准备数据。这样会引出一个新的问题,就是xxUI中
会再次准备formbean 的数据,导致表单页面中不能显示上次的错误输入,而
是显示原始信息。解决办法是在xxUI中准备formbean数据时加一个条件:只有
不是验证失败后转过来的请求 才准备formbean的数据。而这个判断可以使用
一个第一次转到表单页面中没有,验证失败后转过的请求中有的参数的值,如:
if(actionForm.name==null){不是验证失败转过来的,应准备formbean数据}。
* 三、流程定义管理
要完成的功能:
1,流程定义列表:默认显示所有流程定义的最后一个版本;还可以显示指定的流
程定义的所有版本。
2,部署流程定义(PAR):使用文件上传,部署流程定义文档。
3,流程定义文件查看:在线查看流程定定文件(processdefinition.xml)。
4,流程图片查看:在线查看流程图片(processsimage.jpg)。
5,备份流程定义:把流程定义文档打包(zip)下载;要求默认的名字格式为:
"${流程定义名称}_${流程定义的版本}"。
6,删除流程定义。
** 准备知识:
*** 1,将要使用到的API:
a) 查询所有流程定义的最后一个版本:
JbpmContext.getGraphSession().findLatestProcessDefinitions();
b) 查询指定名称的流程定义的所有版本:
JbpmContext.getGraphSession().findAllProcessDefinitionVersions(String);
c) 解析流程定义档:
ProcessDefinition.parseParZipInputStream(zipInputStream);
d) 部署流程定义:
JbpmContext.getGraphSession().deployProcessDefinition(pd);
e) 从流程定义中获取原流程定义文档中的文件:
// 相对于流程定义文档(zip包中)的根目录的相对文件路径,如 classes/cn/itcast/Test.class
String filePath;
// 方法一
// 获取指定文件的文件内容
byte[] fileContent = pd.getFileDefinition().getBytes(fileName);
// 获取所有文件的内容,Map的key是文件路径,value是文件内容
Map<String, byte[]> files = pd.getFileDefinition().getBytesMap();
// 方法二
// 获取指定文件的内容的输入流
InputStream inStream = pd.getFileDefinition().getInputStream(fileName);
// 获取所有文件的内容的输入流,Map的key文件路径,value是文件内容的输入流
pd.getFileDefinition().getInputStreamMap();
f) 删除流程定义:
JbpmContext.getGraphSession().deleteProcessDefinition(id);
*** 2,其他相关知识:
a) 向浏览器输出jpg图片时,设置 ContextType 为 image/jpeg;
输出xml时,设置 contentType 为 text/xml; charset=编码;
下载文件时,设置 ContentType 为 application/zip,并且增加头信息:
Content-Disposition = attachment; filename=文件名(进行URL编码)
b) 用java写zip文件,示例代码如下:
ZipOutputStream out = new ZipOutputStream(outputStream);
out.putNextEntry(new ZipEntry(fileName)); // 添加一项
out.write(file11);
out.closeEntry();
// ... 写其他文件或文件夹项
out.close(); // 写完后一定调用close()方法
1,ZipEntry可以代表一个文件或是一个文件夹,当name以'/'结尾时代表一个
文件夹。 ZipEntry的name是指的一个相对路径(相对于zip包中的根目录)。
例如想在zip文件中的"dir"目录下有一个文件a.txt,可以写成"dir/a.txt";
如果想在dir1下有一个dir2文件夹,应写为"dir1/dir2/"(以'/'结尾),最
终会递归的创建这个目录结构,即不要求dir2以上的目录是存在的。
2,在调用ZipOutputStream的putNextEntry(ZipEntry)方法或close()方法后,
会自动关闭上一个未关闭的ZipEntry(所以可以不必显示调用closeEntry()方
法)。
3,写完zip后一定要调用ZipOutputStream.close()方法,否则会造成写出的
zip文件不完整。
4,在压缩文件时如果zip中有中文名的文件,就会变成乱码。这是由于jdk
(使用有版本为5.0)中把ZipEntry的名字硬编码使用utf-8编码再写到zip中,
而我们windows的本地编码为gbk,用gbk显示时就变成了乱码。 由于是硬编码,
所以不能修改,但我们可以使用ant包中的ZipOutputStream与ZipEntry,使用
代码不用改,就可以解决压缩时的中文乱码了。
汤老师编写代码的经验相当丰富。