【IT168 技术文档】
对一些需要将数据持久化的小型程序中,传统的关系型数据库显得庞大而不实用,OO数据库有一个学习曲线的问题,而使用XML是一种较好的选择.本文将就设计一个合理的XML持久化的解决方案进行探讨.
使用XML作为持久层解决方案的,它的基本功能要有:
1.对象的CRUD功能(本例中基本对象是Member类).
2.保证线程安全,对桌面程序和Web程序都一样适用.
3.有缓存,在存储介质即XML文件突然丢失的情况下还能有效工作.
本例采用了MemberService和MemberPersistence两个类来协作完成这些功能.
MemberService是业务层和持久层之间的桥梁,用于对Member对象的CRUD操作,内置一个hashtable来缓存Member对象,即使文件突然丢失,缓存中的数据也不会被影响.它内置一个MemberPersistence成员来完成与持久介质的交互.
实现添加,删除,更新的三个函数add(),delete(),update()都用lockObj实现了同步,这样就无需担心线程安全问题.其它函数对members成员没有修改,故不需要实现同步.
try{ memberPersistence.add(member); members.put(member.getId(), member); return true; }catch(XmlFileWriteException ex){System.out.println("Member:" + member.getId() + " add error!"); return false; }
当MemberPersistence添加对象成功后,这个对象才会被添加到members中,这样保证了缓存和实际数据的同步;如果颠倒一下顺序,那末MemberPersistence添加对象不成功时,出现XmlFileWriteException异常,这是还需要在catch中删除刚才添加的对象,这样做人为加大了程序的复杂度,不如上面的做法简单高效.
关于查询函数的做法不明白的请见 http://www.blogjava.net/sitinspring/archive/2007/06/05/122119.html 中形式三.
下面是MemberService类的全部代码:
MemberPersistence类是与XML文件打交道的类,通过它,数据才能真正存入持久介质-XML文件.它的函数都很好理解.这些函数工作时实际只会引发两种异常--读XML文件异常(一般由多个根节点导致)和写XML文件异常,会触发这些异常的package com.sitinpsring.service; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import com.sitinpsring.domain.Member; import com.sitinpsring.domain.MemberFilter; import com.sitinpsring.exception.XmlFileReadException; import com.sitinpsring.exception.XmlFileWriteException; import com.sitinpsring.persistence.MemberPersistence; public class MemberService { private static Hashtable<String, Member> members; private static MemberPersistence memberPersistence; private static final Object lockObj = new Object(); static { try { memberPersistence = new MemberPersistence("member.xml"); members = memberPersistence.loadMemberFromFile(); } catch (XmlFileReadException ex) { System.out.println("Can't read the file:member.xml"); }catch (XmlFileWriteException ex) { System.out.println("Can't write to the file:member.xml"); } } public MemberService() { } public boolean hasMember(String id) { return members.containsKey(id); } public boolean hasMember(Member member) { return hasMember(member.getId()); } public boolean add(Member member) { if (hasMember(member)) { System.out.println("Member:" + member.getId() + " has been exist!"); return false; } else { synchronized (lockObj) { try{ memberPersistence.add(member); members.put(member.getId(), member); return true; } catch(XmlFileWriteException ex){ System.out.println("Member:" + member.getId() + " add error!"); return false; } } } } public boolean update(Member member) { if (hasMember(member)) { synchronized (lockObj) { try{ memberPersistence.update(member); Member oldMember = members.get(member.getId()); oldMember.setName(member.getName()); oldMember.setAge(member.getAge()); return true; } catch(XmlFileWriteException ex){ System.out.println("Member:" + member.getId() + " upate error!"); return false; } } } else { System.out.println("Member:" + member.getId() + " can't been found!"); return false; } } public boolean saveOrUpdate(Member member) { if (hasMember(member)) { return update(member); } else { return add(member); } } public boolean delete(Member member) { if (hasMember(member)) { synchronized (lockObj) { try{ memberPersistence.delete(member.getId()); members.remove(member.getId()); return true; }catch(XmlFileWriteException ex){ System.out.println("Member:" + member.getId() + " delete error!"); return false; } } } else { System.out.println("Member:" + member.getId() + " can't been found!"); return false; } } @SuppressWarnings("unchecked") public List<Member> search(MemberFilter memberFilter) { ArrayList<Member> retval = new ArrayList<Member>(); for (Iterator it = members.keySet().iterator(); it.hasNext();) { String key = (String) it.next(); Member member = members.get(key); if (memberFilter.accept(member)) { retval.add(member); } } Collections.sort(retval); return retval; } public List<Member> getAll() { MemberFilter filter = new MemberFilter() { public boolean accept(Member member) { return true; } }; return search(filter); } public Member getMemberById(String id) { for (Iterator it = members.keySet().iterator(); it.hasNext();) { String key = (String) it.next(); Member member = members.get(key); if (member.getId().equals(id)) { return member; } } return null; } }
函数都应该对他们进行捕获和抛出处理.
package com.sitinpsring.persistence; import java.io.File; import java.io.FileWriter; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import com.sitinpsring.domain.Member; import com.sitinpsring.exception.XmlFileReadException; import com.sitinpsring.exception.XmlFileWriteException; public class MemberPersistence { private String xmlFilePos; private Document document; public MemberPersistence(String xmlFilePos) throws XmlFileReadException,XmlFileWriteException{ this.xmlFilePos = xmlFilePos; if (isFileExist(this.xmlFilePos) == false) { // Create document when file not exist createDocument(); return; } else { // Get Docunent when file exist SAXReader reader = new SAXReader(); try { document = reader.read(new File(this.xmlFilePos)); } catch (Exception ex) { throw new XmlFileReadException(ex.getMessage()); } } } private void createDocument() throws XmlFileWriteException{ String text = "<members></members>"; try { document = DocumentHelper.parseText(text); writeDocumentToFile(); } catch (XmlFileWriteException ex) { throw ex; }catch (Exception ex) { ex.printStackTrace(); } } private void writeDocumentToFile() throws XmlFileWriteException{ try { XMLWriter writer = new XMLWriter(new FileWriter(this.xmlFilePos)); writer.write(document); writer.close(); } catch (Exception ex) { throw new XmlFileWriteException(ex.getMessage()); } } public Hashtable<String, Member> loadMemberFromFile() { Hashtable<String, Member> retval=new Hashtable<String, Member>(); List nodes = document.getRootElement().elements("member"); for (Iterator it = nodes.iterator(); it.hasNext();) { Element elm = (Element) it.next(); Member member = new Member(elm.attributeValue("id"),elm.elementText("name"),Integer.parseInt(elm.elementText("age"))); retval.put(member.getId(), member); } return retval; } public boolean add(Member member) throws XmlFileWriteException{ try { Element rootElm = document.getRootElement(); Element newMemberElm = rootElm.addElement("member"); newMemberElm.addAttribute("id", member.getId()); Element nameElm=newMemberElm.addElement("name"); nameElm.setText(member.getName()); Element ageElm=newMemberElm.addElement("age"); ageElm.setText(String.valueOf(member.getAge())); writeDocumentToFile(); return true; } catch (XmlFileWriteException ex) { throw ex; } } public boolean update(Member member) throws XmlFileWriteException{ try { Element rootElm = document.getRootElement(); List nodes = rootElm.elements("member"); for (Iterator it = nodes.iterator(); it.hasNext();) { Element elm = (Element) it.next(); if(elm.attributeValue("id").equals(member.getId())){ elm.element("name").setText(member.getName()); elm.element("age").setText(String.valueOf(member.getAge())); break; } } writeDocumentToFile(); return false; } catch (XmlFileWriteException ex) { throw ex; } } public boolean delete(String id) throws XmlFileWriteException{ try { Element rootElm = document.getRootElement(); List nodes = rootElm.elements("member"); for (Iterator it = nodes.iterator(); it.hasNext();) { Element elm = (Element) it.next(); Member member = new Member(elm.attributeValue("id"),elm.elementText("name"),Integer.parseInt(elm.elementText("age"))); if(member.getId().equals(id)){ rootElm.remove(elm); break; } } writeDocumentToFile(); return false; } catch (XmlFileWriteException ex) { throw ex; } } public static boolean isFileExist(String filePath) { boolean exists = (new File(filePath)).exists(); return exists; } }
Member类代码如下:
MemberFilter接口用于对象的查找:package com.sitinpsring.domain; import java.text.Format; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; public class Member implements Comparable{ private String id; private String name; private int age; public Member(String name,int age){ this(getIdByTime(),name,age); } public Member(String id,String name,int age){ this.id=id; this.name=name; this.age=age; } private static String getIdByTime(){ Date date = new Date(); Format formatter = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss."); Calendar cal = new GregorianCalendar(); int ms = cal.get(Calendar.MILLISECOND); // 0..999 return formatter.format(date)+ms; } public int compareTo(Object obj){ Member another=(Member)obj; return this.name.compareTo(another.name); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString(){ return "\nMember:"+this.id+" Name="+this.name+" Age="+this.age; } }
两个必要的异常类:package com.sitinpsring.domain; public interface MemberFilter { public boolean accept(Member member); }
package com.sitinpsring.exception; public class XmlFileReadException extends Exception{ private static final long serialVersionUID = 274126394941426299L; public XmlFileReadException(){ super(); } public XmlFileReadException(String errmsg){ super(errmsg); } }
示例调用过程:package com.sitinpsring.exception; public class XmlFileWriteException extends Exception{ private static final long serialVersionUID = 27412635354426299L; public XmlFileWriteException(){ super(); } public XmlFileWriteException(String errmsg){ super(errmsg); } }
代码下载:try { MemberService memberService = new MemberService(); List<Member> list6 = memberService.getAll(); System.out.println("list6=" + list6); Member billGates=memberService.getMemberById("2007.08.27.13.40.58.558"); billGates.setAge(48); memberService.update(billGates); List<Member> list7 = memberService.getAll(); System.out.println("list7=" + list7); // Trace by step Member andy = new Member("Andy", 20); Member bill = new Member("bill", 21); Member cindy = new Member("cindy", 22); Member domino = new Member("domino", 23); Member ein = new Member("ein", 24); Member flex = new Member("flex", 25); // Add Process memberService.add(andy); memberService.add(bill); memberService.add(cindy); memberService.add(domino); List<Member> list1 = memberService.getAll(); System.out.println("list1=" + list1); // Delect Process memberService.delete(domino); List<Member> list2 = memberService.getAll(); System.out.println("list2=" + list2); // Update Process memberService.add(ein); bill.setAge(27); memberService.update(bill); List<Member> list3 = memberService.getAll(); System.out.println("list3=" + list3); // SaveOrUpdate Process memberService.saveOrUpdate(flex); bill.setName("Bill Gates"); memberService.saveOrUpdate(bill); List<Member> list4 = memberService.getAll(); System.out.println("list4=" + list4); // Search Process MemberFilter agefilter = new MemberFilter() { public boolean accept(Member member) { return member.getAge() < 27 && member.getAge() > 22; } }; List<Member> list5 = memberService.search(agefilter); System.out.println("list5=" + list5); } catch (Exception ex) { ex.printStackTrace(); }
http://www.blogjava.net/Files/sitinspring/XmlPersistence20070827145516.rar