TP Mappings XML - Relationnel - Objet

Ce TP se déroule sur deux séances. Il a pour objectif d'établir les liens entre les modèles semi-structurés (XML), relationnels (SGBDR: Oracle / PostgreSQL) et objet (Java).

Rendu

Le programme (projet maven + script SQL commenté dans un fichier zip) est à rendre pour le jeudi 12 janvier 2012 par mail à Haytham Elghazel avec pour sujet [BDAV-TP3] Rendu.

Remarque: lorsque l'énoncé demande de modifier une requête, dont donnera le code de chaque étape, pas seulement de la requête finale.

Mapping XML - Relationnel

Dans cette partie on mettra en œuvre la génération de documents XML en SQL. Cette partie peut être réalisée sur la base Oracle ou sur une base PostgreSQL, au choix. Les fonctions de génération de XML sont presque les mêmes sur les deux système. Pour la version Oracle, on s'appuiera sur le schéma fourni au TP1, pour PostgreSQL sur sa traduction fournie au TP2.

XMLElement

La fonction XMLElement permet de créer un élément XML. L'expression suivante permet de créer un élément nommé prix contenant un noeud texte dont la valeur est donnée par l'attribut prix_vente

XMLElement(name "prix", prix_vente)

On peut en particulier l'utiliser dans une requête:

SELECT XMLElement(name "prix", prix_vente)
FROM ventes

Il est possible de spécifier plusieurs enfants pour un élément en les séparant par des virgules. Il est également possible d'imbriquer les appels à XMLElement afin de construire un morceau de document XML plus complexe.

→ Écrire une requête qui pour chaque partie génère un élément XML ”partie” avec un élément ”titre” incluant le titre de la partie et un élément ”contenu” incluant le contenu de la partie.

XMLAttributes

La fonction XMLAttributes permet d'ajouter des attributs à un élément. Le nom par défaut utilisé pour chaque attribut XML est le nom de l'attribut SQL dont on a pris la valeur. Il est également possible d'utiliser la notation AS un_nom afin de changer le nom de l'attribut XML (de manière similaire au nom des colonnes dans un SELECT). L'exemple suivant crée des éléments “article” avec un attribut XMLident” donné par l'attribut SQL ident, un attribut XMLprix” donné par l'attribut SQL prix_vente et un contenu texte donné par l'attribut SQL description:

XMLElement(name "article", 
           XMLAttributes(ident,prix_vente AS "prix"),
           description)

→ Modifier la requête précédente afin d'ajouter un attribut ”id” contenant l'identifiant de la partie. On fera bien attention à la casse des éléments/attributs1).

XMLForest

La fonction XMLForest crée pour chacun de ses arguments un élément (dont on peut optionnellement préciser le nom avec un AS) qui contient du texte correspondant à la valeur calculée pour cet argument. Par exemple:

XMLElement(name "article", 
           XMLAttributes(ident,prix_vente AS "prix"),
           XMLForest(nom_article AS "nom",description)

→ Modifier la requête précédente en utilisant cette fonction plutôt que des XMLElement imbriqués dans le XMLElement principal.

→ Que peut-on remarquer sur les parties n'ayant pas de titre ?

XMLAgg

La fonction XMLAgg est une fonction d'agrégation pour le type XML (donc typiquement à utiliser en conjonction avec un GROUP BY). Son effet est de mettre les unes à la suite des autres les différentes valeurs de l'expression passée en argument. La requête suivante illustre son fonctionnement:

SELECT XMLElement(name "departement", 
                  XMLAttributes(deptno),
                  XMLAgg(XMLElement(name "employe",ename))) AS RESULTAT
FROM scott.emp
GROUP BY deptno;

→ Modifier la requête précédente pour ajouter à chaque élément partie des élémentsauteur” pour chaque auteur. Cet élément contiendra avec un attribut “ref” dont la valeur sera l'identifiant de l'auteur concerné. On supposera que toute partie a au moins un auteur.

→ Modifier à nouveau la requête pour que le contenu des éléments auteur soit du texte contenant le nom de l'auteur et pour remplacer l'attribut ref par un nouvel attribut “email” ayant la valeur adéquate.

Vues pour les sous-parties

→ Créer un vue associant à chaque identifiant de partie la concaténation (via XMLAgg) d'un élément par partie incluse (directement) dans la partie concernée. Ces éléments seront nommés “sous-partie” et contiendront seulement un attribut un attribut “ref” contenant l'identifiant de la partie concernée.

→ Créer suivant la même idée une vue pour créer la liste des auteurs.

→ Utiliser ces deux vues pour ajouter dans la requête de génération de XML pour les parties la liste des sous-parties. Il se peut que les parties n'ayant pas de sous parties n'apparaissent pas dans le résultat. On pourra alors utiliser un LEFT2) OUTER JOIN pour les récupérer.

→ Créer une vue en utilisant cette requête (penser à ajouter un attribut SQL id pour pouvoir requêter cette vue), puis créer une vue qui à chaque livre (id) associe un élément “livre” ayant un élément titre, ainsi qu'un élément “editeur” construit sur le modèle des auteurs de parties. Cet élément livre contiendra également l'ensemble des parties composant ce livre3).

→ Donner la DTD correspondant aux documents XML produits par les vues xml sur les parties et les livres (les deux dernières vues).

Récupération des données XML en Java

Le type Java java.sql.SQLXML permet de représenter une donnée XML renvoyée dans un attribut du résultat d'une requête JDBC. Le code suivant montre comment récupérer une javax.xml.transform.Source depuis une résultat d'une requête:

ResultSet rs = ...
while (rs.next()) {
  SQLXML xmldata = rs.getSQLXML(...);
  javax.xml.transform.dom.DOMSource domSource = xmldata.getSource(DOMSource.class);
  Document document = (Document) domSource.getNode();
  // utilisation du document
}

Pour récupérer une javax.xml.transform.sax.SAXSource, il suffit de remplacer DOMSource.class par SAXSource.class.

→ Reprendre projet du TP JDBC. Dans la classe LivreDAO, ajouter une méthode getLivreXML qui utilise la vue précédement crée pour fournir une représentation XML d'un livre dont l'identifiant est fourni en argument:

public javax.xml.transform.Source getLivreXML(long livreId) throws SQLException 

:!: Sous Oracle, le pilote de base ne sais pas bien gérer le type SQLXML, il faut donc s'inspirer du code suivant:

        ResultSet rs = ...
        if (rs.next()) {
            Reader xmldata = rs.getCharacterStream(...);
            return new StreamSource(xmldata);
        } else {
            return null;
        }

et ajouter dans la requête l'utilisation de getClobVal() comme suit (attention aux parenthèses):

SELECT (mon_xml).getClobVal() AS mon_xml, ...
FROM ...

Ajout de données XML dans une base relationnelle

Fonction de création de livre

→ Créer un déclencheur PL/SQL pour numéroter les livres. Ajouter une fonction PL/SQL de création de livre construite sur le principe de la fonction de création de partie. Ajouter à la classe LivreDAO une méthode correspondante à la fonction précédente.

:!: Avec Oracle, mieux vaut utiliser des procédures et des CallableStatements, c.f. http://java.developpez.com/faq/jdbc/?page=callablestatement :!:

→ Procéder de la même manière pour créer une méthode d'ajout de membre.

→ Créer une méthode qui permet de changer le nom et l'email d'un membre via une requête UPDATE.

Insertion de données XML via SAX

Relire les transparents sur les APIs XML.

On souhaite dans cette partie pouvoir importer des documents conformes à la DTD du TP1.

→ Dans le package epul.bdav, créer une classe LivreImportHandler qui étend org.xml.sax.helpers.DefaultHandler. Elle possèdera un champ dao de type LivreDAO initialisé via un argument du constructeur.

Cette classe sera utilisée en conjonction avec un parser SAX qui appellera des méthodes telles que startDocument, startElement, endElement, etc lors de la lecture d'un fichier XML. Elle devra insérer les données du document dans la base relationnelle. Pour cela il faudra retenir des informations via des champs de la classe. Il faudra en particulier traiter:

  • les livres/parties sans identifiant
  • les identifiants de membre
  • les liens implicites partie ↔ partie et partie ↔ livre.

Il sera nécessaire de garder des informations de contexte dans des champs, par exemple le texte contenu dans le dernier élément titre qui a été lu, l'identifiant de la salle courante, etc.

Les exceptions SQLException seront rattrapées et relancées via:

try {
...
} catch (SQLException ex) {
throw new SAXException(ex);
}

:!: Les identifiants du documents servent uniquement en interne, on supposera que tous les identifiants des tuples de la base sont générés. Il peut ainsi être nécessaire de maintenir une correspondance id xml → id sql4). :!:

:!: Les données sur les membres venant après les données sur les livres, il sera nécessaire de mettre à jour leurs informations après les avoir créés avec des informations par défaut (nom et email vides). :!:

:!: Les parties pouvant être imbriquées les unes dans les autres, il sera nécessaire d'utiliser une pile5) pour connaître la liste des parties ancêtres de la partie courante. On pourra mettre dans un premier temps au point l'import sans partie imbriquées :!:

→ Créer une méthode importXML dans la classe LivreDAO prenant en argument une javax.xml.transform.Source et mettant en oeuvre le code suivant pour utiliser le LivreImportHandler:

javax.xml.transform.Transformer copy = javax.xml.transform.TransformerFactory.newInstance().newTransformer();
copy.transform(la_source, new javax.xml.transform.sax.SAXResult(new LivreImportHandler(this)));

Mappings Relationnel - Objet

Mise à jour du projet

Ajouter les dépendances vers Hibernate, une implémentation de l'api JPA. Pour cela, ajouter au bon endroit dans le fichier pom.xml6):

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.6.9.Final</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.6.1</version>
            <scope>runtime</scope>
        </dependency>

Ajouter un fichier src/main/resources/META-INF/persistence.xml ayant le contenu suivant:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="bdav_pu_postgres" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.connection.username" value="etudiant"/>
      <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
      <property name="hibernate.connection.password" value="etudiant"/>
      <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/postgres"/>
      <property name="hibernate.hbm2ddl.auto" value="validate"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
    </properties>
  </persistence-unit>
  <persistence-unit name="bdav_pu_oracle" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.connection.username" value="EPU2AXX"/>
      <property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver"/>
      <property name="hibernate.connection.password" value="EPU2AXX"/>
      <property name="hibernate.connection.url" value="jdbc:oracle:thin:@pedagowin710:1521:orapeda1"/>
      <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
      <property name="hibernate.hbm2ddl.auto" value="validate"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/>
    </properties>
  </persistence-unit>
</persistence>

Ajouter les deux classes suivantes dans les Sources Packages:

Livre

package epul.bdav.modele;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
 
/**
 *
 * @author ecoquery
 */
@Entity
public class Livre {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "LIVRE_GEN")
    @SequenceGenerator(name = "LIVRE_GEN", sequenceName = "livre_seq", allocationSize=1) 
    private int id;
    @Column(name="titre")
    private String titre;
    @ManyToOne
    @JoinColumn(name = "editeur")
    private Membre editeur;
 
    public int getId() {
        return id;
    }
 
    public String getTitre() {
        return titre;
    }
 
    public void setTitre(String titre) {
        this.titre = titre;
    }
 
    public Membre getEditeur() {
        return editeur;
    }
 
    public void setEditeur(Membre editeur) {
        this.editeur = editeur;
    }
}

Membre

package epul.bdav.modele;
 
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
 
/**
 *
 * @author ecoquery
 */
@Entity
public class Membre {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBRE_GEN")
    @SequenceGenerator(name = "MEMBRE_GEN", sequenceName = "livre_seq", allocationSize=1) 
    private int id;
    @OneToMany(mappedBy="editeur")
    private Collection<Livre> livres = new ArrayList<Livre>();
 
}

Ainsi que la classe de test suivante, cette fois-ci dans les Test Packages

package epul.bdav;
 
import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import junit.framework.TestCase;
 
/**
 *
 * @author ecoquery
 */
public class JPATest extends TestCase {
 
    public void testOracle() {
        EntityManager em = Persistence.createEntityManagerFactory("bdav_pu_oracle").createEntityManager();
        em.close();
    }
 
    public void testPostgreSQL() {
        EntityManager em = Persistence.createEntityManagerFactory("bdav_pu_postgres").createEntityManager();
        em.close();
    }
 
}

→ Commenter le test inutilisé7) et modifier éventuellement le fichier persistence.xml en changeant le login et le mot de passe le cas échéant. Vérifier que les tests se déroulent bien8).

Première modifications

→ Regarder la javadoc pour chaque annotation des classes Livre et Membre. Ajouter un commentaire décrvant brièvement l'effet de chaque annotation.

→ Ajouter les accesseurs utiles pour les champs de la classe Membre. Comme id a sa valeur générée, on aura pas de setter dessus. Ajouter les informations manquantes dans la classe Membre (nom et email).

→ La cohérence des champs Livre.editeur et Membre.livres n'est actuellement pas garantie. Modifier/ajouter les méthodes nécessaires pour encapsuler ces champs et garantir cette cohérence.

:!: L'ORM va injecter sa propre implémentation de Collection9), il ne faut donc pas créer de nouvelle instance pour le champ livre. Le seul moment où l'on affecte directement la collection est lors de l'initialisation par défaut. Dans la même idée on ne mettra pas de setter sur livres, l'ORM étant capable d'affecter au besoin directement un champ, même si celui-ci est privé.

DAO, version ORM

→ Créer une classe epul.bdav.LivreDAOORM. Elle contiendra un champ EntityManager em, initialisé via son constructeur. Elle devra passer avec succès le test unitaire suivant:

package epul.bdav;
 
import epul.bdav.modele.Livre;
import epul.bdav.modele.Membre;
import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import junit.framework.TestCase;
 
/**
 *
 * @author ecoquery
 */
public class LivreDAOORMTest extends TestCase {
 
    private EntityManager em;
 
    protected EntityManager createEntityManager() {
        return Persistence.createEntityManagerFactory("bdav_pu_oracle").createEntityManager();
    }
 
    @Override
    protected void setUp() throws Exception {
        if (em == null) {
            em = createEntityManager();
        }
        em.getTransaction().begin();
    }
 
    @Override
    protected void tearDown() throws Exception {
        em.getTransaction().rollback();
    }
 
    public void testOperationsVariees() {
        LivreDAOORM dao = new LivreDAOORM(em);
        Membre m = dao.createMembre("Sting", "sting@police.com");
        assertNotNull(m);
        Membre m2 = dao.getMembre(m.getId());
        assertEquals(m,m2);
        assertTrue(m==m2);
        Livre l = dao.createLivre("Synchronicity", m);
        assertNotNull(l);
        Livre l2 = dao.getLivre(l.getId());
        assertEquals(l,l2);
        assertTrue(l==l2);
        assertEquals(1,m.getLivres().size()); 
        // adapter éventuellement l'instruction précédente suivant le niveau 
        // d'encapsulation du champ livres de la classe Membre
        int lId = l.getId();
        dao.supprimeLivre(lId);
        assertNull(dao.getLivre(lId));
        int mId = m.getId();
        dao.supprimeMembre(mId);
        assertNull(dao.getMembre(mId));
        m = dao.createMembre("Peter Gabriel", "peter@secretworld.org");
        l = dao.createLivre("Live", m);
        mId = m.getId();
        lId = l.getId();
        dao.supprimeLivre(l);
        dao.supprimeMembre(m);
        assertNull(dao.getLivre(lId));
        assertNull(dao.getMembre(mId));
    }
 
}

