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

JPA Java Discussion :

Problème de requête SELECT COUNT(*) avec table de jointure many-to-many


Sujet :

JPA Java

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2012
    Messages : 474
    Points : 586
    Points
    586
    Par défaut Problème de requête SELECT COUNT(*) avec table de jointure many-to-many
    Bonjour,

    Pour mon qcm, je veux calculer le nombre des bonnes réponses. J'avais repris un exemple sur le net avec l'annotation @Formula dans ma classe Entité mais ca ne marchait pas.
    J'ai alors voulu passé par une requête dans la couche métier, mais bien qu'elle fonctionne parfaitement sous mysql, j'ai une erreur (la table de jointure n'est pas mappée).
    Pourriez vous m'aider à trouver la solution ?

    Voici ma requête :
    "SELECT COUNT(*) FROM Evaluation_Reponse er, Reponse r WHERE er.evaluation_id = :evaluationId AND r.id = er.listOfReponses_id AND r.correct");

    où Evaluation et Reponse sont 2 entités liées en many-to-many. Le fait d'ajouter @JoinTable, de modifier la casse... n'a jamais rien changer.

  2. #2
    Modérateur

    Homme Profil pro
    Développeur java, access, sql server
    Inscrit en
    Octobre 2005
    Messages
    2 709
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur java, access, sql server
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 709
    Points : 4 790
    Points
    4 790
    Par défaut
    Tu peux indiquer les structures de tables ?
    Labor improbus omnia vincit un travail acharné vient à bout de tout - Ambroise Paré (1510-1590)

    Consulter sans modération la FAQ ainsi que les bons ouvrages : http://jmdoudoux.developpez.com/cours/developpons/java/

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2012
    Messages : 474
    Points : 586
    Points
    586
    Par défaut
    Voila :
    classe Evaluation
    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
    @Entity
    public class Evaluation implements Serializable {
     
        private static final long serialVersionUID = -1L;
     
    	private Long id;
    	private Date Date;
    	private int time ;
     
        private Qcm qcm;
        private List<Reponse> listOfReponses = new ArrayList<Reponse>();
     
        private Integer nbCorrectAnswers;
     
        @ManyToMany
        @JoinTable(name="Evaluation_Reponse") //je ne l'avais pas mis au début
        public List<Reponse> getListOfReponses() {
    	return listOfReponses;
        }
    Classe Reponse
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Entity
    public class Reponse implements Serializable{
     
    	private static final long serialVersionUID = 1L;
     
    		private Long id;
    		private String libelle;
    		private boolean correct;
    		private Question question;
    J'ai ajouté @JoinTable(name="Evaluation_Reponse") après coup, mais ca n'a rien changé, meme en ajoutant les @JoinColumns.
    Pour la classe Reponse, je n'ai pas mis de lien avec Evaluation car je n'ai pas besoin d'une table Reponse_Evaluation. Je l'ai fais pour vérifier, mais ca ne change rien.

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2012
    Messages : 474
    Points : 586
    Points
    586
    Par défaut
    oups, j'ai mal lu, ce sont les tables et non les classes que tu voulais.
    Je précise qu'avec jpa, elles sont créées automatiquement dans la base. Je les remplis ensuite si besoin est.

    donc dans la table evaluation, j'ai les colonnes
    - id
    - date
    - nbCorrectAnswers
    - time
    - qcm_id
    Elle est vide au départ et se remplit à chaque nouveau test.

    Dans la table reponse, j'ai
    - id
    - correct
    - libelle
    - question_id
    Après qu'elle ait été créée par jpa, je l'ai rempli manuellement

    et enfin, la table de jointure evaluation_reponse contient :
    - Evaluation_id
    - listOfReponse_id
    Tout comme la table evaluation, elle se remplit à chaque test

    je sais, c'est un peu du franglais, mais j'avais commencé en français et on m'a dit de tout mettre en anglais, donc avant de tout modifier, je préférerai que ca marche nickel.

  5. #5
    Modérateur

    Homme Profil pro
    Développeur java, access, sql server
    Inscrit en
    Octobre 2005
    Messages
    2 709
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur java, access, sql server
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 709
    Points : 4 790
    Points
    4 790
    Par défaut
    Comment récupères-tu les données dans java avec la requête SQL ?
    Labor improbus omnia vincit un travail acharné vient à bout de tout - Ambroise Paré (1510-1590)

    Consulter sans modération la FAQ ainsi que les bons ouvrages : http://jmdoudoux.developpez.com/cours/developpons/java/

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2012
    Messages : 474
    Points : 586
    Points
    586
    Par défaut
    j'ai pas tout bien compris la question, donc ma requête est incluse dans la méthode suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Override
    	public void saveCorrectAnswers(Evaluation evaluation) {
    		Query query = em.createQuery("SELECT COUNT(*) FROM evaluation_reponse er, Reponse r WHERE er.Evaluation_id = :evaluationId AND r.id = er.listOfReponses_id AND r.correct");
    		query.setParameter("evaluationId", evaluation.getId());
    		Long result=(Long) query.getSingleResult();
    		int nbCorrectAnswers = ((Long) result).intValue(); // convertit result en Integer
    		evaluation.setNbCorrectAnswers(nbCorrectAnswers);
    		evaluationRepository.save(evaluation);		
    	}
    J'appelle cette méthode après avoir enregistrer mon Evaluation, car sinon il recherche dans du vide. Cette méthode marche très bien puisque si j'utilise une requête toute simple du genre ("SELECT COUNT(*) FROM Evaluation e"), le nombre est bien chargé dans la base.

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2012
    Messages : 474
    Points : 586
    Points
    586
    Par défaut
    l'erreur que j'ai en console est :
    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
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    20 août 2012 01:16:46 org.apache.catalina.core.StandardWrapperValve invoke
    GRAVE: Servlet.service() for servlet [spring-mvc] in context with path [/cmap-web] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Evaluation_Reponse is not mapped [SELECT COUNT(*) FROM Evaluation_Reponse er, com.almerys.jpa.tomcatspring.Reponse r WHERE er.Evaluation_id = :evaluationId AND r.id = er.listOfReponses_id AND r.correct]] with root cause
    org.hibernate.hql.internal.ast.QuerySyntaxException: Evaluation_Reponse is not mapped [SELECT COUNT(*) FROM Evaluation_Reponse er, com.almerys.jpa.tomcatspring.Reponse r WHERE er.Evaluation_id = :evaluationId AND r.id = er.listOfReponses_id AND r.correct]
    	at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:180)
    	at org.hibernate.hql.internal.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:110)
    	at org.hibernate.hql.internal.ast.tree.FromClause.addFromElement(FromClause.java:93)
    	at org.hibernate.hql.internal.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:326)
    	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3252)
    	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3141)
    	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:694)
    	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:550)
    	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:287)
    	at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:235)
    	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:248)
    	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:183)
    	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)
    	at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:101)
    	at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
    	at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:119)
    	at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:214)
    	at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:192)
    	at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1537)
    	at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:284)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    	at java.lang.reflect.Method.invoke(Unknown Source)
    	at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
    	at $Proxy33.createQuery(Unknown Source)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    	at java.lang.reflect.Method.invoke(Unknown Source)
    	at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
    	at $Proxy25.createQuery(Unknown Source)
    	at com.almerys.jpa.tomcatspring.service.QcmServiceImpl.saveCorrectAnswers(QcmServiceImpl.java:43)
    	at com.almerys.jpa.tomcatspring.web.FormationController.processQcm(FormationController.java:97)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    	at java.lang.reflect.Method.invoke(Unknown Source)
    	at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
    	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
    	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
    	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
    	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
    	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
    	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
    	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
    	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
    	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
    	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1001)
    	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579)
    	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    	at java.lang.Thread.run(Unknown Source)

  8. #8
    Modérateur

    Homme Profil pro
    Développeur java, access, sql server
    Inscrit en
    Octobre 2005
    Messages
    2 709
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur java, access, sql server
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 709
    Points : 4 790
    Points
    4 790
    Par défaut
    Bon, je ne connais pas hibernate (je ne travaille qu'en JDBC).
    On peut essayer de procéder par élimination.

    D'abord, je n'écrirais pas la requête sans donner un nom de champ pour le résultat :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT COUNT(*) AS NbRepOK FROM evaluation_reponse er, Reponse r WHERE er.Evaluation_id = :evaluationId AND r.id = er.listOfReponses_id AND r.correct
    Pour info, c'est vrai que ça marche dans MySql sans donner de nom de colonne car certains outils crée automatiquement un nom (MySqlWorkbench par exemple).
    En revanche si j'écris cela dans JDBC c'est la plantade assurée !
    -> 1ère action : écrire la requête comme ci-dessus


    Par ailleurs, pour le passage de paramètre, evaluationId, je suis assez sceptique.
    La doc Hibernate donne comme exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    List mothers = session.createQuery(
        "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
        .setString(0, name)
        .list();
    ce qui veut dire que l'emplacement du paramètre doit être symbolisé par un "?" (comme dans JDBC)
    -> 2ème action : faire un test sans paramètre en écrivant une valeur réelle dans la requête.
    En supposant qu'il a une evaluationId=123, cela donne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    		Query query = em.createQuery("SELECT COUNT(*) AS NbRepOK FROM evaluation_reponse er, Reponse r WHERE er.Evaluation_id = 123 AND r.id = er.listOfReponses_id AND r.correct");
    		Long result=(Long) query.getSingleResult();
    		int nbCorrectAnswers = ((Long) result).intValue(); // convertit result en Integer
    si le code ci-dessus fonctionne, cela veut dire que c'est le passage de paramètre qui est dans les bégonias.
    Labor improbus omnia vincit un travail acharné vient à bout de tout - Ambroise Paré (1510-1590)

    Consulter sans modération la FAQ ainsi que les bons ouvrages : http://jmdoudoux.developpez.com/cours/developpons/java/

  9. #9
    Membre averti
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    250
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2011
    Messages : 250
    Points : 403
    Points
    403
    Par défaut
    La stacktrace te précise que l'objet Evaluation_Response n'est pas mappé.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    org.hibernate.hql.internal.ast.QuerySyntaxException: Evaluation_Reponse is not mapped
    C'est normal, tu as fait ta requête en JPQL qui se base sur les objets, or ton objet Evaluation_Response n'existe pas d'un point de vue Hibernate (Aucune classe Evaluation_Response annotée @Entity n'existe).
    si tu veux conserver ta requête SQL, tu dois utiliser une nativeQuery et pas une Query JPQL.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Query q = session.createSQLQuery("SELECT COUNT(*) FROM evaluation_reponse er, Reponse r WHERE er.Evaluation_id = :evaluationId AND r.id = er.listOfReponses_id AND r.correct= :pCorrect")

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2012
    Messages
    474
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2012
    Messages : 474
    Points : 586
    Points
    586
    Par défaut
    je viens juste de voir ta dernière réponse.
    Avant cela, j'ai bien suivi un protocole de mapping pour des relations many-to-many. Et pour résoudre le problème de mapping de la table de jointure, j'avais trouvé sur le net qu'il suffisait de mettre @Transactional à ses classes de persistences, voire aussi de Service. Je l'ai donc fais un peu partout mais j'ai eu une nouvelle erreur, un problème de Persist de la classe Reponse.

    Je ne veux pas particulièrement conserver ma requête, j'en veux juste une qui marche
    En essayant la tienne, Eclipse voyait rouge, mais tu m'as tout de même donné une bonne piste puisque j'ai réussi à trouver la solution. Ce n'est pas "session.createSQLQuery" qu'il fallait mettre mais "em.createNativeQuery". Et au final, la méthode ressemble à ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    @Override
    	public void saveCorrectAnswers(Evaluation evaluation) {
    		Query query = em.createNativeQuery("SELECT COUNT(*) AS NbRepOK FROM Evaluation_Reponse er, Reponse r WHERE er.Evaluation_id = :evaluationId AND r.id = er.listOfReponses_id AND r.correct");
    		query.setParameter("evaluationId", evaluation.getId());
    		Integer nbCorrectAnswers = ((BigInteger)query.getSingleResult()).intValue(); // convertit result en Integer
    		evaluation.setNbCorrectAnswers(nbCorrectAnswers);
    		evaluationRepository.save(evaluation);		
    	}
    Je l'ai testé 2-3 fois, et ca a l'air de marcher. J'ai juste eu un petit problème technique de Cast à modifier.
    Tant de temps pour juste ca, je suis bien dégouté. En tout cas, merci de ton aide, ca m'a libéré d'un poids.

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

Discussions similaires

  1. select count avec 3 tables
    Par Riwalenn dans le forum Langage
    Réponses: 3
    Dernier message: 15/09/2010, 14h26
  2. [Requête SQL] - Select count avec plusieurs tables
    Par Pithonnette dans le forum SQL
    Réponses: 7
    Dernier message: 25/06/2009, 19h19
  3. Réponses: 1
    Dernier message: 22/12/2008, 10h15
  4. problème pour requête SELECT sur plusieurs tables
    Par 3dagard dans le forum Requêtes
    Réponses: 15
    Dernier message: 18/08/2008, 00h34
  5. Réponses: 21
    Dernier message: 03/08/2007, 12h19

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