DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 將XML結點轉換成JAVABEAN並存入數據庫
將XML結點轉換成JAVABEAN並存入數據庫
編輯:XML詳解     

1.概述 我們要將外部系統給的XML文件進行解析,並存入到數據庫。

但是我們並沒有DTD或者Schema,只有一個Word格式的說明文檔;更離譜的是,XML結點樹的結構(即XML結點與XML結點之間的關系)與業務Bean樹的結構(即業務Bean與業務Bean的關系)並不完全一致,比如說,從業務角度講,一只豬有只豬頭,而在XML裡,卻寫成了 pig --content --pighead 的三級關系,無端端多了一個content結點! 沒有DTD/Schema,結構又不規范,我們就沒法用自動化的第三方Java轉換API進行解析,而只能手動地、一個一個地解析。但在手動解析的過程中,我們仍然發現各個結點的解析和入庫中有很多東西是共同的,或者有共同的規律,這些東西可以抽出來作為一個准框架,然後再將結點中不同的部分開放出來,允許具體的結點做具體的實現,並最終形成一個半自動的解析/入庫框架。

為什麼說它是半自動的?它有哪些限制?

自動:不必為每個結點編寫XML 解析代碼和入庫代碼

“半”:需手動地編寫每個JavaBEAN,並手動地為每個BEAN建表

限制:

a.所有業務字段的類型只能設為STRING/VARCHAR,並且非業務字段的類型在BEAN中不能為STRING

b.BEAN名與表名必須相同,或者可以進行一對一映射

c.BEAN的成員變量名必須與XML結點的屬性名/元素名相同,或者可以進行一對一映射

這三種限制都是利用Java反射機制進行自動操作的前提。

2.基本思想

所謂的XML解析,就是將XML結點轉換成JAVABEAN實例,XML結點的ATTRIBUTE值和ELEMENT值就是JAVABEAN實例的成員變量值; 所謂的持久化,就是將JAVABEAN實例變成數據庫對應表中的一條記錄,JavaBEAN實例的成員變量值就是記錄中某個字段的值,或者其他表中某個參考了該記錄的另一條記錄。

而在XML中,JavaBEAN體系中,數據據表關系結構中,結點和結點之間的關系都是樹形的關系。整體的解析和入庫,就是在遍歷樹時執行轉換動作。而我們知道,樹的遍歷是可以用遞歸算法實現的,而遞歸,就不用說了吧,它是實現程序“自動化”的主要途徑之一。

以下是對各“樹”的具體分析:

假設兩個業務實體A和B之間存在聚合關系(父子關系)。那麼具體可分三種情況:

a.B是一個原子字段(即不可再分),並且是A的一個屬性。

XML中,B是A的XML ATTRIBUTE或者A的原子ELEMENT

BEAN中,B是A的成員變量,並且B是一個Java內置的數據類型

數據庫中,B是A表的一個列

b.B是一個復合字段,並且是A的一個屬性,而且和A是1:1關系

XML中,B是A的ELEMENT,並且B有自己的ELEMENT或者ATTRIBUTE

BEAN中,B是A的成員變量,並且程序中有個B類

數據庫中,B表是A表的子表(即B外鍵參考了A表)

c.B是一個復合字段,並且是A的一個屬性,而且和A是N:1關系

XML中,B是A的ELEMENT,並且B有自己的ELEMENT或者ATTRIBUTE

BEAN中,B組成一個類集(List,Set)共同作為A的成員變量,並且程序中有個B類

數據庫中,B表是A表的子表(即B外鍵參考了A表)

了解了這三種情況,接下來就好辦了。程序每抓到一個結點,都要遞歸地進行以下處理:先處理它的原子屬性(情形a),接著處理它的單個子結點(情形b),最後處理它的類集子結點( 情形c)。

3.代碼實現的重點

兩個重點:

a.如何讓業務實體在三棵樹內一一對應好?

b.如何發現樹形關系,比如A的屬性有哪些,A的子結點有哪些?

問題a很簡單,就是讓三棵樹裡相同的業務實體取相同的名字。

a.解析XML時發現 結點X 的 屬性Y 等於 值Z,則執行PropertyUtils.setProperty(結點X , 屬性Y , 值Z)即可。在這裡X,Y,Z是變量,程序不用關心具體的結點和屬性是哪些個。需要注意的是,如果屬性Y是原子字段,則要求屬性Y必須為String類型,否則程序不知道將值Z轉換成哪種類型(注:關於PropertyUtils, 請見apache commons Beanutils )

b.入庫時發現x.getY()=z。如果屬性y是原子字段,則執行SQL insert into X(...,y,...) values (...,z,...),這裡要求y字段必須為varchar/char類型, 以免發生類型轉換錯誤.

關於問題b

XML樹:JDOM, dom4j等都可以直接找到父子關系

BEAN體系:

I.原子屬性。我們限定一個BEAN中所有有業務意義的原子字段的類型都STRING,所有String類型的字段都是業務字段

II.單個子結點。我們讓所有有業務意義的非原子字段都實現一個共同的接口BusiNode,這樣一個BEAN中所有BusiNode成員都是這個BEAN的子結點

III.類集子結點。我們也可以限定所有且只有類集子結點可以使用List或Set類型,這樣可以利用過濾出所有類集子結點。然而,在JAVA1.4及以前的版本裡,程序並不知道過濾出的類集子結點是哪個Class的實例(因為沒有泛型),也就沒辦法實例化一個類集子結點(見後文),因此只能手動注冊類集子結點的屬性名和Class。Java1.5以上的版本我沒用過,不知道可不可以解決這個問題。

數據庫表關系: 這就不用多說了,就是通過外鍵參考。因此每類結點對應的表中,都必須有個外鍵,以參考它的父結點;還必須有個主鍵,以供它的子結點參考。各表的外鍵名必須相同並為一常數,否則程序生成INSERT SQL時才可以不用理會具體表的具體的外鍵名。

程序在解析時,遍歷的是BEAN樹;在持久化時也是。比起XML樹,BEAN樹代表真正的業務結構;比起數據庫表關系樹,BEAN樹才能由父至子地進行先序遍歷

4.其他問題

a.要讓程序知道,原子屬性中哪些是XML結點的屬性,哪些是XML結點的原子ELEMENT。代碼中這是兩個抽象方法,必須讓具體的結點類實現

b.回顧本文概述部分提到的“pig --content --pighead 的三級關系,無端端多了一個content結點”,因此我們要讓程序知道,pighead,pigfoot等結點的子結點,究竟是pig,還是pig下的content。處理不規范XML時要注意這個問題。這也是一個抽象方法,必須讓具體的結點類實現

c.與上一條類似但更變態的,是類集結點的不規范問題。假設一個pig有多個pighead,那結構可能為 pig--pighead,pighead,...,也可能為pig--pigheads--content,content.... 必須讓程序知道某個具體結點用的是哪種模式

5.代碼

核心:多態 + 遞歸

a.接口BusiNode

import Java.util.*;

import org.dom4j.Element;

/**
 * 每個結點都要實現的接口
 * 它提供了一些方法以方便實現遞歸的XML解析和持久化
 *
 */
public interface BusiNode {
    
    /**
     * 所有類型為不可分類型的屬性
     * @return 屬性名的集合
     */
    public List getAtomicPropNames();
    
    /**
     * 一些成員變量。這些成員變量是XML結點的屬性
     * @return
     */
    public List getXmlAttributes();
    
    /**
     * 一些成員變量。這些成員變量是XML結點的子元素,並且類型為不可分的
     * @return
     */
    public List getXmlAtomicElements();
    
    
    /**
     * 所有類型為類集的屬性,並且這些類集中每個元素的類型都是BusiNode
     * @return  key = 屬性名, value = 屬性類的Class對象。
     * 如果為空不返回NULL,而是空的MAP
     */
    public Map getCollectionPropsMap();
    

    /**
     * 所有類型為BusiNode的屬性
     * @return 屬性名的集合
     */
    public List getBusiNodePropNames();
    
    
    /**
     * 從XML中解析出來。
     * @param element
     * @return
     */
    public void parseFromXML(Element element);    
    }
b.默認的實現 

import java.lang.reflect.FIEld;
import Java.util.*;
import org.apache.commons.beanutils.PropertyUtils;
import org.dom4j.Attribute;
import org.dom4j.Element;

/**
 * 默認的BUSI NODE。 繼承此類的BUSI NODE 需滿足 所有不可分屬性集=String類型的屬性集
 * MyUtils類的代碼欠奉
 * 
 */
public abstract class DefaultBusiNode implements BusiNode {

    public List getAtomicPropNames() {
        return MyUtils.getFieldNamesOfClass(this.getClass(), String.class);
    }

    public List getBusiNodePropNames() {
        return MyUtils.getFIEldNamesOfClass(this.getClass(), BusiNode.class);
    }

    /*
     * 所有子元素的父元素。有時是本結點,有時是本結點下的元素。變態
     */
    public abstract Element getXmlElementParent(Element rootElement);

    /*
     * 類集子結點根元素的Iterator 。 假設一個pig有多個pighead,
     * 那結構可能為 pig--pighead,pighead,...,
     * 也可能為pig--pigheads--content,content.... 
     * 必須讓程序知道某個具體結點用的是哪種模式
     * 
  
     * 如果為空則返回一個空類集的Iterator ,不要返回NULL
     */
    public abstract Iterator getCollectionElementIterator(
            Element xmlElementParent, String attName);

