面對無 DTD/Schema、結構不規範(如多層巢狀 pig → content → pighead)的 XML,無法使用自動化綁定工具。本文提出基於「多態 + 遞迴」的半自動解析/入庫框架:自動處理共同邏輯,業務差異由具體類別實作。
| 關係類型 | XML | JavaBean | 資料庫 |
|---|---|---|---|
| 原子屬性 (1:1) | Attribute 或單純 Element | String 欄位 | 主表欄位 |
| 複合屬性 (1:1) | 含子元素的 Element | 實作 BusiNode 的單一欄位 | 子表(外鍵指向主表) |
| 複合屬性 (N:1) | 重複出現的 Element | List/Set<BusiNode> | 子表(外鍵指向主表) |
public interface BusiNode {
List<String> getAtomicPropNames(); // 原子屬性名(均為 String)
List<String> getXmlAttributes(); // 對應 XML Attribute 的屬性
List<String> getXmlAtomicElements(); // 對應 XML 原子 Element 的屬性
Map<String, Class> getCollectionPropsMap(); // 集合屬性名 → 元素類別
List<String> getBusiNodePropNames(); // 單一複合屬性名
Element getXmlElementParent(Element root); // 取得子元素父節點(處理多層巢狀)
Iterator<Element> getCollectionElementIterator(Element parent, String name); // 集合元素迭代器
void parseFromXML(Element root); // 遞迴解析進入點
}
利用反射自動發現屬性:
String 型別欄位BusiNode 的欄位Map<屬性名, 元素Class>(Java 1.4 無泛型資訊)解析順序:Attribute → 原子 Element → 單一複合 → 集合複合。持久化順序:主表 → 集合子表 → 單一子表。
// 解析 XML Attribute
protected void parseAttributesFromXml(Element root) {
for (String name : getXmlAttributes()) {
Attribute att = root.attribute(name);
if (att != null) PropertyUtils.setProperty(this, name, att.getValue());
}
}
// 解析集合屬性(遞迴)
protected void parseCollectionPropsFromXml(Element root) {
Element parent = getXmlElementParent(root);
for (Map.Entry<String, Class> entry : getCollectionPropsMap().entrySet()) {
String name = entry.getKey();
Class> type = entry.getValue();
Collection coll = (Collection) PropertyUtils.getProperty(this, name);
for (Element child : getCollectionElementIterator(parent, name)) {
BusiNode node = type.newInstance();
node.parseFromXML(child);
coll.add(node);
}
}
}
// 持久化:先存主記錄取得 ID,再遞迴存子記錄
private Long saveBusiNode(BusiNode node, Long parentId) {
Long id = saveBareNode(node, parentId); // 插入主表,返回自增 ID
// 存集合子節點
for (Map.Entry<String, Class> e : node.getCollectionPropsMap().entrySet()) {
for (BusiNode child : (Collection) PropertyUtils.getProperty(node, e.getKey()))
saveBusiNode(child, id);
}
// 存單一子節點
for (String name : node.getBusiNodePropNames()) {
BusiNode child = (BusiNode) PropertyUtils.getProperty(node, name);
if (child != null) saveBusiNode(child, id);
}
return id;
}
public class Pig implements BusiNode {
private String id, name; // 原子屬性
private PigHead head; // 單一複合
private List<PigFoot> feet; // 集合複合
// 實作介面方法...
public List<String> getXmlAttributes() { return Arrays.asList("id"); }
public List<String> getXmlAtomicElements() { return Arrays.asList("name"); }
public List<String> getBusiNodePropNames() { return Arrays.asList("head"); }
public Map<String, Class> getCollectionPropsMap() {
return Collections.singletonMap("feet", PigFoot.class);
}
public Element getXmlElementParent(Element root) {
return root.element("content") != null ? root.element("content") : root;
}
public Iterator<Element> getCollectionElementIterator(Element parent, String name) {
return parent.elementIterator(name); // 或 parent.element("content").elementIterator(name)
}
}
String/VARCHAR,非業務欄位不可為 String