IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java EE Discussion :

[JEE 5] JPA OneToMany + EJB3 Stateless + WS = Casse tête


Sujet :

Java EE

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 10
    Points : 6
    Points
    6
    Par défaut [JEE 5] JPA OneToMany + EJB3 Stateless + WS = Casse tête
    Bonjour à tous,
    Je souhaiterais avoir votre avis sur le problème suivant :

    A modéliser : tâches ayant plusieurs sous tâches et une tâche parente.

    JPA :
    Une première classe sert uniquement pour l'id.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public class BaseEntity {
    	private Integer id;
    }
    Je ne mets pas volontairement les getters et les setters pour la lisibilité.

    Une tâche :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public class Task extends BaseEntity {
    	private Date time;
    	private String name;
    	private BaseEntity parent;
    	private Set<BaseEntity> enfants;
    }
    La config :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    [...]
    	<entity class="BaseEntity">
    		<attributes>
    			<id name="id">
    				<generated-value strategy="IDENTITY" />
    			</id>
    			<transient name="new" />
    		</attributes>
    	</entity>
     
    	<entity class="Task">
    		<attributes>
    			<basic name="time">
    				<temporal>TIME</temporal>
    			</basic>
    			<basic name="name"></basic>
    			<many-to-one name="parent" target-entity="BaseEntity"
    				fetch="LAZY" />
    			<one-to-many name="enfants" target-entity="BaseEntity">
    			 	<cascade>
    			 		<cascade-all/>
    			 	</cascade>
    			</one-to-many>
    		</attributes>
    	</entity>
    [...]
    EJB/WS :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    @Stateless
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    @WebService(name="ProjectTest", serviceName="ServiceProject")
    @SOAPBinding(style=Style.RPC)
    public class ServiceProject {
    	@EJB
    	private IDaoProject dao;
     
    	@WebMethod
    	public Task[] getAll() {
    		List<Task> tasks = dao.getAll(); 
    		return tasks.toArray(new Task[0]);
    	}
    }
    Petite subtilité : getAll renvoie un Task[] pour pouvoir être une WebMethod.

    Une fois que tout est déployé, j'obtiens le wsdl suivant (via l'adresse http://localhost:8080/[dossier de déploiement]/ServiceProject?wsdl) :

    (à peu prêt, j'ai taillé dans mon wsdl)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    <definitions name="ServiceProject" targetNamespace="http://service.test.org/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://service.test.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <types>
      <xs:schema targetNamespace="http://service.test.org/" version="1.0" xmlns:tns="http://service.test.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
       <xs:complexType name="task">
        <xs:complexContent>
         <xs:extension base="tns:baseEntity">
          <xs:sequence>
           <xs:element maxOccurs="unbounded" minOccurs="0" name="enfants" nillable="true" type="tns:baseEntity"/>
           <xs:element minOccurs="0" name="name" type="xs:string"/>
           <xs:element minOccurs="0" name="parent" type="tns:baseEntity"/>
           <xs:element minOccurs="0" name="time" type="xs:dateTime"/>
          </xs:sequence>
         </xs:extension>
        </xs:complexContent>
       </xs:complexType>
       <xs:complexType name="baseEntity">
        <xs:sequence>
         <xs:element minOccurs="0" name="id" type="xs:int"/>
        </xs:sequence>
       </xs:complexType>
       <xs:complexType final="#all" name="taskArray">
        <xs:sequence>
         <xs:element maxOccurs="unbounded" minOccurs="0" name="item" nillable="true" type="tns:task"/>
        </xs:sequence>
       </xs:complexType>
      </xs:schema>
     </types> 
     <message name="ProjectTest_getAll"/>
     <portType name="ProjectTest">
      <operation name="getAll">
       <input message="tns:ProjectTest_getAll"/>
       <output message="tns:ProjectTest_getAllResponse"/>
      </operation>
     </portType>
     <binding name="ProjectTestBinding" type="tns:ProjectTest">
      <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
      <operation name="getAll">
       <soap:operation soapAction=""/>
       <input>
        <soap:body namespace="http://service.test.org/" use="literal"/>
       </input>
       <output>
        <soap:body namespace="http://service.test.org/" use="literal"/>
       </output>
      </operation>
     </binding>
     <service name="ServiceProject">
      <port binding="tns:ProjectTestBinding" name="ProjectTestPort">
       <soap:address location="http://127.0.0.1:8080/simexplorer-si-template-ejb/ServiceProject"/>
      </port>
     </service>
    </definitions>
    Et maintennant, si je remplis ma base avec une arborescence de tâches, je peux très bien appeler mon service depuis mon appli web, pour retrouver la liste des tâches. Les enfants de ceux ci ne sont pas initialisés, mais j'ai des BaseEntity avec des id.

    Mais si j'appelle getAll en WS, l'appel à mon service va bien se passer, mais lorsque la librairie construit le résultat pour le client, elle va chercher à instancier toute l'arborescence. Et puisqu'on est sorti de la méthode du service (où mon contexte transactionnel me permet le chargement JPA), on a une LazyException.
    Le deuxième problème qui est apparu, c'est que lorsque le parent d'un enfant est remonté pour la réponse, il retrouve la même instance. Il constate la récursivité que cela impliquerait et s'arrête.
    Pour palier à ce problème, j'ai essayé de passer le parent et les enfants en BaseEntity, pour renvoyer uniquement les ids. Mais l'instance de parent est bien une Task, et le WS l'"introspecte" quand même...

    Si vous voyez une issue, je suis preneur

    Merci

  2. #2
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 10
    Points : 6
    Points
    6
    Par défaut
    Une solution :

    JAXB est utilisé pour "marshalisé" les POJO en XML lors des appels WS.
    Il suffit de le configurer un petit peu :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Task extends BaseEntity 
    {
    	private Date time;
    	private String name;
    	@XmlElement(type=BaseEntity.class)
    	private Task parent;
    	@XmlJavaTypeAdapter(TaskSetAdapter.class)
    	private Set<Task> enfants;
    }
    avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    public class TaskSetAdapter extends XmlAdapter<BaseEntity[],Set<Task>> {
     
    	@Override
    	public BaseEntity[] marshal(Set<Task> arg0) throws Exception {
    		BaseEntity[] r = new BaseEntity[arg0.size()];
    		int i = 0;
    		for (Iterator<Task> iterator = arg0.iterator(); iterator.hasNext();) {
    			Task task = (Task) iterator.next();
    			r[i] = new BaseEntity();
    			r[i].setId(task.getId());
    			i++;
    		}
    		return r;
    	}
     
    	@Override
    	public Set<Task> unmarshal(BaseEntity[] arg0) throws Exception {
    		Set<Task> r = new HashSet<Task>();
    	    for( BaseEntity be : arg0 ) {
    	    	Task t = new Task();
    	    	t.setId(be.getId());
    	        r.add(t);
    	    }
    	    return r;
    	}
     
    }
    Plus d'infos : http://weblogs.java.net/blog/kohsuke...pter_in_j.html

    Du coup dans mon orm.xml, je repasse mes propriétés de Task vers le vrai type, sans me soucier de la serialization.


  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 10
    Points : 6
    Points
    6
    Par défaut
    Pour préciser le
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    	@XmlElement(type=BaseEntity.class)
    	private Task parent;
    ne force pas l'instance parent à être marshalisé en BaseEntity... J'ai du créer un autre XmlAdapter qui prend un Task pour le transformer en BaseEntity...

  4. #4
    Membre régulier
    Profil pro
    Architecte
    Inscrit en
    Mai 2004
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Architecte

    Informations forums :
    Inscription : Mai 2004
    Messages : 57
    Points : 80
    Points
    80
    Par défaut
    Bonjour,
    J'ai un problème similaire mais avec JPA pour persister une classe qui peut être parente d'elle même.

    Voilà : J'ai des dossiers avec des sous dossier.

    J'ai donc fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    @Entity
    @Table (name = "t_dossier")
    public class Dossier {
    	@Id
    	private Long id;
    	@Column(unique = true, nullable = false)
    	private String nom;
            @Column ...
    	private String description;
     
    	@OneToMany(?? )
    	private List<Dossier> dossiers;
    }
    Ma question est la suivante : Comment coder les annotations pour ma liste de dossier "enfants".
    Est ce la bonne solution ?
    Il faut peut être avoir un champ parent dans le dossier et prévoir une requête qui remonte la liste des dossier ayant le même parent. Mais dans ce cas comment annoter l'attribut parent ?

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2004
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2004
    Messages : 10
    Points : 6
    Points
    6
    Par défaut
    Citation Envoyé par cyspeo Voir le message
    Ma question est la suivante : Comment coder les annotations pour ma liste de dossier "enfants".
    Mon problème concernait plutôt le passage des Entity dans les Webservice, là où la serialization peut poser problème avec des enfants et un parent.
    Pour ton problème, je te suggère de lire attentivement le tutorial présent sur ce site : http://tahe.developpez.com/java/jpa/ . Il est vraiment très très bon.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 24/07/2009, 10h59
  2. JPA @OneToMany and database
    Par useramina dans le forum JPA
    Réponses: 1
    Dernier message: 05/05/2009, 20h26
  3. JSF injection EJB3 stateless
    Par nathieb dans le forum JSF
    Réponses: 3
    Dernier message: 04/02/2009, 10h06
  4. [Integration] No Hibernate Session bound to thread, ejb3 stateless et Spring
    Par pbdlpc dans le forum Spring
    Réponses: 4
    Dernier message: 26/09/2008, 16h23
  5. JPA OneTOMany Deux EJB jar
    Par nathieb dans le forum JPA
    Réponses: 5
    Dernier message: 10/07/2008, 16h18

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo