为了巩固之前学习过的XML编程,本讲会教大家做一个XML式的考试成绩管理系统。这里把XML当做数据库,完成系统的增加、删除、查找功能!之所以能把XML当做是数据库,是因为XML可以体现数据之间的关系,可以完成整个数据库单独模块内容的操作!此外,利用三层架构,将程序的功能拆分,某部分代码负责某部分的功能实现!将整个程序进行拆分,如图所示:
有一点须注意,写程序的时候,先写JavaBean,开发从下层往上层写,因为此处没有数据库,所以先把代表数据库的exam.xml写出来。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<exam>
<student examid="222" idcard="111">
<name>李阿昀</name>
<location>天门</location>
<grade>90</grade>
</student>
<student examid="444" idcard="333">
<name>徐婷</name>
<location>武汉</location>
<grade>97</grade>
</student>
</exam>
第一步,先将代表学生的JavaBean写出来,其起到的作用就是对象数据的存储或传递。
package cn.liayun.domain;
public class Student {
private String idcard;//身份证号
private String examid;//准考证号
private String name;
private String location;//所在地
private double grade;
public String getIdcard() {
return idcard;
}
public void setIdcard(String idcard) {
this.idcard = idcard;
}
public String getExamid() {
return examid;
}
public void setExamid(String examid) {
this.examid = examid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public double getGrade() {
return grade;
}
public void setGrade(double grade) {
this.grade = grade;
}
}
第二步,将XmlUtils工具类写出来,封装得到Document对象和更新文档这些工具性质的操作!
package cn.liayun.utils;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
//公共的代码使用工具类进行抽取,工具类中的方法都为静态(约定俗成)
public class XmlUtils {
private static String filename = "src/exam.xml";
// 获得Document对象
public static Document getDocument() throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(filename);
}
/*
* 更新XML文档
* 当想把异常当作返回值使用编译时异常,否则通通转换为运行时异常
*/
//thinking in java的作者
//java之父
//spring作者
public static void write2Xml(Document document) throws Exception {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer tf = factory.newTransformer();
tf.transform(new DOMSource(document), new StreamResult(new FileOutputStream(filename)));
}
}
注意:一定要深刻认识到对异常的处理原则,当要把异常当作一个返回值的时候,使用编译时异常,否则通通转换为运行时异常。
第三步,编写最核心部分的代码,即StudentDao类,该类实现添加用户、删除用户、查询用户等功能;而且还可以通过该类深刻理解在实际开发中是如何对异常进行处理的。
package cn.liayun.dao;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import cn.liayun.domain.Student;
import cn.liayun.exception.StudentNotExistException;
import cn.liayun.utils.XmlUtils;
public class StudentDao {
public void add(Student s) {
try {
Document document = XmlUtils.getDocument();//throws出了编译时Exception(checked exception)
//创建出封装学生信息的标签
Element student_tag = document.createElement("student");
student_tag.setAttribute("idcard", s.getIdcard());
student_tag.setAttribute("examid", s.getExamid());
//创建用于封装学生姓名、所在地和成绩的标签
Element name = document.createElement("name");
Element location = document.createElement("location");
Element grade = document.createElement("grade");
name.setTextContent(s.getName());
location.setTextContent(s.getLocation());
grade.setTextContent(s.getGrade() + "");
student_tag.appendChild(name);
student_tag.appendChild(location);
student_tag.appendChild(grade);
//把封装了学生信息的标签挂到文档上
document.getElementsByTagName("exam").item(0).appendChild(student_tag);
//更新内存
XmlUtils.write2Xml(document);
} catch (Exception e) {
/*
* 编译时异常,必须自己处理,直接抛到上层甚至上上层,
* 没有意义只会带来麻烦,只能抓,并且对异常进行转型,
* 转成运行时异常再抛出去,但异常链不能断,
* 必须把原来的异常信息封装进去,抛出异常给上一层,
* 上一层就会知道到底出了什么问题,问题已经报告给上层了,
* 它爱处理就处理,不处理就不处理。
*/
throw new RuntimeException(e);//为了避免给上一层带来麻烦,通常会对这个异常进行转型,转成unchecked exception
/*
* 出现异常,一定要通知上一层程序,抛个异常给上一层,
* 不能打印在控制台上,没有意义,因为这只是给程序员看的。
*/
// e.printStackTrace();
}
}
public Student find(String examid) {
try {
Document document = XmlUtils.getDocument();
NodeList list = document.getElementsByTagName("student");
//NodeList没实现Iterable接口,所以不能使用增强for循环
for (int i = 0; i < list.getLength(); i++) {
Element student_tag = (Element) list.item(i);
if (student_tag.getAttribute("examid").equals(examid)) {
//找到与examid相匹配的学生,new出一个Student学生对象来封装这个学生信息并返回。
Student s = new Student();
s.setExamid(examid);
s.setIdcard(student_tag.getAttribute("idcard"));
s.setName(student_tag.getElementsByTagName("name").item(0).getTextContent());
s.setLocation(student_tag.getElementsByTagName("location").item(0).getTextContent());
s.setGrade(Double.parseDouble(student_tag.getElementsByTagName("grade").item(0).getTextContent()));
return s;
}
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//此处抛出编译时异常,则必须要处理,即让调用者知道删除是否成功
public void delete(String name) throws StudentNotExistException { //delete("xxx")
try {
Document document = XmlUtils.getDocument();
NodeList list = document.getElementsByTagName("name");
for (int i = 0; i < list.getLength(); i++) {
if (list.item(i).getTextContent().equals(name)) {
list.item(i).getParentNode().getParentNode().removeChild(list.item(i).getParentNode());
XmlUtils.write2Xml(document);//更新内存
return;
}
}
/*
* 删除用户没有成功,抛出一个编译时异常(自定义异常),
* 自定义异常都放在exception包中,如cn.liayun.exception
*
* 当要把异常当作一个返回值的时候,使用编译时异常
*/
throw new StudentNotExistException(name + "不存在!!!");//异常作为返回值,就抛一个编译时异常
} catch (StudentNotExistException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
这里还需要定义一个自定义异常:StudentNotExistException。
package cn.liayun.exception;
public class StudentNotExistException extends Exception {
public StudentNotExistException() {
// TODO Auto-generated constructor stub
}
public StudentNotExistException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public StudentNotExistException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
public StudentNotExistException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public StudentNotExistException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
}
第四步,在实际的代码实现阶段,还会需要一个测试类,方便于在整个程序还没有完成的时候对程序的部分功能进行测试,做一步,测试一步,以免整个程序完成后由于页面太多或者是代码量太大给查找错误造成更大的负担!所以这里会多一个测试类!此测试类放在junit.test包中。
package junit.test;
import org.junit.Test;
import cn.liayun.dao.StudentDao;
import cn.liayun.domain.Student;
import cn.liayun.exception.StudentNotExistException;
public class StudentDaoTest {
@Test
public void testAdd() {
StudentDao dao = new StudentDao();
Student s = new Student();
s.setExamid("121");
s.setGrade(89);
s.setIdcard("121");
s.setLocation("北京");
s.setName("林青霞");
dao.add(s);
}
@Test
public void testFind() {
StudentDao dao = new StudentDao();
dao.find("121");
}
@Test
public void testDelete() throws StudentNotExistException {
StudentDao dao = new StudentDao();
dao.delete("林青霞");
}
}
第五步,即最后一步,编写主页UI类(虽然这里的界面仍然是在控制台)。
package cn.liayun.UI;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import cn.liayun.dao.StudentDao;
import cn.liayun.domain.Student;
import cn.liayun.exception.StudentNotExistException;
public class Main {
public static void main(String[] args) {
try {
System.out.println("添加学生(a)\t删除学生(b)\t查找学生(c)");
System.out.print("请输入操作类型:");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String type = br.readLine();
if ("a".equals(type)) {
System.out.print("请输入学生姓名:");
String name = br.readLine();
System.out.print("请输入学生准考证号:");
String examid = br.readLine();
System.out.print("请输入学生身份证号:");
String idcard = br.readLine();
System.out.print("请输入学生所在地:");
String location = br.readLine();
System.out.print("请输入成绩:");
String grade = br.readLine();
Student s = new Student();
s.setExamid(examid);
s.setGrade(Double.parseDouble(grade));
s.setIdcard(idcard);
s.setLocation(location);
s.setName(name);
StudentDao dao = new StudentDao();
dao.add(s);
System.out.println("添加成功!!");
} else if ("b".equals(type)) {
System.out.print("请输入要删除的学生姓名:");
String name = br.readLine();
try {
StudentDao dao = new StudentDao();
dao.delete(name);
System.out.println("删除成功!!!");
} catch (StudentNotExistException e) {
/*
* 不需要记录这个异常,预料不到才记录,便于后来维护,但此异常是预料到的,
* 所以不需要记录这个异常。
*/
System.out.println("您要删除的用户不存在!!!");
}
} else if ("c".equals(type)) {
//...自己做完
System.out.print("请输入查询的学生准考证号:");
String examid = br.readLine();
StudentDao dao = new StudentDao();
Student s = dao.find(examid);
if(s != null) {
System.out.println("您查询的学生信息为:");
System.out.println("姓名:"+s.getName()+", 身份证号:"+
s.getIdcard()+", 准考证号:"+s.getExamid()
+", 地区:"+s.getLocation()+", 成绩:"+s.getGrade());
} else {
System.out.println("没有查询到任何信息!!!");
}
} else {
System.out.println("不支持您的操作!!!");
}
} catch (IOException e) {
/*
* 给用户友好提示,还要在后台记录这个异常,记录这个异常是为了便于
* 程序员去观察这个异常,去修正这个异常,去维护这个程序。
*/
e.printStackTrace();//需要在后台,日志记录器记录这个异常。
System.out.println("对不起,俺出错了!!!");
}
}
}
至此,一个XML式的考试成绩管理系统就算做出来了。这个案例最重要的还是XML节点操作的应用,顺带的加上了简单的三层构架!工具类,业务实现(实际操作)类,UI类等!