<?xml version='1.0' encoding="UTF-8"?>
<!--
  ~ Hibernate, Relational Persistence for Idiomatic Java
  ~
  ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
  ~ indicated by the @author tags or express copyright attribution
  ~ statements applied by the authors.  All third-party contributions are
  ~ distributed under license by Red Hat Middleware LLC.
  ~
  ~ This copyrighted material is made available to anyone wishing to use, modify,
  ~ copy, or redistribute it subject to the terms and conditions of the GNU
  ~ Lesser General Public License, as published by the Free Software Foundation.
  ~
  ~ This program is distributed in the hope that it will be useful,
  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
  ~ for more details.
  ~
  ~ You should have received a copy of the GNU Lesser General Public License
  ~ along with this distribution; if not, write to:
  ~ Free Software Foundation, Inc.
  ~ 51 Franklin Street, Fifth Floor
  ~ Boston, MA  02110-1301  USA
  -->

<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "../HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.ent">
%BOOK_ENTITIES;

]>

<chapter id="xml">
    <title>XML Mapping</title>

    <para><emphasis>
        XML Mapping is an experimental feature in Hibernate 3.0 and is currently under
        active development.
    </emphasis></para>

    <section id="xml-intro" revision="1">
        <title>Working with XML data</title>

        <para>
            Hibernate allows you to work with persistent XML data in much the same way
            you work with persistent POJOs. A parsed XML tree can be thought of
            as another way of representing 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 can 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 can be used to map just the XML.
        </para>
        
        <section 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 role="XML"><![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>
        </section>
        
        <section id="xml-onlyxml">
            <title>Specifying only an XML mapping</title>

            <para>
                Here is an example where there is no POJO class:
            </para>
            
            <programlisting role="XML"><![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 or java <literal>Map</literal>s. The property names
                are purely logical constructs that can be referred to in HQL queries.
            </para>

        </section>
        
     </section>
     
    <section id="xml-mapping" revision="1">
        <title>XML mapping metadata</title>

        <para>
            A range of Hibernate mapping elements accept the <literal>node</literal> attribute.
            This lets 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 not appear at all.
        </para>
        
        <para>
            Do not leave <literal>embed-xml="true"</literal> for
            too many associations, since XML does not deal well with circularity.
        </para>
        
        <programlisting role="XML"><![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, the collection of account ids is embedded, 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 role="XML"><![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>&lt;one-to-many&gt;</literal>
            mapping, the data might look more like this:
        </para>
        
        <programlisting role="XML"><![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>
       
    </section>
    
    
    <section id="xml-manipulation" revision="1">
        <title>Manipulating XML data</title>
        
        <para>
            You can also re-read and update XML documents in the application. You can do this by
            obtaining a dom4j session:
        </para>
        
       <programlisting role="JAVA"><![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 role="JAVA"><![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>
            When implementing XML-based data import/export, it is useful to combine this feature with Hibernate's <literal>replicate()</literal>
            operation.
        </para>
       
    </section>
     
</chapter>

