XMLマッピングはHibernate3.0では試験的な機能であり、非常に活動的に開発中です。
Hibernateでは永続性のPOJOを使って作業するのとほぼ同じようなやり方で、 永続性のXMLデータを使って作業できます。
HibernateはXMLツリーを操作するためのAPIとしてdom4jをサポートしています。 データベースからdom4jのツリーを復元するクエリを書くことができ、 ツリーに対して行った修正は自動的にデータベースと同期されます。 またXMLドキュメントを取得することができ、dom4jを使ってドキュメントをパースし、 Hibernateの任意の基本操作を使ってデータベースへ書き込むことができます。: つまり、persist(), saveOrUpdate(), merge(), delete(), replicate() 操作です(マージはまだサポートしていません)。
データのインポート/エクスポート、 JMSによるエンティティデータの外部化やSOAP、XSLTベースのレポートなど、 この機能には多くの用途があります。
単一のマッピングは、クラスのプロパティとXMLドキュメントのノードを 同時にデータベースへマッピングするために使うことができます。 またマッピングするクラスがなければ、 XMLだけをマッピングするために使うことができます。
これはPOJOとXMLを同時にマッピングする例です。:
<class name="Account"
table="ACCOUNTS"
node="account">
<id name="accountId"
column="ACCOUNT_ID"
node="@id"/>
<many-to-one name="customer"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"/>
<property name="balance"
column="BALANCE"
node="balance"/>
...
</class>これはPOJOクラスがないマッピングの例です。:
<class entity-name="Account"
table="ACCOUNTS"
node="account">
<id name="id"
column="ACCOUNT_ID"
node="@id"
type="string"/>
<many-to-one name="customerId"
column="CUSTOMER_ID"
node="customer/@id"
embed-xml="false"
entity-name="Customer"/>
<property name="balance"
column="BALANCE"
node="balance"
type="big_decimal"/>
...
</class>このマッピングにより、dom4jツリーか、 プロパティ名/値の組のグラフ(javaの Map)として データにアクセスできます。 プロパティの名前は、HQLクエリー内で参照できる純粋な論理構造です。
多くのHibernateのマッピング要素は node 属性が使用できます。 これによりXML属性の名前やプロパティやエンティティデータを保持する要素を指定できます。 node 属性のフォーマットは以下の中の1つでなければなりません。:
"element-name" - 指定したXML要素へマッピングします
"@attribute-name" - 指定したXML属性へマッピングします
"." - 親要素へマッピングします
"element-name/@attribute-name" - 指定したエレメントの指定した属性へマッピングします
コレクションと単一の値の関連に対して、 おまけの embed-xml 属性があります。 デフォルトの embed-xml="true" と設定した場合、 関連するエンティティ(値型のコレクション)のXMLツリーは、 直接関連を所有するエンティティのXMLツリー内に埋め込まれます。 反対に、embed-xml="false" と設定した場合、 参照される識別子の値だけが多重度1側の関連に対するXMLに現れ、 単純にコレクションはまったく現れなくなります。
あまりに多くの関連に対して embed-xml="true" としたままにするのは注意すべきです。 XMLは循環をうまく扱えません。
<class name="Customer"
table="CUSTOMER"
node="customer">
<id name="id"
column="CUST_ID"
node="@id"/>
<map name="accounts"
node="."
embed-xml="true">
<key column="CUSTOMER_ID"
not-null="true"/>
<map-key column="SHORT_DESC"
node="@short-desc"
type="string"/>
<one-to-many entity-name="Account"
embed-xml="false"
node="account"/>
</map>
<component name="name"
node="name">
<property name="firstName"
node="first-name"/>
<property name="initial"
node="initial"/>
<property name="lastName"
node="last-name"/>
</component>
...
</class>この例では、実際のaccountのデータではなく、 accountのidのコレクションを埋め込むことにしました。 続きのHQLクエリです:
from Customer c left join fetch c.accounts where c.lastName like :lastName
このようなデータセットを返すでしょう
<customer id="123456789">
<account short-desc="Savings">987632567</account>
<account short-desc="Credit Card">985612323</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer><one-to-many> マッピングで embed-xml="true" と設定した場合、 データはこのようになるでしょう。
<customer id="123456789">
<account id="987632567" short-desc="Savings">
<customer id="123456789"/>
<balance>100.29</balance>
</account>
<account id="985612323" short-desc="Credit Card">
<customer id="123456789"/>
<balance>-2370.34</balance>
</account>
<name>
<first-name>Gavin</first-name>
<initial>A</initial>
<last-name>King</last-name>
</name>
...
</customer>XMLドキュメントを、アプリケーション内で再読み込みや更新をしてみましょう。 以下ではdom4jのセッションを取得することで行います。:
Document doc = ....;
Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
List results = dom4jSession
.createQuery("from Customer c left join fetch c.accounts where c.lastName like :lastName")
.list();
for ( int i=0; i<results.size(); i++ ) {
//add the customer data to the XML document
Element customer = (Element) results.get(i);
doc.add(customer);
}
tx.commit();
session.close();Session session = factory.openSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Transaction tx = session.beginTransaction();
Element cust = (Element) dom4jSession.get("Customer", customerId);
for ( int i=0; i<results.size(); i++ ) {
Element customer = (Element) results.get(i);
//change the customer name in the XML and database
Element name = customer.element("name");
name.element("first-name").setText(firstName);
name.element("initial").setText(initial);
name.element("last-name").setText(lastName);
}
tx.commit();
session.close();XMLベースのデータのインポート/エクスポートを実装するために、 Hibernateの replicate() 操作をこの機能を結びつけるのは 極めて有効です。