试题管理是整个系统的核心。为了尽量的从逻辑上组织试题和尽量的减少数据冗余。在线考试系统把领域对象划分地非常细。下图4-7说明了试题库相关领域类的设计
图4-7 试题库模块类图
这些领域类最终都要保存到数据库表里。为了方便管理将所有QuestionContent类的所有子类全部保存到QuestionContent表中。并用Type字段来区分不同的题型。Choice侧以XML的格式保存到Choices字段中。并编写了自定义的ChoiceList类来负责Choice集合从字段中的XML格式到对象集合的转换。
{
public class ChoiceList : IUserType
{
//代码省略…
}
}
图4-8 QuestionContent表
为了支持章节的树形模型。也就是说每个章节可以包含多个子章节。我们的Chapter表中有一个ParentID字段用来指明该章节的父章节。如下图4-9。
图4-9 Chapter表
Subject表如下图4-10:
图4-10 Subject表
有了领域类的关系和对应的表关系。当需要加载领域类的时候必须把表里的记录转换成内存中的对象。当要保存领域类时候又必须把内存对象保存成记录。这些任务都有NHibernate自动来完成。我们要做的就是告诉NHibernate如何来做这两种不同风格数据表现的转换和映射。我们通过映射文件来告诉NHibernate那个类应该保存到那个表里。那个属性应该保存在那个字段里或者保存到其他表里。反之亦然!下面就用Subject,和Chapter来说明类到表是如何映射的。
<hibernate-mapping XMLns="urn:nhibernate-mapping-2.2" assembly="ExaminationSystem.BLL" namespace="ExaminationSystem.BLL.Domain">
<class name="Subject" table="Subject" lazy="false">
<id name="ID" column="ID">
<generator class="identity" />
</id>
<property name="Name" column="Name" />
<bag name="Chapters" table="Chapter" inverse="true" cascade="all">
<key column="SubjectID"/>
<one-to-many class="Chapter"/>
</bag>
<bag name="PaperStrategys" table="PaperStrategy" inverse="true" cascade="all">
<key column="SubjectID"/>
<one-to-many class="PaperStrategy"/>
</bag>
</class>
</hibernate-mapping>
以上是Subject.hbm.XML的完整代码其中
<class name="Subject" table="Subject" lazy="false">
节点告诉NHibernate Subject类放到Subject表。
<id name="ID"column="ID">
<generator class="identity" />
</id>
告诉NHibernate,ID属性是Subject的标识属性,对应到表ID自增长字段。
<property name="Name" column="Name" />
告诉Nhibernate Name属性保存到Name字段。
<bag name="Chapters" table="Chapter" inverse="true" cascade="all">
<key column="SubjectID"/>
<one-to-many class="Chapter"/>
</bag>
这里的Chapters属性是一个Chapter对象的集合。这些Chapter对象保存到Chapter表中。并且是通过Chapter表的SubjectId类来标识Chapter表的记录属于那个Subject对象。这里只给了Subject类的映射文件。并且只是简单的说明了一般属性的映射和一到多关系的映射。关于更加复杂的类继承关系映射和自定义用户类型的映射、多对多关联的映射、延迟加载等更高级的主题请参考NHibernate官方文档。通过映射文件NHibernate完成了类到表,表到类的转换。让我们可以用面向对象的方式设计和实现系统。使系统更加的健壮和灵活。下面介绍关于QuestionContent及其所有子类的更新和删除问题。为了减少数据冗余我们将试题分成了两种不同的概念。分别是Question和QuestionContent 。Question代表的是试卷上的试题。下面用类图来说明Question和QuestionContent的关系。如图4-11:
图4-11 Question和QuestionContent关系类图
QuestionContent包含了题目的内容和标准答案、难度等信息。Question类包含分值和学生答案和得分。这样分离可以使每个试题的内容和标准答案只保留一份。比如当有100张试卷。且每张试卷都只有一样的一道题。会有100个不同Question对象.但是只要一个QuestionContent对象。这样设计无疑会大大的减少数据的冗余。但是也带来了一些问题。比如,如果试卷Paper上的Question对象上引用了一个QuestionContent的对象。我们将无法删除这个QuestionContent对象。因为Question对象对QuestionContent对象的引用在数据库中是通过外键来引用的。如果我们删除一个被引用着的QuestionContent对象将违反外键约束。而且即使没有外键约束在逻辑上也不应该删除修改这个QuestionContent对象。假设一个学生做过了一个题A,题的内容是B。学生答对了该题目。学生根据分值得到了100分。如图4-12:
图4-12 Question和Content对象图
后来老师忽然觉得这个题目过于简单。于是将试题内容改了改。B对象的Content变成了1*1=?。答案自然也变成了1。如果老师改完题目后又直接去改卷子。发现学生竟然答案是2于是无情的就给了0分。当然这些假如有些勉强,但是问题是在逻辑上一旦题目在考试卷上出现过它就没办法改变。似乎Question应该单独拥有一个QuestionContent对象。这样在修改题库里QuestionContent才不会影响Question对象。但是如果这样就会有很多的重复的QuesitonContent对象。从逻辑上分析QuesitonContent 对象确实应该是一个值类型的对象。而不应该是引用类型。但是作为引用类型来处理确实能省掉很多的内存和数据库空间。只是在修改的时候才会出现问题。在线考试系统的确实是用引用类型的方式来处理QuestionContent的。但是怎样才能避免上面提到的修改问题呢。也就是说如何让一个引用类型的对象具有值类型的行为。这里借鉴了.net类库里对Sting类型的实现方法。就是保证QuestionContent 具有不变性。当在题库更新一个QuestionContent对象的时候就新创建一个QuestionContent然后在将旧的对象和Chapter对象直接的关系给移除。这样从逻辑上就把旧的QuestionContent给删除了。但是又不影响Quesiton。删除时就是把QuestionContent对象和Chapter对象的关系解除。当然如果这个QuestionContent
对象从来没有被Quesiton对象引用过。我们只解除和Chapter的关系而不是物理删除它会存在很多垃圾数据。这些数据将永远存在数据库中,却永远不会被系统使用。内存中的垃圾数据可以有垃圾回收机制来回收。但是数据库中的却没有,所以我们还应该定期的清理那些无用的数据。
{
//更新试题操作
public void Update(QuestionContent questionConent)
{
//新建一个试题,并删除老的试题
QuestionContent newQuesitonContent = new QuestionContent();
newQuesitonContent.Chapter = questionConent.Chapter;
newQuesitonContent.Content = questionConent.Content;
newQuesitonContent.Answer = questionConent.Answer;
Dao.Save(newQuesitonContent);
Delete(questionConent);
}
public void Delete(QuestionContent quesitonContent)
{
//删除操作只是将试题和章节解除关联
questionContent.Chapter=null;
Dao.Update(questionContent);
}
public void ClearUselessQuestionContent(QuestionContent quesitonContent)
{
//如果试题没有被使用就从数据库中删除,否则什么都不做
if(questionContent is not used)
Dao.Delete(quesitonContent);
else
//Do nothing
}
}
这样就可以即减少数据冗余又保证系统的正确性!