    /**
     * 解析XML屬性
     * 
     * @param rootElement

*/ protected void parseAttributesFromXml(Element rootElement) { List xmlAttributes = this.getXmlAttributes(); for (int i = 0; i < this.getXmlAttributes().size(); i++) { String attName = (String) xmlAttributes.get(i); Attribute att = rootElement.attribute(attName); if (att != null) { try { PropertyUtils.setProperty(this, attName, att.getValue()); } catch (Exception e) { throw new RuntimeException(e); } } } } /** * 解析不可分的Element * * @param rootElement */ protected void parseAtomicElementFromXml(Element rootElement) { Element xmlElementParent = getXmlElementParent(rootElement); if (xmlElementParent == null) { return; } List xmlElements = this.getXmlAtomicElements(); for (int i = 0; i < xmlElements.size(); i++) { String attName = (String) xmlElements.get(i); Element elmt = XMLElementParent.element(attName); if (elmt != null) { 
try { PropertyUtils.setProperty(this, attName, elmt.getText()); } catch (Exception e) { throw new RuntimeException(e); } } } } /** * 解析BusiNode屬性 * * @param rootElement */ protected void parseBusiNodeElementFromXml(Element rootElement) { Element xmlElementParent = getXmlElementParent(rootElement); if (xmlElementParent == null) { return; } // 再解析BusiNode屬性 List busiNodePropNames = this.getBusiNodePropNames(); for (int i = 0; i < busiNodePropNames.size(); i++) { try { String attName = (String) busiNodePropNames.get(i); Element elmt = xmlElementParent.element(attName); if (elmt != null) { Field field = this.getClass().getDeclaredField(attName); BusiNode att = (BusiNode) fIEld.getType().newInstance(); att.parseFromXML(elmt); PropertyUtils.setProperty(this, attName, att);
} } catch (Exception e) { throw new RuntimeException(e); } } } /** * 解析類集屬性 * * @param rootElement */ protected void parseCollectionPropsFromXml(Element rootElement) { // 先解析XML屬性 Element xmlElementParent = getXmlElementParent(rootElement); if (xmlElementParent == null) { return; } // 最後解析類集屬性 Map collectionPropsMap = this.getCollectionPropsMap(); for (Iterator it = collectionPropsMap.keySet().iterator(); it.hasNext();) { try { String attName = (String) it.next(); Collection coll = (Collection) PropertyUtils.getProperty(this, attName); Class attType = (Class) collectionPropsMap.get(attName); Iterator collElementsIt = this.getCollectionElementIterator( xmlElementParent, attName); // XMLElementParent.elementIterator(attName); while (collElementsIt.hasNext()) { Element collElmt = (Element) collElementsIt.next(); 
BusiNode sinlgeAtt = (BusiNode) attType.newInstance(); sinlgeAtt.parseFromXML(collElmt); coll.add(sinlgeAtt); } } catch (Exception e) { throw new RuntimeException(e); } } } /** * 從XML中解析出結點。此方法可能拋出RumtimeException */ public void parseFromXML(Element rootElement) { this.parseAttributesFromXml(rootElement); this.parseAtomicElementFromXml(rootElement); this.parseBusiNodeElementFromXml(rootElement); this.parseCollectionPropsFromXML(rootElement); } } /** * 入庫 * JdbcUtil,MyUtils的代碼欠奉 * */ public class BusiNodeDAO { private Long saveBusiNode(BusiNode node, Long parentNodeId) { // 先存儲原子屬性 Long id = saveBareBusiNode(node, parentNodeId); // 再存儲類集屬性 Map collectionPropsMap = node.getCollectionPropsMap(); for (Iterator it = collectionPropsMap.keySet().iterator(); it.hasNext();) { String attName = (String) it.next(); Collection coll = null; 
try { coll = (Collection) PropertyUtils.getProperty(node, attName); } catch (Exception e) { throw new RuntimeException("編碼錯誤"); } for (Iterator iitt = coll.iterator(); iitt.hasNext();) { BusiNode subNode = (BusiNode) iitt.next(); saveBusiNode(subNode, id); } } // 最後存儲所有BusiNode屬性 Iterator iitt = node.getBusiNodePropNames().iterator(); while (iitt.hasNext()) { BusiNode subNode = null; try { subNode = (BusiNode) PropertyUtils.getProperty(node, (String) iitt.next()); } catch (Exception e) { throw new RuntimeException("編碼錯誤"); } if (subNode != null) { saveBusiNode(subNode, id); } } return id; } /** * 插入某個BusiNode的根結點,此方法可能拋出RuntimeException * * @param node * @return */ private Long saveBareBusiNode(BusiNode node, Long parentNodeId) { 
StringBuffer sbForSql = new StringBuffer(); List paramValues = new ArrayList(); genInsertSqlAndParam(node, parentNodeId, node.getAtomicPropNames(), sbForSql, paramValues); return new Long(JdbcUtil.queryForLong( sbForSql.toString(), paramValues.toArray())); } /** * 生成某個結點的插入語句和paramValues數組,此方法可能拋出RuntimeException * * @param node * @param columnNames * @param sbForSql * @param paramValues */ private void genInsertSqlAndParam(BusiNode node, Long parentNodeId, List columnNames, StringBuffer sbForSql, List paramValues) { sbForSql.append(" insert into "); sbForSql.append(MyUtils.getClassBareName(node.getClass())); List cns = new ArrayList(); cns.addAll(columnNames); cns.add("parentNodeId"); sbForSql.append(MyUtils.encloseWithCurve(MyUtils .joinCollectionStrings(cns, ","))); sbForSql.append(" values "); List qms = new ArrayList(); // 問號 for (Iterator it = columnNames.iterator(); it.hasNext();) {
qms.add("?"); String cn = (String) it.next(); try { paramValues.add(PropertyUtils.getProperty(node, cn)); } catch (Exception e) { throw new RuntimeException(e); } } qms.add("?"); // parentNodeId paramValues.add(parentNodeId); sbForSql.append(MyUtils.encloseWithCurve(MyUtil .joinCollectionStrings(qms, ","))); sbForSql.append(";select @@identity"); } }
XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved