293 lines
9.7 KiB
XML
293 lines
9.7 KiB
XML
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
<chapter id="xml">
|
|
<title>Mapping XML</title>
|
|
|
|
<para><emphasis>
|
|
Notez que cette fonctionnalité est expérimentale dans Hibernate 3.0 et
|
|
est en développement extrêmement actif.
|
|
</emphasis></para>
|
|
|
|
<sect1 id="xml-intro" revision="1">
|
|
<title>Travailler avec des données XML</title>
|
|
|
|
<para>
|
|
Hibernate vous laisse travailler avec des données XML persistantes de la
|
|
même manière que vous travaillez avec des POJOs persistants. Un arbre XML
|
|
peut être vu comme une autre manière de représenter les données relationnelles
|
|
au niveau objet, à la place des POJOs.
|
|
</para>
|
|
|
|
<para>
|
|
Hibernate supporte dom4j en tant qu'API pour la manipulation des arbres XML.
|
|
Vous pouvez écrire des requêtes qui récupèrent des arbres dom4j à partie de la
|
|
base de données, et avoir toutes les modifications que vous faites sur l'arbre
|
|
automatiquement synchronisées dans la base de données. Vous pouvez même prendre
|
|
un document XML, l'analyser en utilisant dom4j, et l'écrire dans la base de
|
|
données via les opérations basiques d'Hibernate :
|
|
<literal>persist(), saveOrUpdate(), merge(), delete(), replicate()</literal>
|
|
(merge() n'est pas encore supporté).
|
|
</para>
|
|
|
|
<para>
|
|
Cette fonctionnalité a plusieurs applications dont l'import/export de données,
|
|
l'externalisation d'entités via JMS ou SOAP et les rapports XSLT.
|
|
</para>
|
|
|
|
<para>
|
|
Un simple mapping peut être utilisé pour simultanément mapper les propriétés
|
|
d'une classe et les noeuds d'un document XML vers la base de données, ou,
|
|
si il n'y a pas de classe à mapper, il peut être utilisé juste pour mapper
|
|
le XML.
|
|
</para>
|
|
|
|
<sect2 id="xml-intro-mapping">
|
|
<title>Spécifier le mapping XML et le mapping d'une classe ensemble</title>
|
|
|
|
<para>
|
|
Voici un exemple de mapping d'un POJO et du XML simultanément :
|
|
</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>Spécifier seulement un mapping XML</title>
|
|
|
|
<para>
|
|
Voici un exemple dans lequel il n'y a pas de class POJO :
|
|
</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>
|
|
Ce mapping vous permet d'accéder aux données comme un arbre dom4j, ou comme
|
|
un graphe de paire nom de propriété/valeur (<literal>Map</literal>s java). Les
|
|
noms des propriétés sont des constructions purement logiques qui peuvent être
|
|
référées des dans requêtes HQL.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="xml-mapping" revision="1">
|
|
<title>Métadonnées du mapping XML</title>
|
|
|
|
<para>
|
|
Plusieurs éléments du mapping Hibernate acceptent l'attribut <literal>node</literal>.
|
|
Ceci vous permet de spécifier le nom d'un attribut XML ou d'un élément qui
|
|
contient la propriété ou les données de l'entité. Le format de l'attribut
|
|
<literal>node</literal> doit être un des suivants :
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para><literal>"element-name"</literal> - mappe vers l'élément XML nommé</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para><literal>"@attribute-name"</literal> - mappe vers l'attribut XML nommé</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para><literal>"."</literal> - mappe vers le parent de l'élément</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
<literal>"element-name/@attribute-name"</literal> -
|
|
mappe vers l'élément nommé de l'attribut nommé
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
Pour des collections et de simples associations valuées, il y a un attribut
|
|
<literal>embed-xml</literal> supplémentaire. Si <literal>embed-xml="true"</literal>,
|
|
qui est la valeur par défaut, l'arbre XML pour l'entité associée (ou la collection
|
|
des types de valeurs) sera embarquée directement dans l'arbre XML pour l'entité qui
|
|
possède l'association. Sinon, si <literal>embed-xml="false"</literal>, alors
|
|
seule la valeur de l'identifiant référencé apparaîtra dans le XML pour de simples
|
|
associations de points, et les collections n'appraîtront simplement pas.
|
|
</para>
|
|
|
|
<para>
|
|
Vous devriez faire attention à ne pas laisser <literal>embed-xml="true"</literal>
|
|
pour trop d'associations, puisque XML ne traite pas bien les liens circurlaires.
|
|
</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>
|
|
dans ce cas, nous avons décidé d'embarquer la collection d'identifiants de compte,
|
|
mais pas les données actuelles du compte. La requête HQL suivante :
|
|
</para>
|
|
|
|
<programlisting><![CDATA[from Customer c left join fetch c.accounts where c.lastName like :lastName]]></programlisting>
|
|
|
|
<para>
|
|
devrait retourner l'ensemble de données suivant :
|
|
</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>
|
|
Si vous positionnez <literal>embed-xml="true"</literal> sur le mapping
|
|
<literal><one-to-many></literal>, les données pourraient
|
|
ressembler plus à ça :
|
|
</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>Manipuler des données XML</title>
|
|
|
|
<para>
|
|
Relisons et mettons à jour des documents XML dans l'application. Nous faisons
|
|
ça en obtenant une session dom4j :
|
|
</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>
|
|
Il est extrêmement utile de combiner cette fonctionnalité avec l'opération
|
|
<literal>replicate()</literal> d'Hibernate pour implémenter des imports/exports
|
|
de données XML.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|