注:本文是"使用XML文件作为Web系统持久层的OO分析过程"(http://www.blogjava.net/sitinspring/archive/2007/06/03/121640.html)的续文.

对一些需要将数据持久化的小型程序中,传统的关系型数据库显得庞大而不实用,OO数据库有一个学习曲线的问题,而使用XML是一种较好的选择.本文将就设计一个合理的XML持久化的解决方案进行探讨.

使用XML作为持久层解决方案的,它的基本功能要有:
1.对象的CRUD功能(本例中基本对象是Member类).
2.保证线程安全,对桌面程序和Web程序都一样适用.
3.有缓存,在存储介质即XML文件突然丢失的情况下还能有效工作.

本例采用了MemberService和MemberPersistence两个类来协作完成这些功能.

MemberService是业务层和持久层之间的桥梁,用于对Member对象的CRUD操作,内置一个hashtable来缓存Member对象,即使文件突然丢失,缓存中的数据也不会被影响.它内置一个MemberPersistence成员来完成与持久介质的交互.

实现添加,删除,更新的三个函数add(),delete(),update()都用lockObj实现了同步,这样就无需担心线程安全问题.其它函数对members成员没有修改,故不需要实现同步.

值得一提的是这三个函数的内部写法,下面是add()函数的一部分:
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类的全部代码:

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;
    }

}



MemberPersistence类是与XML文件打交道的类,通过它,数据才能真正存入持久介质-XML文件.它的函数都很好理解.这些函数工作时实际只会引发两种异常--读XML文件异常(一般由多个根节点导致)和写XML文件异常,会触发这些异常的函数都应该对他们进行捕获和抛出处理.

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