287 lines
9.4 KiB
XML
Executable File
287 lines
9.4 KiB
XML
Executable File
<chapter id="xml">
|
|
<title>XML Mapping</title>
|
|
|
|
<para><emphasis>
|
|
Note that this is an experimental feature in Hibernate 3.0 and is under
|
|
extremely active development.
|
|
</emphasis></para>
|
|
|
|
<sect1 id="xml-intro" revision="1">
|
|
<title>Working with XML data</title>
|
|
|
|
<para>
|
|
Hibernate lets you work with persistent XML data in much the same way
|
|
you work with persistent POJOs. A parsed XML tree can be thought of
|
|
as just another way to represent the relational data at the object level,
|
|
instead of POJOs.
|
|
</para>
|
|
|
|
<para>
|
|
Hibernate supports dom4j as API for manipulating XML trees. You can write
|
|
queries that retrieve dom4j trees from the database and have any
|
|
modification you make to the tree automatically synchronized to the
|
|
database. You can even take an XML document, parse it using dom4j, and
|
|
write it to the database with any of Hibernate's basic operations:
|
|
<literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
|
|
(merging is not yet supported).
|
|
</para>
|
|
|
|
<para>
|
|
This feature has many applications including data import/export,
|
|
externalization of entity data via JMS or SOAP and XSLT-based reporting.
|
|
</para>
|
|
|
|
<para>
|
|
A single mapping may be used to simultaneously map properties of a class
|
|
and nodes of an XML document to the database, or, if there is no class to map,
|
|
it may be used to map just the XML.
|
|
</para>
|
|
|
|
<sect2 id="xml-intro-mapping">
|
|
<title>Specifying XML and class mapping together</title>
|
|
|
|
<para>
|
|
Here is an example of mapping a POJO and XML simultaneously:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
</sect2>
|
|
|
|
<sect2 id="xml-onlyxml">
|
|
<title>Specifying only an XML mapping</title>
|
|
|
|
<para>
|
|
Here is an example where there is no POJO class:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
This mapping allows you to access the data as a dom4j tree, or as a graph of
|
|
property name/value pairs (java <literal>Map</literal>s). The property names
|
|
are purely logical constructs that may be referred to in HQL queries.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="xml-mapping" revision="1">
|
|
<title>XML mapping metadata</title>
|
|
|
|
<para>
|
|
Many Hibernate mapping elements accept the <literal>node</literal> attribute.
|
|
This let's you specify the name of an XML attribute or element that holds the
|
|
property or entity data. The format of the <literal>node</literal> attribute
|
|
must be one of the following:
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para><literal>"element-name"</literal> - map to the named XML element</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para><literal>"@attribute-name"</literal> - map to the named XML attribute</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para><literal>"."</literal> - map to the parent element</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>"element-name/@attribute-name"</literal> -
|
|
map to the named attribute of the named element
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
For collections and single valued associations, there is an additional
|
|
<literal>embed-xml</literal> attribute. If <literal>embed-xml="true"</literal>,
|
|
the default, the XML tree for the associated entity (or collection of value type)
|
|
will be embedded directly in the XML tree for the entity that owns the association.
|
|
Otherwise, if <literal>embed-xml="false"</literal>, then only the referenced
|
|
identifier value will appear in the XML for single point associations and
|
|
collections will simply not appear at all.
|
|
</para>
|
|
|
|
<para>
|
|
You should be careful not to leave <literal>embed-xml="true"</literal> for
|
|
too many associations, since XML does not deal well with circularity!
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
in this case, we have decided to embed the collection of account ids, but not
|
|
the actual account data. The following HQL query:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
|
|
|
|
<para>
|
|
Would return datasets such as this:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
If you set <literal>embed-xml="true"</literal> on the <literal><one-to-many></literal>
|
|
mapping, the data might look more like this:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
|
|
<sect1 id="xml-manipulation" revision="1">
|
|
<title>Manipulating XML data</title>
|
|
|
|
<para>
|
|
Let's rearead and update XML documents in the application. We do this by
|
|
obtaining a dom4j session:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
<programlisting><![CDATA[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();]]></programlisting>
|
|
|
|
<para>
|
|
It is extremely useful to combine this feature with Hibernate's <literal>replicate()</literal>
|
|
operation to implement XML-based data import/export.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|