Parties

→ Créer une classe epul.bdav.modele.Partie pour représenter les parties de livre. Annoter cette classe pour la persistance. On fera particulièrement attention aux points suivants:

  • La gestion de l'identifiant est a faire via la séquence livre_seq.
  • Les associations correspondant aux auteurs et aux sous-parties doivent être prises en compte.
  • On s'assurera de la cohérence entre les différents champs représentant ces associations.
  • On ajoutera les méthodes adéquates à LivreDAOORM.

Remarque: il est possible de changer la propriété hibernate.hbm2ddl.auto du fichier persistence.xml en update, ce qui autorise le framework à changer le schéma. Cela peut être utile afin de comprendre ce qui correspond aux annotations. Cependant votre mapping devra correspondre au schéma de l'énoncé du TP (i.e. le mapping doit fonctionner en validate avec le schéma relationnel de départ).

Mappings XML - Objet

Cette partie a pour but d'introduire les mappings XML - Objets par annotation des classes (API JAXB)

Mise à jour du projet: annotations XML

Modifier les classes Membre et Livre en intégrant les modifications dans le code donné ci-dessous. Dans un premier temps, ajouter une annotation @XmlTransient sur les champs et les méthodes getXXX que vous avez ajouté dans ces classes.

Membre

package epul.bdav.modele;
 
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlTransient;
 
/**
 *
 * @author ecoquery
 */
@Entity
public class Membre {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBRE_GEN")
    @SequenceGenerator(name = "MEMBRE_GEN", sequenceName = "livre_seq", allocationSize=1) 
    @XmlTransient // l'id sera transformé en String
    private int id;
    @OneToMany(mappedBy="editeur")
    @XmlTransient
    private Collection<Livre> livres = new ArrayList<Livre>();
 
    @XmlTransient   
    public int getId() {
        return id;
    }
 
    // Ajouté pour gérer les identifiants hors des ORMs
    public void setId(int id) {
        this.id = id;
    }
 
    @XmlTransient
    public Collection<Livre> getLivres() {
        return livres;
    }
 
    // Id sous forme de chaine de caractères
    @XmlAttribute(name="id")
    @XmlID
    private String getStringId() {
        return String.valueOf(id);
    }
 
    private void setStringId(String newId) {
        id = Integer.parseInt(newId);
    }
 
}

Livre:

package epul.bdav.modele;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.XmlTransient;
 
/**
 *
 * @author ecoquery
 */
@Entity
// inutile de mettre @XmlRootElement, sauf si on souhaite pouvoir 
// lire/écrire un document contenant uniquement un élément livre
public class Livre {
 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "LIVRE_GEN")
    @SequenceGenerator(name = "LIVRE_GEN", sequenceName = "livre_seq", allocationSize=1) 
    @XmlAttribute
    private int id;
 
    @Column(name="titre")
    @XmlElement(name="titre")
    private String titre;
 
    @ManyToOne
    @JoinColumn(name = "editeur")
    @XmlIDREF
    @XmlAttribute
    private Membre editeur;
 
    @XmlTransient
    public int getId() {
        return id;
    }
 
    // Ajouté pour gérer les identifiants hors des ORMs
    public void setId(int id) {
        this.id = id;
    }
 
    @XmlTransient
    public String getTitre() {
        return titre;
    }
 
    public void setTitre(String titre) {
        this.titre = titre;
    }
 
    @XmlTransient
    public Membre getEditeur() {
        return editeur;
    }
 
    public void setEditeur(Membre editeur) {
        this.editeur = editeur;
    }
}

Créer une classe epul.bdav.modele.ListeLivres avec le code suivant:

