目 录
1引言... 1
1.1课题起源... 1
1.2课题目的... 1
1.3课题意义... 1
2 系统实现架构环境与技术说明... 1
2.1系统架构... 1
2.2系统实现环境和技术... 2
2.2.1开发环境... 2
2.2.2 运行环境... 2
2.3系统框架说明... 2
2.3.1 struts 介绍... 2
2.3.2 理解MVC设计模式... 5
2.3.3 spring 介绍... 5
2.3.4 hibernate组件介绍... 6
2.3.5 jsp组件介绍... 6
2.3.6 SQL Server 2000介绍... 7
2.3.7 Tomcat介绍... 8
3 功能需求分析... 9
3.1功能需求... 9
3.1.1 公共资源管理... 9
3.1.2 教务资源管理... 9
3.1.3 试题资源管理... 9
3.1.4 试卷资源管理... 9
3.2非功能需求... 9
4数据库设计... 10
5 系统模块的设计与实现... 13
5.1系统模块的设计... 13
5.1.1 公共资源管理模块设计... 13
5.1.2 教务资源管理模块设计... 14
5.1.3 试题资源管理模块设计... 15
5.1.4 试卷资源管理模块设计... 15
5.2 系统模块的实现... 15
5.2.1 公共资源管理模块实现... 16
5.2.2 教务资源管理模块实现... 20
5.2.3 试题资源管理模块实现... 21
5.2.4 试卷资源管理模块实现... 24
6 测试... 25
结 论... 26
参考文献... 27
致 谢... 28
声 明... 29
随着社会的发展,计算机的普及,将教育产业信息化成为可能而且这种呼声也越来越高,传统的考试、统计、评估、管理工作也越来越显的繁重和笨拙,传统的这些操作,即费时又费力,而且容易出错,工作效率较低,并且不管统计、评估还是管理工作合理性都不好。因此,拥有一套实用、高效、完善的评估管理软件就显得尤为重要了,它可以提升学校的教育质量和教学管理质量,提高工作效率,降低行政成本。
SMART在线考试系统目的是为中小学生提供在线考试、在线评估的智能化平台,并为学校、主管部门教学评估、教学质量控制提供决策依据。该软件系统内含在线考试评估子系统。本软件产品是一项独立的软件,而且全部内容自含。
设计一套符合中小学生实际情况、完整统一、技术先进、高效稳定、安全可靠的基于C/S架构的智能化的在线考试、在线评估的系统,并为学校、主管部门教学评估、教学质量控制提供决策依据的一个平台。
作为在中小学生,在线考试系统更趋于公证、客观、针对性,更能激发学生的学习兴趣和热情;作为老师,大大减轻了出卷,阅卷,评卷,以及统计,评估、管理等等工作负担,从而大大提高了工作效率。
系统在选用结构的时候,有B/S、C/S、Smart Client(智能客户端)三种可能的方案。它们各有各的优缺点。
B/S架构的优点在于客户端和服务器通过Intranet进行数据交换,客户端基于统一的WEB浏览器,减少了投资,解决了系统维护升级的问题,另外只有极少部分事务逻辑在前端(Browser)实现,但是主要事务逻辑在服务器端(Server)实现,这也就充分保护了数据的安全。虽然如此,B/S结构还是有很多的不足,其中最大的缺点是在界面操作上具有很大局限性,用起来很难,开发起来也很费钱。
采用C/S结构可以减轻服务器运行数据负荷,数据的储存管理也较为透明,最重要的是开发较为迅速。而它的缺点也是不少,它会造成高昂的维护成本,且投资大,维护不方便。在实际开发中,部署不如B/S方便。但由于它能快速开发出较为使用的用户界面,而且可以将部分运算转移到客户端来,所以是一个值得考虑的方案。
Smart Client是下一代客户端软件的代表,它可以充分利用终端设备的优势(Full PC、PDA、Phone)、能够调用Web Service、支持在线和离线两种状态、能够如同Web应用程序一般简单方便的部署。由于这套系统的目标客户是教务处负责排课的老师,因此它不会涉及到对终端设备的支持,但是方便部署这一点确实需要的,因为这正是C/S的缺点。
Smart 在线考试项目(以下简称“Smart”),将在struts+sprintg 管理的hibernate作为平台框架搭建具体的Smart在线考试业务系统。
采用struts MVC框架主要用于将Smart的数据流和业务流分开,采用hibernate,主要用于更高效率对数据库存取效率及提高项目开发效率。采用spring管理的hibernate主要主要是获得Spring对Hibernate的支持,获得更高的开发效率和更高的Hibernate操作性能。并能获得Spring相关的事务支持。之所以不完全用Spring框架是考虑项目组的技术水平可能不足以完全驾驭这样的框架规范。所以本系统不属于标准的Struts + Spring + Hibernate的标准J2EE应用。具体框架版本:Struts 1.2 + hibernate 2.1。
对于Hibernate的数据库IO操作,将采用DAO模式将操作封装至DAO对象中。而将建立专门的业务层,对WEB层及其他子模块或类进行接口。
The Jakarta Struts项目是由Apache Software Foundation发起的开源项目,它在java服务器端实现了MVC(Model-View-Controller)设计模式。The Jakarta Struts项目最初是由Craig McClanahan在2000年五月创建的,但后来被开源组织所接收。The Jakarta Struts作为一个开源框架的主要意图是为了在创建WEB应用时能比较容易的分离表示层和业务数据层。自从它诞生以来收到大量开发人员的支持,并很快的成为开源社区中一个重要的成员。
数据库 |
Hibernate平台 |
DAO类(被Spring管理) |
Spring平台 |
业务层(Service类) |
WEB(Action、page)
|
图2-1 Web层及其他子模块层
Struts最早作为Apache Jakarta项目的组成部分问世。这个名字来源于在建筑和旧式飞机中使用的支持金属架。它产生的目的是为了帮助开发者减少运用MVC设计模式来开发Web应用的时间。Struts框架是基于JAVA技术的,开发人员通过Struts框架可以充分利用代码重用和“一次编写、到处运行”的优点。它提供了一种创建Web应用程序的框架,其中对应用程序的显示、表示和数据的后台代码进行了抽象。
Struts是一个比较好的MVC框架,继承了MVC的各项特性,并根据J2EE的特点,做了相应的变化和扩展。它采用的主要技术是Servlet,JSP和tag library。Struts架构是围绕一个Action Mapping把HTTP消息形式的用户请求转换成应用程序的动作。Action Mapping创建了一个Action对象来处理请求。一旦Action对象完成了一个任务,它就通过在一个JSP页面上写结果来直接回应一个用户请求,或者它可以让一个引用程序流动到其他地方做回应。
Struts作为一个MVC的框架,对Model、View和Controller都提供了响应的实现组件,对此分别做介绍,并且看看他们是如何结合在一起的。
Controller:控制器的作用是从客户端接受请求,并且选择执行相应的业务逻辑,然后把响应结果送回到客户端,在Struts中由ActionServlet和ActionMapping对象组成。
Model:struts为Model部分提供了Action 和ActionForm对象。Struts建议在实现是把“做什么”(Action)和“如何做”(业务逻辑)分离。
View:Struts应用中的View部分是通过JSP技术实现的。Struts提供了自定义的标记库可以使用,通过这些自定义标记可以非常好地和系统的Model部分交互,通过使用这些自定义表创建的JSP表单,可以实现和Model部分中的ActionForm的映射,完成对用户数据的封装,同时这些自定义标记还提供了像模板定制等多种显示功能。
在这章节,我们简要的画出一幅和MVC模式对应的STRUTS框架图2-2:
视图view1 |
模型module |
View2 |
Action Servlet |
Action1 |
Action4 |
Action3 |
Action2 |
图2-2 struts框架图
图2-2是Struts框架下应用程序请求流通过的路径。这个处理过程由5个基本的步骤组成。
下面是处理步骤的描述。
1.由显示视图产生一个请求。
2.请求被ActionServlet(控制器)接收,它在struts-config.xml文件中寻找请求的URI,找到对应的Action类后,Action类执行相应的业务逻辑。
3.Action类执行建立在模型组件基础上的业务逻辑,模型组件是和应用程序关联的。
4. 一旦Action类处理完业务逻辑,它把控制权返回给ActionServlet。,Action类提供一个键值作为返回的一部分,它指明了处理的结果。ActionServlet使用这个键值来决定在什么视图中显示Action的类处理结果。
5. 当ActionServlet把Action类的处理结果传送到指定的视图中,请求的过程也就完成了。
为了更深刻的理解Struts框架,你必须先理解MVC设计模式,Struts技术就是基于MVC设计模式的。MVC设计模式起源于Smalltalk语言,它由以下三个部分组成:模型(model),视图(view),控制器(Controller)。表2.1定义了这些组件。
表2-1 Mvc组件表
组件 |
描述 |
模型(model) |
封装数据对象。模型用来封装和显示数据对象。 |
视图(view) |
作为模型的显示,它表示数据对象的当前状态 |
控制器(Controller) |
定义对用户的输入执行相应操作的接口,它用来操作模型(model)和数据对象 |
使用MVC的好处包括如下:
可靠性:表示层和业务层别分离,这样就允许你更改你的表示层代码而不用重新编译你的模型(model)和控制器(Controller)代码。
高重用和可适应性: MVC模式允许你使用各种不同样式的视图来访问同一个服务器端的代码。它包括任何WEB(HTTP)浏览器或则无线浏览器(WAP)。
较低的生命周期成本:MVC使降低开发和维护用户接口的技术含量成为可能。
快速的部署:开发时间会得到相当大的缩减,它使程序员(java开发人员)集中精力于业务逻辑,界面程序员(HTML和JSP开发人员)集中精力于表现形式上。
可维护性: 分离表示层和业务逻辑层也使得基于Struts的Web应用更易于维护和修改。
根据Spring的官方网站描述,“Spring是一种多层的J2EE应用程序框架,它是以Rod Johnson 编著的Expert One-on-One J2EE Design and Development—书的代码为基础发展而来的。”Spring的核心,提供一种新的机制来管理业务对象及其依赖关系。例如,利用loC(反转控制),你可以指定一个DAO(Data Access Object,数据访问对象)类依赖一个DataSource类。它也允许开发人员通过接口编程,使用xml文件来简单的定义其实现。Spring有很多类用来支持其它的框架(如Hibernate和Struts),这使得集成变得易如反掌。
Spring既是全面的又是模块化的。Spring有分层的体系结构,这意味着你能选择仅仅使用它任何一个独立的部分,而它的架构又是内部一致。因此你能从你的学习中,得到最大的价值。例如,你可能选择仅仅使用Spring来简单化JDBC的使用,或用来管理所有的业务对象。
它的设计从一开始就是要帮助你编写易于测试的代码。Spring是使用测试驱动开发的工程的理想框架。
Hibernate是一个免费的开源Java包,它使得与关系数据库打交道变得十分轻松,就像您的数据库中包含每天使用的普通Java对象一样,同时不必考虑如何把它们从神秘的数据库表中取出(或放回到数据库表中)。它解放了您,使您可以专注于应用程序的对象和功能,而不必担心如何保存它们或稍后如何找到它们。
大多数应用程序都需要处理数据。Java应用程序运行时,往往把数据封装为相互连接的对象网络,但是当程序结束时,这些对象就会消失在一团逻辑中,所以需要有一些保存它们的方法。有时候,甚至在编写应用程序之前,数据就已经存在了,所以需要有读入它们和将其表示为对象的方法。手动编写代码来执行这些任务不仅单调乏味、易于出错,而且会占用整个应用程序的很大一部分开发工作量。
这类工具有很多,从昂贵的商业产品到内置于J2EE中的EJB标准。然而,在很多情况下,这些工具具有自身的复杂性,使得开发人员必须学习使用它们的详细规则,并修改组成应用程序的类以满足映射系统的需要。由于这些工具为应付更加严格和复杂的企业需求而不断发展,于是在比较简单和常见的场景中,使用它们所面临的复杂性反而盖过了所能获得的好处。这引起了一场革命,促进了轻量级解决方案的出现,而Hibernate就是这样的一个例子。
在传统的HTML文件中假如JAVA程序片段和JSP标签,就构成了JSP网页。JAVA程序片段可以操作数据库、重定向网页以及发送E-MAIL等,实现建立动态望站所需要的功能。所有程序操作都在服务器端执行,网络上传送给用户端的仅仅为输出结果。JSP技术大大降低了对用户浏览器的要求,即使用户浏览器端不支持Java,也可以访问JSP网页。
当JSP容器接受到WEB用户的一个JSP文件请求时,它对JSP文件进行语法分析并生成JAVA Servlet源文件,然后对其进行编译。一般情况下,Servlet源文件的生成和编译仅在初次调用JSP时发生。如果原始的JSP文件被更新了,JSP容器将检测所做的更新,在执行他之前重新生成Servlet并惊醒编译。下图显示了JSP容器初次执行JSP的过程:尽管JSP在本质上是Servlet,但这两者的创建方式不一样。Servelet完全由java程序代码构成,擅长于流程控制和事务处理,而通过Servlet来生成动态网页很不直观;JSP由HTML代码和JSP标签构成可以方便的编写动态网页。因此在实际应用中,采用Servlet来控制业务流程,而采用JSP来生成动态网页。在Struts框架中,JSP位于MVC设计模式的视图层,而Servlet位于控制层。
浏览器 |
JSP容器 |
JSP文件 |
生成Servlet源代码 |
经编译的Servlet类 |
请求 |
响应 |
图2-3 JSP容器初次执行JSP的过程
Server 是一个关系数据库管理系统,它最初是由Microsoft、Sybase 和Ashton-Tate三家公司共同开发的。SQL Server 2000 是Microsoft 公司推出的SQL Server 数据库管理系统,该版本继承了SQL Server 7.0 版本的优点,同时又比它增加了许多更先进的功能、具有使用方便、可伸缩性好与相关软件集成程度高等优点。SQL Server 不但可以应用于大中型数据库管理中,建立分布式关系数据库,并且也可以开发桌面数据库。事实上,SQLServer 数据库处理的基本结构,采取关系型数据库模式,尽管如此,相信大家都可以轻易的发现,在SQL Server 的数据库处理方式,则是使用面向对象的操作方式与精神,也就是说,SQL Server 的所有功能,都可以基于系统已经建立好的一些对象来达成,是相当OO(面向对象)的一个系统结构。SQL Server 企业管理器是 SQL Server 的主要管理工具,它提供了一个遵从MMC 标准的用户界面,使用户得以:
· 定义 SQL Server 实例组。
· 将个别服务器注册到组中。
· 为每个已注册的服务器配置所有 SQL Server 选项。
· 在每个已注册的服务器中创建并管理所有 SQL Server 数据库、对象、
登录、用户和权限。
· 在每个已注册的服务器上定义并执行所有 SQL Server 管理任务。
· 通过唤醒调用 SQL 查询分析器,交互地设计并测试 SQL 语句、批处
理和脚本。
· 唤醒调用为 SQL Server 定义的各种向导。
Tomcat是是基于JAVA的开源WEB应用服务器, Tomcat是一个免费的开源的Serlvet容器,它是Apache基金会的Jakarta项目中的一个核心项目,由Apache,Sun和其它一些公司及个人共同开发而成。由于有了Sun的参与和支持,最新的Servlet和Jsp规范总能在Tomcat中得到体现。Tomcat被JavaWorld杂志的编辑选为2001年度最具创新的java产品,可见其在业界的地位。
由于Java的跨平台特性,基于Java的Tomcat也具有跨平台性。 与传统的桌面应用程序不同,Tomcat中的应用程序是一个WAR(Web Archive)文件。WAR是Sun提出的一种Web应用程序格式,与JAR类似,也是许多文件的一个压缩包。这个包中的文件按一定目录结构来组织:通常其根目录下包含有Html和Jsp文件或者包含这两种文件的目录,另外还会有一个WEB-INF目录,这个目录很重要。通常在WEB-INF目录下有一个web.xml文件和一个classes目录,web.xml是这个应用的配置文件,而classes目录下则包含编译好的Servlet类和Jsp或Servlet所依赖的其它类(如JavaBean)。通常这些所依赖的类也可以打包成JAR放到WEB-INF下的lib目录下,当然也可以放到系统的CLASSPATH中,但那样移植和管理起来不方便。 在Tomcat中,应用程序的部署很简单,你只需将你的WAR放到Tomcat的webapp目录下,Tomcat会自动检测到这个文件,并将其解压。你在浏览器中访问这个应用的Jsp时,通常第一次会很慢,因为Tomcat要将Jsp转化为Servlet文件,然后编译。编译以后,访问将会很快。另外Tomcat也提供了一个应用:manager,访问这个应用需要用户名和密码,用户名和密码存储在一个xml文件中。通过这个应用,辅助于Ftp,你可以在远程通过Web部署和撤销应用。当然本地也可以。
公共资源管理模块涉及到省、市、县/区、乡/镇、村、学校、年级、班级等基本信息的设置(其中乡/镇、村是动态的,用户可以根据具体情况设置或者不设置)。用户可通过它实现添加、删除、修改省、市、县/区、乡/镇、村、学校、年级、班级的基本信息。
3.1.2 教务资源管理
教务资源管理具体包括课程设置,教科书设置,章节设置,知识点设置。用户可以对课程,教科书,章节,知识点等进行添加,删除,修改等操作。还可以对知识点进行搜索,针对哪一个年级,哪一个课程,哪一个章节进行搜索。
3.1.3 试题资源管理
试题资源管理主要是对题目类型,答案类型,难度系数进行设置,用户可以对题目类型,答案类型,难度系数进行添加,删除,修改等操作。由于三个类型的数据相似,所以这三个类型的数据放在“code代码”一张表中。
3.1.4 试卷资源管理
试卷资源管理负责管理考试类型和试卷类型的基本信息。用户可通过它实现考试类型以及试卷类型的添加,删除,修改等操作。
⑴ 包容性和可扩展性
系统应具有较好的可扩展性和包容性。系统的可扩展性应包括能接纳已有的系统和在今后系统软硬件扩展时,能有效地保护已有的投资。特别是在应用需求变化时(应用与系统的需求往往提不全或者经常会变化),有一个较好的应用平台,能容易地加以调整。系统易于扩充升级,既能满足当前业务的需求,又为今后的扩充留有空间。
⑵ 系统简洁,易使用、易维护、适用非计算机专业人员使用
在线考试系统的设计符合一般考试系统的需求,功能完备实用,简单易学,界面友好清晰,易于扩展。考虑到考试的复杂性,系统的设计需要考虑用户的计算机水平,做到简单易用,没有计算机专业背景的用户也可以轻松的使用本系统完成复杂的排课工作。
⑶ 设计的合理性
省、市、县/区、乡/镇、村、学校、年级、班级这个级别的等级是逐渐递减的,但是乡/镇和村的设置是根据用户的具体情况可以设置,也可以不设置,不能出现等级的错位(比如:学校的下个级别是市区设置等),否则会影响整个系统的功能实现,还有课程要跟老师以及年级对应起来。这样哪一个年级的哪一门课程就跟老师对应起来。知识点要跟课程和章节对应起来。
⑷ 充分保护数据的一致性
在线考试系统所使用的数据来源于教务管理系统中的省级信息、市级信息、县/区信息、乡/镇信息、村级信息、学校信息、年级信息、班级信息、课程信息、教科书信息、章节信息、知识点信息、题目类型、难度类型、答案类型、考试类型信息、试卷类型信息等多种信息。各信息存在着一定的关联关系。在线考试系统所使用的信息需要数据的高度一致,因此,非常有必要在设计时将数据的一致性作为一个重点来考虑。
⑸ 采用先进成熟的技术,建立实用可靠的系统
本系统的建设目标是否能实现,系统整个投资能否发挥应有的效益,最终将取决于该系统是否可靠实用。为此,系统应采用成熟可靠的技术,应贯彻产品化的设计原则和实施方法。同时,系统的起点要高,而不是基于淘汰的技术,系统的建立应适应未来技术发展的趋势。
在前一版本的开发工程中,数据库的个别地方在设计上存在着一些缺陷,使得软件在开发过程中遇到了一些本可以避免的问题,因此新版本的数据库在设计上进行了适当的优化。
Smart系统—公共资源管理所使用到的所有表,一共有14张。分别是:
[1] CODE表 -存储题目类型,答案类型,试卷类型等等的信息。
表4-1 “CODE”表
“CODE”表 code |
|||||
字段名称 |
数据类型 |
是否是主键 |
是否是外键 |
是否可NULL |
备注 |
Code_id |
UNIQUEIDENTIFIER |
YES |
NO |
NO |
主键 |
code-type |
VARCHAR(50) |
NO |
NO |
NO |
类型 |
code_name |
VARCHAR(20) |
NO |
NO |
NO |
存放具体的名字 |
code_value |
VARCHAR(50) |
NO |
NO |
NO |
存放值 |
code_module |
VARCHAR(50) |
NO |
NO |
YES |
组件 |
remark |
VARCHAR(100) |
NO |
NO |
YES |
描述 |
[2] smart_book_publishing_company表 - 存储书本信息
表4-2 “书本”表
“书本”表 smart_book_publishing_company |
|||||
字段名称 |
数据类型 |
是否是主键 |
是否是外键 |
是否可NULL |
备注 |
Book_no |
UNIQUEIDENTIFIER |
YES |
NO |
NO |
主键 |
Book_name |
VARCHAR(50) |
NO |
NO |
NO |
书的名字 |
ref_course_no |
VARCHAR(20) |
NO |
NO |
NO |
课程的Id |
company_name |
VARCHAR(50) |
NO |
NO |
NO |
公司的名字 |
remark |
VARCHAR(50) |
NO |
NO |
YES |
描述 |
record_number |
VARCHAR(100) |
NO |
NO |
YES |
版本号码 |
[3] smart_province表 - 存储省相关信息
表4-3“省信息”表
“省信息”表 smart_province |
|||||
字段名称 |
数据类型 |
是否是主键 |
是否是外键 |
是否可NULL |
备注 |
province_id |
UNIQUEIDENTIFIER |
YES |
NO |
NO |
主键 |
province_name |
VARCHAR(50) |
NO |
NO |
NO |
书的名字 |
province_no |
VARCHAR(20) |
NO |
NO |
NO |
课程的Id |
lastclass_no |
VARCHAR(50) |
NO |
NO |
NO |
公司的名字 |
remark |
VARCHAR(50) |
NO |
NO |
YES |
描述 |
record_number |
VARCHAR(100) |
NO |
NO |
YES |
版本号码 |
length |
Bigint |
NO |
NO |
YES |
省的编号的长度 |
ruleclass_name |
VARCHAR(50) |
NO |
NO |
YES |
这个级别的设置名称 |
lastname |
VARCHAR(50) |
NO |
NO |
YES |
上个级别的名字 |
nextName |
VARCHAR(50) |
NO |
NO |
YES |
下个级别的名字(如:市级设置) |
其他表包括市,县/区,乡/镇,村,学校,年级,班级表跟此表雷同,这里就不再叙述。
[4] smart_course表 - 存储课程相关信息
表4-4 “课程”表
“课程”表 smart_course |
|||||
字段名称 |
数据类型 |
是否是主键 |
是否是外键 |
是否可NULL |
备注 |
course_no |
UNIQUEIDENTIFIER |
YES |
NO |
NO |
全局唯一码(主键) |
course_name |
VARCHAR(50) |
NO |
NO |
NO |
课程的名字 |
Course_teacher |
VARCHAR(20) |
NO |
NO |
NO |
课程的老师 |
Section_count |
INT |
NO |
NO |
NO |
章节号 |
remark |
VARCHAR(50) |
NO |
NO |
YES |
描述 |
grade_id |
UNIQUEIDENTIFIER |
NO |
YES |
YES |
年级id |
[5] smart_examtype表 - 存储考试类型信息
表4-5 “考试类型”表
“考试类型”表 smart_examtype |
|||||
字段名称 |
数据类型 |
是否是主键 |
是否是外键 |
是否可NULL |
备注 |
examtype_no |
UNIQUEIDENTIFIER |
YES |
NO |
NO |
全局唯一码(主键) |
examtype_name |
VARCHAR(50) |
NO |
NO |
NO |
考试类型的名字 |
Examtype_year |
VARCHAR(20) |
NO |
NO |
NO |
学年 |
Examtype_term |
INT |
NO |
NO |
NO |
学期 |
remark |
VARCHAR(50) |
NO |
NO |
YES |
描述 |
Record_number |
INT |
NO |
NO |
YES |
版本号 |
Examtype_code |
VARCHAR(50) |
NO |
NO |
YES |
标志(设置为无) |
[6] smart_section表 - 存储章节相关的信息表
表4-6“章节”表
“章节”表 smart_section |
|||||
字段名称 |
数据类型 |
是否是主键 |
是否是外键 |
是否可NULL |
备注 |
section_no |
UNIQUEIDENTIFIER |
YES |
NO |
NO |
全局唯一码(主键) |
section_name |
VARCHAR(50) |
NO |
NO |
NO |
章节的名字 |
section_rank |
VARCHAR(20) |
NO |
NO |
NO |
章节的编号(如:第一章节) |
real_grade_id |
INT |
NO |
NO |
NO |
年级的Id |
remark |
VARCHAR(50) |
NO |
NO |
YES |
描述 |
course_no |
UNIQUEIDENTIFIER |
NO |
YES |
NO |
课程的ID |
book_no |
UNIQUEIDENTIFIER |
NO |
YES |
NO |
书本的ID |
Record_number |
VARCHAR(50) |
NO |
NO |
YES |
版本号 |
[7] smart_knowledge_point表 - 存储知识点相关信息
表4-7“知识点”表
“知识点”表 smart_knowledge_point |
|||||
字段名称 |
数据类型 |
是否是主键 |
是否是外键 |
是否可NULL |
备注 |
knowledge _no |
UNIQUEIDENTIFIER |
YES |
NO |
NO |
全局唯一码(主键) |
knowledge _name |
VARCHAR(50) |
NO |
NO |
NO |
知识点的名字 |
section_no |
VARCHAR(20) |
NO |
NO |
NO |
章节的Id |
real_grade_id |
INT |
NO |
NO |
NO |
年级的Id |
remark |
VARCHAR(50) |
NO |
NO |
YES |
描述 |
Record_number |
VARCHAR(50) |
NO |
NO |
YES |
版本号 |
在系统的公共资源管理模块中,我们设定了八级的关系(包括:省级设置、市级设置、县/区设置、乡/镇设置、村级设置、学校设置、年级设置、班级设置)其中他们的关系是一级一级向下的。也就是只有设置了上一级别,下一个级别才能设置(其中乡/镇设置、村级设置可以根据具体情况可以设置或者不设置)。
在这个设计中,开始的时候系统会自动跳出省级的设置选项,用户只有添加了省信息后(如:四川省)之后才能进一步的设置市级的信息(如:成都市),当用户设置了省市后(如:四川省成都市),就进入县/区设置,这个时候用户要添加具体的县/区时候,它对应的下一个级别会有俩个选择(如:学校设置和乡/镇设置),这时候用户可以根据具体的情况做出选择是设置学校还是设置乡/镇(如:四川省成都市双流县一中或者四川省成都市双流县金花镇)。同样的道
理,如果用户设置了乡/镇,它的下一级别同样有俩个选择(如:学校设置和村级设置),用户同样根据具体的情况下级别设置学校还是村级设置(如:四川省成都市双流县金花镇中心小学或则四川省成都市双流县金花镇金花村),最后用户选择的是学校对应的年级和班级。
各个级别的关系是通过编号(NO)来联系的,就是下一级别的(NO)是上个级别的编号(NO)加上用户自己输入的编号。比如:成都市编号:11112222(数据库中)。那么如果用户定义双流县的编号是:3333。那么我们保存双流县信息的时候是把成都市的编号:11112222,加上用户输入编号:3333。最终保存到数据库中的双流县编号就是:111122223333。而且在用户输入每个编号都是唯一的。如果用户输入重复的编号,那么就不能让用户输入这个重复的编号。还有当用户更新一条数据的编号的时候,也不能更新一条重复的纪录。而且更新的时候要把以前那个编号下面的所有级别都删除。比如:双流县编号:111122223333。如果成都市编号更新为:11112223。那么这个双流县就要自动删除。所以当执行这个操作一定要提醒用户,以免误操作。我们画出各个级别设置详细流程图,图4-1是各个级别的设置选择。
开始 |
省级设置 |
市级设置 |
县/区设置 |
乡/镇设置 |
学校设置 |
学校设置 |
年级设置 |
班级设置 |
学校设置 |
村级设置 |
图5-1 级别设置流程图解
5.1.2 教务资源管理模块设计
教务资源模块 |
课程设置 |
章节设置 |
知识点设置 |
教科书设置国 |
添加 |
删除 |
修改 |
图5-2教务资源管理模块
图4-2所示的是教务资源管理具体的操作过程,用户进入分别可以对课程,教科书,章节,知识点进行添加,删除,修改操作。
5.1.3 试题资源管理模块设计
试题资源管理模块 |
题目类型 |
答案类型 |
难度系数 |
添加 |
删除 |
修改 |
图5-3试题资源管理模块
图4-3所示的是试题资源管理具体的操作过程,用户进入可以分别对题目类型,难度系数,答案类型进行添加,删除,修改操作
5.1.4 试卷资源管理模块设计
图4-4所示的是试卷资源管理具体的操作过程,用户进入可以分别对考试类型,试卷类型进行添加,删除,修改操作。
试卷资源管理模块 |
考试类型 |
试卷类型 |
添加 |
删除 |
修改 |
图5-4试卷资源管理模块
各子模块的重要功能如下(只提供部分界面截图):
表5-1 系统模块
序号 |
模块名 |
功能描述 |
1 |
公共资源管理模块(图4-5) |
负责管理省级设置、市级设置、县/区设置、乡/镇设置、村级设置、学校设置、年级设置、班级设置添加、删除、修改基本信息。为系统提供基本信息。 |
2 |
教务资源管理模块(图4-6) |
负责管理教务的基本信息。用户可通过本模块实现课程、章节、知识点、教科书信息的添加、删除、修改。 |
3 |
试题资源管理模块 |
负责管理试题资源的基本信息。用户可通过本模块实现试题类型,答案类型等信息的添加、删除、修改操作。 |
4 |
试卷资源管理模块 |
负责管理试卷的基本信息。用户可通过本模块实现考试类型和试卷类型的添加、删除、修改操作。 |
图5-5 公共资源管理模块界面
(1)这是返回用户定义的编号的方法:
public String getId(String nowId,Integer maxLength){//根据数据库的编号(NO)返回用户输入的定义的NO
String id="";
Integer length=(Integer)(nowId.length())-maxLength; //从什么位置开始
Integer size=nowId.length();//什么地方结束
id=nowId.substring(length,size);//这个id就是上个级别的编号(NO)
return id;//返回上个级别的编号(NO)
}
(2)这是执行下个级别的具体操作:
/*************当执行下一步操作
,以及初始化时候执行这里*****************/
String forward = "";
String step = toChi ((String) request.getParameter ("step"));
List result = typeBaseInfo(step);//得到下级别信息的list
String str [] = step.split ("'");
String rulename = "";
String fathername = "";// 上一级别的名字
String grandfather = "";// 上一级别的名字
String maxLength = "";
String id2 = "";
if (str.length == 5) {
Rulename = str[0];
id2 = str[1];
fathername = str[2];
maxLength = str[3];
grandfather = str[4];
} else {
return null;}
request.getSession ().setAttribute ("rulename", rulename);
request.getSession ().setAttribute ("fathername", fathername);
request.getSession ().setAttribute ("grandfather", grandfather);
request.getSession ().setAttribute ("maxLength", maxLength);
request.getSession ().setAttribute ("id2", id2);
Action中前台得到step参数,其中,step有编号(NO),编号(NO)长度,上级别的名字(如:省级设置),这个级别的设置(如:市级设置),通过方法typeBaseInfo把下个级别的list返回到页面。具体的代码逻辑如下:
/** 根据step返回 step中rulename 级别对应的list....step中包括下一级别的名字,和这一级别的NO上级别的名字 **/
Public List typeBaseInfo (String step) {
List list = (List) new java.util.ArrayList ();
Map params = new HashMap ();
String str [] = step.split ("'");
String rulename = "";//下个级别的名字
String fathername = "";// 这个级别的名字
String grandfather = "";// 上一级别的名字
String maxLength = "";//编号(NO)的长度
String id = "";
if (str.length == 5) {
Rulename = str[0];
id = str[1];
fathername = str[2];
maxLength = str[3];
grandfather = str[4];
} else {
return null;
}
Map map = new HashMap();
map.put("id", id);
……
if (rulename.equals("市级设置"))
{
List rule = (List) this.call(new Carrier(map,
BusinessConstants.UNITS_PUBLIC_BUSINESS, "ShowCity"));
Iterator itClass1 = rule.iterator();
SmartCity city = null;
while (itClass1.hasNext())
{
UnitsPublicForm unitsPublicForm = new UnitsPublicForm();
city = (SmartCity) itClass1.next();
unitsPublicForm.setMaxlength(city.getLength());
unitsPublicForm.setRuleclass("第2级别");
unitsPublicForm.setRulename(rulename);
unitsPublicForm.setRemark(city.getRemark());
unitsPublicForm.setName (city.getCityName ());
unitsPublicForm.setID (city.getCityNo ());
unitsPublicForm.setNextName (city.getNextName ());
unitsPublicForm.setLastID (city.getLastclassNo ());
unitsPublicForm.setLastName (city.getLastName ());
list.add (unitsPublicForm);
}
return list;
}
这段代码是把城市的信息查处来后,放在UnitsPublicForm,并通过list放回。
(3)这是执行上一级别的具体操作:
通过前台得到上个级别的编号(id2),并通过编号长度(length),用ID = id2.substring(0, length2 - length1)方法得到上个级别再上个级别的编号(ID),最后通过typeBaseInfo方法得到上个级别的信息。
具体的逻辑代码参照以下:
/***************执行上一步操作的时候********************************/
String rulename = (String) request.getSession ().getAttribute ("rulename");
String fathername = (String) request.getSession().getAttribute("fathername");
String maxLength = (String) request.getSession().getAttribute("maxLength");
String id2 = (String) request.getSession ().getAttribute ("id2");
Integer maxLength1 = Integer.parseInt (maxLength);
String ID = ""; function deleteBaseInfo(str)
{
var a = document.getElementsByName(str);
var n = a.length;
var temp="";
var flag=true;
for (var i=0; i if(a[i].checked){ flag=false; if (temp==""){ temp=a[i].value+"'"; }else{ temp = temp + a[i].value+"'"; }} } if(flag) {alert("对不起,你还没有选择!")} else{ if (confirm("确实要删除?")){ window.location.href="<%=request.getContextPath()%>/unitsTopicDifficultyAction.do?action=delete&step=two&id="+temp; }} return !flag; } /*********当id2就是上级的NO,它为0时候是表示执行省级设置操作***********/ if (id2.equals("0") || fathername.equals("0") || id2.equals("")) { ID = "0"; fathername = "省级设置"; request.getSession ().setAttribute ("id2", "0"); request.getSession().setAttribute("rulename", "省级设置"); }else { Integer length1 = Integer.parseInt(maxLength); Integer length2 = id2.length(); ID = id2.substring(0, length2 - length1);//得到上级别再上个级别的编号(ID) request.getSession().setAttribute("id2", ID); request.getSession().setAttribute("rulename", fathername);} List list = (List) typeBaseInfo(ID, fathername);// 查出信息表 request.getSession().setAttribute("resultList", list);// 返回到页面的list 这里的typeBaseInfo方法实现的功能是通过上个级别的编号(ID),和这个级别的设置名字(如:市级设置)返回这个级别的所有的信息。并通过list返回。具体的代码如下: /*****根据rulename(比如“省级设置“) ,以及上个级别的id 返回这个级别的List*****/ public List typeBaseInfo(String id, String rulename) { Map map = new HashMap(); List rule = (List) new java.util.ArrayList (); List list = (List) new java.util.ArrayList (); map.put ("id", id); if (rulename.equals("市级设置")) { rule = (List) this.call(new Carrier(map, BusinessConstants.UNITS_PUBLIC_BUSINESS, "ShowCity")); Iterator itClass1 = rule.iterator(); list = (List) new java.util.ArrayList (); SmartCity city = null; while (itClass1.hasNext ()) { UnitsPublicForm unitsPublicForm = new UnitsPublicForm(); city = (SmartCity) itClass1.next (); unitsPublicForm.setMaxlength (city.getLength ()); unitsPublicForm.setRuleclass("第2级别"); unitsPublicForm.setRulename (rulename); unitsPublicForm.setRemark (city.getRemark ()); unitsPublicForm.setName (city.getCityName ()); unitsPublicForm.setID (city.getCityNo ()); unitsPublicForm.setNextName (city.getNextName ()); unitsPublicForm.setLastID (city.getLastclassNo ()); unitsPublicForm.setLastName (city.getLastName ()); list.add (unitsPublicForm); } }…… 图5-6教务资源管理模块界面 下面是用户添加一条具体信息的代码: (1)Action类: public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { UnitsKnowledgeForm knowledgeForm = (UnitsKnowledgeForm) form; SmartKnowledgePoint entity = new SmartKnowledgePoint(); entity.setKnowledgeNo("00"+System.currentTimeMillis()); entity.setKnowledgeName(knowledgeForm.getPoint()); SmartSection se = new SmartSection(); se.setSectionNo(knowledgeForm.getSectionNo()); entity.setSectionNo(se); Map map = new HashMap(); map.put("entity", entity); this.call(newCarrier(map,BusinessConstants.UNITS_KNOWLEDGE_BUSINESS, "SaveUnitsKnowledge"));//执行business里面的方法。 return mapping.findForward("save");} (2)Business类: public void processSaveUnitsKnowledge(Carrier vo) { Map params = (Map) vo.getData(); SmartKnowledgePoint entity = (SmartKnowledgePoint) params.get("entity"); if (params != null) { ((UnitsKnowledgeDAO) getDao()).save(entity); } } 这一段代码是把Action中的SmartKnowledgePoint实体取出来后进一步到UnitsKnowledgeDAO类中的save的方法中执行。 (3)DAO类: public void saveTopicEntity(Code code); 这一行的代码是通过配置文件可以执行到Impl的类的saveTopicEntity方法,并把参数code传过去。 (4)Impl类: public void saveTopicEntity(Code code) { save(code); } 这一行的代码是得到DAO中传过来的实体code,然后调用继承的类中的方法save保存实体code。 图5-7 试题资源管理模块界面 下面是用户删除一条具体信息的代码: (1)Action类: public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { String step = (String) request.getParameter("step"); String forward=""; if("one".equals(step)){ Map params = new HashMap(); String id = request.getParameter("id"); request.getSession().setAttribute("ID",id); params.put("ID", id); UnitsTopicSetBusiness topicSetBusiness = new UnitsTopicSetBusiness(); this .call(new Carrier(params, BusinessConstants.UNITS_TOPIC_BUSINESS, "DeleteTopicById"));} else if("two".equals(step)){ Map params = new HashMap(); String ID = request.getParameter("id"); String str = "delete code where code_id in(" + ID + ")"; System.out.println(ID); params.put("ID", ID); this .call(new Carrier(params, BusinessConstants.UNITS_TOPIC_BUSINESS, "deleteTopicByIdCount")); } Map params = null; List result = (List) this.call(new Carrier(params, BusinessConstants.UNITS_TOPIC_BUSINESS, "ShowTopicAnswer")); request.getSession().setAttribute("resultList", result); return mapping.findForward("unitsTopicAnswer"); } 这里通过request.getParameter("id")方法得到要删除的实体的ID,然后把这个ID返回到Business的方法中进行删除。 (2)Business类: public void processdeleteTopicByIdCount(Carrier vo) { Map params = (Map) vo.getData(); String ID = (String) params.get("ID"); String recId[]=ID.split("'"); List recIdList=null; for(int i=0;i String id=recId[i]; String str="from Code where recId='"+id+"'"; List list=(List)((UnitsTopicSetDAO)this.getDao()).showTopicAnswer(str); Iterator itClass = list.iterator(); while(itClass.hasNext()){ Code code = (Code)itClass.next(); ( (UnitsTopicSetDAO)this.getDao()).DeleteTopicById(code); } } } 这一段代码是把封装的ID串解析后得到每个ID的值,然后根据每个ID的值删除每个实体。 这个封装的ID是在前台页面的js方法中实现的,具体代码如下: var a = function deleteBaseInfo(str) { var a = document.getElementsByName(str); var n = a.length; var temp=""; var flag=true; for (var i=0; i if(a[i].checked){ flag=false; if (temp=="") { temp=a[i].value+"'"; }else { temp = temp + a[i].value+"'"; } } } if(flag) {alert("对不起,你还没有选择!")} else{ if (confirm("确实要删除?")){ window.location.href="<%=request.getContextPath()%>/unitsTopicDifficultyAction.do?action=delete&step=two&id="+temp; }}return !flag; } 图5-8 试卷资源管理模块界面 下面是用户更新一条具体信息的代码: (1)Action类: public ActionForward update(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { UnitsExamTypeAddForm examTypeAddForm = (UnitsExamTypeAddForm) form; SmartExamType examtype = (SmartExamType) request.getSession() .getAttribute(Constants.CACHED_ENTITY); examtype.setExamtypeName(examTypeAddForm.getExamtypeName()); examtype.setExamtypeYear(examTypeAddForm.getExamtypeYear()); examtype.setExamtypeTerm(examTypeAddForm.getExamtypeTerm()); Map map = new HashMap(); map.put("entity", examtype); this.call(new Carrier(map, BusinessConstants.UNITS_EXAMTYPE_BUSINESS, "UpdateExamType")); return mapping.findForward("update");} 这里通过得到form的属性值 (2)Business类: public String processUpdateExamType(Carrier vop){ Map data = vop.getData(); SmartExamType examtype = (SmartExamType) data.get("entity"); if(examtype != null) { this.getDao().update(examtype); } return examtype.getExamtypeNo(); } 这一段代码是得到Action传过来的实体,并通过调用方法update更新这个实体。 设计和开发占据了我大部分的精力和时间,经过反复测试,已完成系统所需的功能。系统的测试界面截图参考上面系统功能设计部分的图片。 单元测试:在开发系统的过程中,对所有代码的正确性和逻辑行得到一定的检验,暂时没有出现错误。单元测试就是指为了确保我们的软件在运行过程中的正确无误。 单元测试任务包括: 模块接口测试、数据库连接测试等。 本系统的单元测试中,使用了Eclipse作为测试工具。在测试过程中,我们要对每一个类实现的方法编写一个相应的测试方法。例如下面一个简单方法的用例。 Public void updateUnitsTopic (Code user) { try { this.update (user); } catch (SynchronizeException ex) { Code tmp = this.loadUnitsTopic (user.getRecId ()); if (tmp != null) { try { BeanUtils.copyProperties (tmp, user); } catch (Exception e) { } this.update (tmp); } } } /*******下面是测试方法正确性**********/ public static void main(String[] args) { Map params = new HashMap(); Code code=new Code(); code.setRemark(“描述”); code.setcodeType(“考试类型”); code.setcodeMudule(“模块名称”); UnitsPublicImpl impl = new UnitsPublicImpl(); impl.updateUnitsTopic (code);//测试方法是否正确 } 功能测试:它是在已知系统所应具有的功能,通过测试来检测每个功能的情况。通过测试,本系统功能全部实现,并能正常使用。 性能测试:测试软件的运行性能。这种测试常常与强度测试结合进行,需要事先对被测软件提出性能指标。从测试的结果看,多个用户同时访问时,性能存在有一个饱和点,一旦超过这个负荷,就使性能表现出下降趋势。而且系统的性能还跟计算机硬件性能也有很大的关系。硬件也好,该软件的性能也越好。 结 论 教育改革能否成功,除了从“教”与“学”两方面着手外,行政改革的执行同样扮演着重要的角色。本文提出一个可在网络平台上运作的在线考试系统,此在线考试系统可以尽最大限度的满足每一位教育局或者学校老师以及学生的要求。而公共资源管理模块在整个系统中是一个基础,它涉及到用户信息设置的资源,如:省,市,学校,年级,班级,等等。还涉及到考试类型设置,试卷类型设置等等参加考试的一些最基本的信息设置。因此这个模块的重要性不言而喻,做完这个模块让我感触最深的是这个模块尽管看上去不是整个系统的重点,但是它是整个系统的基石,尽管看上去都是一些基本的设置(包括修改,删除,添加,搜索等),但是在设计和开发中还要跟整个系统的设计风格以及系统部署环境相符,比如:要考虑是否用户是否要设置乡/镇,村级别的信息等等。这样才能提高系统的人性化。 当然系统某些方面还是有一些不足之处的。如: 总之系统总体上来说已经基本实现了其功能,而且我相信对教育局或者老师来说,这套系统的一定能满足他们的要求的。 参考文献 [1] 陈刚.Eclipse从入门到精通[M].北京:清华大学出版社,2005。 [2] Ted Husted[美].Struts In Action[M].北京:机械工业出版社,2003。 [3] 方睿,刁仁宏,吴四九.网络数据库原理及应用[M].成都:四川大学出版社,2005。 [4] 夏昕. Spring 开发指南[M].北京:电子工业出版社,2003。 [5] 夏昕,曹晓钢,唐勇.深入前出Hibernate[M].北京:电子工业出版社,2005。 [6] 耿祥义,张跃平.Java 2实用教程(第二版)[M].北京:清华大学出版社,2004。 [7] 万峰.Jsp网站开发四“酷”全书[M].北京:电子工业出版社,20055.2.2 教务资源管理模块实现
5.2.3 试题资源管理模块实现
5.2.4 试卷资源管理模块实现
6 测试