我在上一篇Blog中已经贴上了系统使用说明书,并把系统源码和数据库脚本放到了我的邮箱里供各位下载。但我觉得仅仅使用说明书和源码还是不太利于学习,所以我把我写的有关报表部分的技术说明书放到这篇Blog中以方便各位阅读源码。
首先需要说明的是,本来我们打算使用Struts作为Web层,但是由于时间关系没有使用。我和一个师兄就参考MVC模式和Struts的原理自行设计了一个简单的Web层模式(下文中有介绍),希望不会给各位读源码造成困惑。其次,由于技术说明书里很多UML图,而Blog系统上传图片实在太麻烦,所以我只把重要的系统结构图贴上,其他的类图就上传到我的邮箱里,请各位到那里下载吧:)
1. 系统整体设计架构... 3
1.1. 系统设计... 3
1.2. 目录/包结构... 4
1.3. 系统公用部分... 6
1.3.1. ActionServlet/Dao/其他工具类... 7
1.3.2. JSP公用功能页面... 10
1.3.3. 报表显示与打印部分... 11
2. 系统使用说明... 11
1. 系统整体设计架构
1.1. 系统设计
科研管理子系统采用JSP/Servlet+JavaBean+数据库的三层B/S结构实现。整体的业务流程和架构设计如下图所示:
一般情况下,用户在进入系统功能页面,进行一系列业务操作(如录入,增删改,查询提交等等)之后,都会将所获得数据或参数提交到相应的ActionServlet,由ActionServlet来进行识别事件、验证、执行业务逻辑、转到其他页面等后续操作。由此实现用户界面与业务逻辑处理的分离,有助于提高系统的健壮性和可维护性。
图中注释里描述的是一种特殊情况。因为在正常情况下,所有功能页面都需要将其业务逻辑的执行交由相应的ActionServlet来进行控制。但是在某些比较特殊情况下,即在不影响系统安全和健壮性的前提下,适当采用页面之间直接跳转的方式。在本系统中,这种情况多出现在生成网页报表的部分,所以不会影响到系统安全和业务逻辑。
对于数据库操作部分的描述如下图所示:
其中,数据库对象是指数据表。每一个数据库对象都有唯一一个实体Bean与其对应,该Bean的所有属性与数据库中的属性的名称和类型也保持一一对应关系。而所有实体Bean都继承自接口GeneralEntity--该接口没有任何方法,它仅仅是一个标识接口。其目的是满足设计模式中的里氏原则和反转依赖原则,以达到增加系统可维护性和复用性的目的。图右所示的GeneralDao是一个抽象类,顾名思义DAO即表示它封装了对数据库进行访问的方法,所有的实体类都需要实现其规定的方法,具体情况留待下节详细介绍。
(注:里氏原则:所有基类可以出现的地方,子类也应该可以出现。反转依赖原则:应该对抽象/接口编程,而不是对实现编程。满足这两点的设计有助于提高系统的可维护性和模块的复用性。我们在上图中看到,由于所有的DAO类都仅对于GeneralEntity接口进行编程,从而隐藏了具体的Entity类的信息,所以在更改/添加/删除Entity类的时候不会对整体产生很大的影响)
1.2. 目录/包结构
系统模块目录结构如下图所示
其中,由于JSP页面过多所以这里仅显示其中的一部分作为代表。
l CSS:该目录主要用于存放style.css文件,该文件定义了所有页面元素的格式。
l META-INF:该目录中存有context.xml和MANIFEST.MF两个文件。前者定义了系统所使用的连接池有关的信息,后者为网络模块的描述文件。
l Reports:内含有全部报表所需要的模版文件。
l WEB-INF:这是一个基于Java的Web程序基本目录,内含可选的Lib目录用于包含所需要的类库或jar包以及web.xml文件。
l Silver/Classic/Blus:这些目录用于保存页面左侧导航菜单的主题(颜色,字体,布局等)文件。分别代表银色主题,经典主题和蓝色主题。本系统采用蓝色主题。
l Images:该目录保存系统页面所需要的全部图片资源。
l Config:该目录用于保存数据库及系统信息。
所有目录和Jsp页都保存在模块的根目录(即“/”目录下)。
系统的Java包的目录结构如下图所示:
Ø cn.edu.hlju.oa.kygl.action:主要用于包含上文中所提到的ActionServlet,包括GeneralAction在内的所有的Action类都包含在这个包中。
Ø cn.edu.hlju.oa.kygl.db:该包中仅含有一个类,即DBConnection。该类的具体信息将会在下面详细介绍。
Ø cn.edu.hlju.oa.kygl.db.dao:这个包中主要包括GeneralDao在内的所有DAO类,即所有的数据库访问类都在这个包中。
Ø cn.edu.hlju.oa.kygl.db.entities:所有数据库实体Bean类都在这个包中定义。
Ø cn.edu.hlju.oa.kygl.print:该包中主要包含报表所需要的Bean和生成这些Bean的工厂类。有关于这些类的说明将会在下一节的报表的显示与打印部分详细介绍。
Ø cn.edu.hlju.oa.kygl.statistic:主要保存统计查询模块所需要的类的定义。
Ø cn.edu.hlju.oa.kygl.util:系统其他模块所能用到的工具类都位于这个包中。
1.3. 系统公用部分
在本系统中,由于一些功能模块/代码/页面具有公共意义,所以在实现过程中我们将具有可重用部分的代码/页面提取出来供系统中的其他部分使用,下面就来详细介绍一下他们的具体内容。
1.3.1. ActionServlet/Dao/其他工具类
Ø GeneralAction
该类继承自HttpServlet,是所有ActionServlet的基类。该类中的常量规定了功能页面向ActionServlet提交的事件类型,ActionServlet将根据这些类型,参考业务逻辑来判断应该执行那些操作和转向那些页面。其中0x800之前的常量是与数据库操作有关的事件;0x1000-0x100000之间是报表事件,0x100000之后的事件是统计查询事件。actionType属性定义了事件的类型,这个属性的取值范围就是上述的常量。actionPage属性保存系统要转向的页面,它是由ActionServlet根据事件类型之后得到的。
1) doGet()方法将处理过程代理给了doPost()方法。
2) doPost()主要调用业务逻辑处理方法process(),如出现错误,则转到出错页面并给出错误信息。
3) process()方法将依次调用:
n preProcess(request,response);
n validate(request,response);
n executeBusiniess(request,response);
n reDirection(request,response);
来实现业务逻辑。其中preProcess(request,response); 用于进行一些初始化操作;validate(request,response);用于进行包括权限验证和数据有效性验证之内的各种验证操作;executeBusiniess(request,response)用于执行所需的业务,这三个方法都是抽象方法,需要继承GeneralAction的子类来实现。
4) reDirection(request,response);方法用于进行页面的转向操作,它根据actionPage中所指明的页面进行跳转,而actionPage应该已经在上面3个方法中的一个中被指定。
Ø DBConnection
DBConnection用于获得数据库连接。方法getConnection()有两种获得连接的方式,即直接生成连接(使用getDriverConnection()方法)和使用连接池(使用getDSConnection()方法)。releaseConnection()用于释放获得的数据库连接。开关量isDS用于进行控制使用的连接类型。true表示使用连接池,否则不使用连接池。余下几个字段用于合成获得数据库连接所需要的URL,包括使用的数据库驱动名称,utl,数据库名,数据库用户名和密码。
Ø GeneralDao
GeneralDao是一个抽象类,它负责定义数据库操作所需要用到的一些公用的方法。通过“系统设计”一节所描述的方式来操作数据库。其中的addNew(),update(),delete(),fillList()都是抽象方法,分别表示对数据对象进行增、删、改和将查询结果插入到一个List中。
selectAll方法用于返回给定数据表全部内容。orderPropertyName表示结果将按照哪一个属性排序。
selectCount方法用于返回给定数据表中所含有的数据的数量。
其他的DAO类可以扩展或覆盖GeneralDao的方法以适应自身的应用环境。
Ø GeneralEntity
这是一个标识性的接口,不含任何方法或数据。所有的实体Bean都应该是这个类的子类,原因已经在“系统设计”说明,这里不再赘述。
Ø 其他工具类
1) PaginationUtil:这是一个用于进行分页操作所需要的工具类。getRecordCount方法用于获得给定数据表的全部元组数。而ceil方法是一个取顶函数的实现。
2) GlobalSetting:该接口仅用于保存一些系统参量,包含每页所显示的数据数量,报表导出图片的宽和高。
3) GlobalUtil:该类为所有其他模块提供一些实用方法。包括各种形式的格式转换方法(各种TimestampFormater方法),获得重点实验室信息的方法getLabInfo,以及获得人事信息的方法getMinistryInfo。
4) DBMaps用于将数据库表中的名字与相应的中文名称作一一映射。initHash方法用于对这一映射进行初始化操作。getTableMap方法可以获得key为tableName字段的相应中文名称映射表。
1.3.2. JSP公用功能页面
l GeneralHTMLReportViewer.jsp:该页面负责以网页的形式显示生成的报表,如果报表的内容超过一页,则将其自动分页,每次只显示一页的内容,并提供翻页的功能按钮、导出成PDF/Word/Excel格式的功能按钮。该页面要求在进入本页之前要传递进来一个jasperPrint对象,和该报表的名字,再由JRHtmlExporter导出成网页格式。具体情况如下面代码所示:
JasperPrint jasperPrint = (JasperPrint)session.getAttribute("JasperPrint");
session.setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_SESSION_ATTRIBUTE,
jasperPrint);
String pageTitle = (String)session.getAttribute("pageTitle");
JRHtmlExporter exporter = new JRHtmlExporter();……..
PDF/Word/Excel格式的导出功能是由JasperReport类库提供的Servlet实现的,在点击相应的按钮之后GeneralHTMLReportViewer.jsp就会转到相应的Servlet来获得所需要的格式文件。
l GeneralBarChart.jsp和GeneralLineChart.jsp:该页面用于显示利用指定查询结果生成的柱状图和曲线图。图形由JFreeChart实现,同时使用JFreeChart类库提供的ServletUtilities将图表直接变成PNG图片显示在网页上。该页面需要获得一个已经生成好的JFreeChart对象。
1.3.3. 报表的生成
本系统采用开源报表工具JasperReport来完成报表生成,各式转化及打印工作,其工作过程如下图所示:
报表对象的生成有两个方法,一个是将SQL语句直接写到报表的模板中,在运行期由JasperReport负责处理获取数据和显示的工作,这样作虽然工作量小但是不利于今后报表的修改和扩充,所以我们主要采用第二种方法,即使用JavaBean作为报表数据。
具体方法是:将查询结果的每一个元组都用JavaBean来表示,所以全部结果酒是一个JavaBean数组。我们在运行期利用BeanFactory生成查询结果的Bean数组,结合报表模板一起交给JasperReport来生成报表对象,从而获得和第一种方法一样的结果。只是这种方法易于修改和扩充,即在不修改报表本身的情况下就可以更改查询方式或显示方式。cn.edu.hlju.oa.kygl.print包中的各个Bean都是用于定义结果Bean的;而各个Factory类的功能就是BeanFactory。
2. 系统使用说明
本节将着重介绍系统中的每一个Java类和JSP页面的具体功能和实现方法。Java类采用UML图格式进行描述,按照“目录/包结构”一节所列的包的顺序进行,在上文已经介绍过的类这里将不再重复叙述;JSP页面则只介绍其主要功能和实现方法。
2.1. Java部分
2.1.1. cn.edu.hlju.oa.kygl.action
l IlabArticlePaperAction:专著,论文情况的ActionServlet
其预处理部分用于判断事件的类型,缺省的类型为REPORT_QUERY,即报表查询,该事件对应的下一个页面为ILabArticlePaperQuery.jsp。另一个事件为REPORT_PREVIEW,其对应的下一个页面为:GeneralHTMLReportViewer.jsp。validate方法主要用于对权限进行检查。该Action的主要业务逻辑(即excuteBusiniess方法的功能)是:
Ø 如果是REPORT_QUERY则获得著作、论文的信息,并将这些信息交给下一个页面。
Ø 否则,如果类型为REPORT_PREVIEW,则根据用户输入的查询条件,生成论文专著情况的报表对象,并将其传递给下一个页面。
与这个类相似,所有以Ilab开头的ActionServlet都遵循同样的模式,由于它们都继承自GeneralAction,它们之间的区别仅仅在于所面对的事件类型和对每个事件的处理方法不同,其他流程都和上面所叙述的方法类似。所以下面就不再叙述每一个以Ilab开头的ActionServlet的具体功能而只是给出其UML表示。只需记住,preProcess方法是将事件类型进行分类,并给出下一个要跳转到的页面名称;validate方法用于检测查询的有效性和权限;excuteBusiniess则用于针对不同的事件作出不同的处理。以Ilab开头的ActionServlet的业务类型是报表查询和报表预览。
l IlabAwardProjectAction:获奖项目情况的ActionServlet
l IlabMemberInfoAction:实验室人员信息的ActionServlet
l IlabPaperIndexedAction:论文索引情况的ActionServlet
l ILabProjectInfo4ResearcherAction:实验室人员承担项目情况的ActionServlet
l IlabProvincePlusProjectAction:承担省级以上项目情跨功能的ActionServlet
l IlabPublishedArticleAction:出版著作情况的ActionServlet
l IlabResearchResultAction:研究成果情况的ActionServlet
l IlabScienceCommAction:学术交流情况的ActionServlet
l IlabVisitorScholarAction:访问学者的ActionServlet
l StatisticAction:统计查询的ActionServlet
统计查询共有7种事件类型,分别是:STATISTIC_QUERY、STATISTIC_WHOLE、STATISTIC_PROJECT、STATISTIC_AWARD、STATISTIC_RESULT、STATISTIC_ARTICLE和STATISTIC_PAPER。其中STATISTIC_QUERY是请求进入查询页面的事件;STATISTIC_WHOLE是请求查询整体科研信息的事件;后面5种是请求各种详细信息的事件。
validate方法需要检查查询有效性,即检查用户是否点了输入人事姓名和编号之后又没有输入;判断用户是否未选择输入人员姓名。executeBusiniess方法依然是处理各种类型的事件。
2.1.2. cn.edu.hlju.oa.kygl.db.dao
2.1.3. cn.edu.hlju.oa.kygl.db.entities
2.1.4. cn.edu.hlju.oa.kygl.print
这个包内的类主要是报表所用到的Bean和其BeanFactory。
l ILabArticleBean.:论文报表的Bean
l IlabArticlePaperBean:论文专著报表的Bean
l IlabAwardProjectBean:获奖项目信息报表的Bean
l IlabMemberInfoBean:实验室人员信息报表的Bean
l IlabPaperIndexedBean:论文索引信息报表的Bean
l IlabProvincePlusProjectBean:省级以上项目信息报表的Bean
l IlabResearchResultBean:实验室人员成果情况报表的Bean
l IlabReseacherProjectsBean:实验室人员承担项目情况报表的Bean
l IlabScienceCommBean:学术交流情况报表的Bean
l IlabVisitorScholarBean:访问学者情况报表的Bean
l SRDataSourceFactory:生成各种Bean数组的工厂类
其中createIlabXXXCollection是生成某个Bean数组,而createILabXXXDS是将Bean数组包装成JRDataSource共给JasperReport用于生成报表对象。
2.1.5. cn.edu.hlju.oa.kygl.statistic
l WholeBean:是整体统计信息报表所需要用到的Bean
l GeneralStatBean:用于作为其他类型的统计信息报表的通用Bean
l StatisticDSFactory:用于生成统计信息报表数据源的工厂方法,与SRDataSourceFactory类作用类似,只是服务的对象不同。
2.2. JSP功能页面部分
2.2.1. 科研信息管理
l StatisticQuery.jsp:统计查询页面,用户需要指定起止年分,(可选)指定人事信息和查询类别。提交之后以上信息交由StatisticAction处理。
l StatisticWholeViewer.jsp:统计查询的整体结果显示页面。进一步的查询在用户点击标题上的超链接之后交由StatisticAction处理。
l StatisticGeneralViewer.jsp:统计查询的详细结果显示页面。进一步的生成图表的请求在用户点击工具栏上的超链接之后交由StatisticAction处理。
2.2.2. 重点实验室
l ILabOnlyQuery.jsp:由于有很多重点实验室的报表查询页面仅仅需要指定是哪一个重点实验室,所以需要类似查询页面的查询都采用这个页面。用户提交之后,该页面将信息交给相应的ActionServlet处理。
l ILabArticlePaperQuery.jsp:为专著、论文情况的查询页面,需要用户选择是哪一个重点实验室,并给出查询的起止年度。提交之后以上信息交由相应的ActionServlet处理。
l ILabProjectInfo4ResearcherQuery.jsp:实验室人员承担项目情况的查询页面,需要用户选择是哪一个重点实验室,并给出查询的年限和起始年度。提交之后以上信息交由相应的ActionServlet处理。
l ILabResearchResultQuery.jsp:研究成果情况的查询页面,与ILabArticlePaperQuery.jsp功能类似。
l ILabScienceCommQuery.jsp:学术交流情况的查询页面,与ILabArticlePaperQuery.jsp功能类似。