package epul.bdav.modele;
 
import java.util.ArrayList;
import java.util.Collection;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
 
/**
 *
 * @author ecoquery
 */
@XmlRootElement(name="livres")
public class ListeLivres {
 
    @XmlElement(name="livre")
    private Collection<Livre> livres = new ArrayList<Livre>();
 
    @XmlElement(name="membre")
    private Collection<Membre> membres = new ArrayList<Membre>();
 
    @XmlTransient // nécessaire car JAXB différencie les accesseurs et les champs
    public Collection<Livre> getLivres() {
        return livres;
    }
 
    public void setLivres(Collection<Livre> livres) {
        this.livres = livres;
    }
 
    @XmlTransient // nécessaire car JAXB différencie les accesseurs et les champs
    public Collection<Membre> getMembres() {
        return membres;
    }
 
    public void setMembres(Collection<Membre> membres) {
        this.membres = membres;
    }
 
 
}

Un exemple d'utilisation est donné dans le test unitaire suivant:

package epul.bdav;
 
import epul.bdav.modele.ListeLivres;
import epul.bdav.modele.Livre;
import epul.bdav.modele.Membre;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import junit.framework.TestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
 *
 * @author ecoquery
 */
public class JAXBTest extends TestCase {
 
    private static Logger log = LoggerFactory.getLogger(JAXBTest.class);
 
    public void testGenerateAndLoad() throws JAXBException {
        try {
        String filename = "target/surefire/JAXB_testGenerateAndLoad.xml";
        Membre m = new Membre();
        m.setId(1);
        Livre l = new Livre();
        l.setId(1);
        l.setEditeur(m);
        l.setTitre("Un livre intéressant");
        Livre l2 = new Livre();
        l2.setId(2);
        l2.setEditeur(m);
        l2.setTitre("Un deuxième livre intéressant");
        ListeLivres ll = new ListeLivres();
        ll.getLivres().add(l);
        ll.getLivres().add(l2);
        ll.getMembres().add(m);
        // objet pour (dé)sérialiser les objets en XML
        JAXBContext ctx = JAXBContext.newInstance(ListeLivres.class);
        File f = new File(filename);
        File f2 = f.getParentFile();
        if (!f2.isDirectory()) {
            assertTrue(f2.mkdirs());
        }
        ctx.createMarshaller().marshal(ll,f);
        Object o = ctx.createUnmarshaller().unmarshal(f);
        assertNotNull(o);
        assertTrue(o instanceof ListeLivres);
        } catch (JAXBException e) {
            log.error("Erreur JAXB: "+e.getMessage(), e);
            throw e;
        }
    }
}

→ Exécuter ce test et regarder le fichier XML généré.

→ Pour chaque nouvelle annotation, lire la documentation et ajouter un commentaire contenant une brève explication de son effet.

Modifications de la sérialisation XML des classes Membre et Livre

→ Annoter les champs ou les accesseurs indiquant le nom et l'email de l'auteur.

→ Le champ livres de la classe Membre n'est pas correctement reconstitué. Déplacer les annotations du champ editeur sur son accesseur et, au besoin, ajouter le code nécessaire pour mettre à jour au passage le champ livres du membre.

Sérialisation XML des parties

→ Annoter la classe Partie, ainsi que (si nécessaire) les champs/accesseurs correspondants dans les classes Membre et Livre. Au final, on souhaite que le XML généré corresponde à la DTD (et au schéma correspondant) du premier TP.

1)
qui, au contraire de SQL est importante
2)
ou un RIGHT
3)
on suppose qu'un livre contient au moins une partie
4)
via une java.util.Map par exemple
5)
java.util.Stack
6)
qui se trouve dans Project Files
7)
i.e. celui correspondant à postgres si vous utilisez oracle et inversement
8)
cela requiert le schéma fourni, ainsi que la séquence livre_seq
9)
afin de pouvoir être paresseux sur le chargement des livres d'un membre