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 :

[JPA][HIBERNATE] Comment utiliser un Singleton ?


Sujet :

JPA Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    gnome
    Inscrit en
    Octobre 2004
    Messages
    142
    Détails du profil
    Informations personnelles :
    Localisation : El Salvador

    Informations professionnelles :
    Activité : gnome

    Informations forums :
    Inscription : Octobre 2004
    Messages : 142
    Par défaut [JPA][HIBERNATE] Comment utiliser un Singleton ?
    Bonjour,

    J'utilise Hibernate dans son mode JPA sur une application web (développée sous Struts 2, mais ce n'est pas essentiel). J'utilise aussi le pool de connexion C3PO fourni avec Hibernate.

    En Struts 2, chaque requête utilisateur déclenche l'exécution d'une classe "Action", qui appele ensuite une vue (jsp) pour produire l'affichage de ce qu'elle a traité.

    Dans chacune de mes classes Action, j'ouvre la connexion vers l'unité de persistence comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    EntityManagerFactory emf = Persistence.createEntityManagerFactory(ck.PERSISTENCE_UNIT);
    em = emf.createEntityManager();
    EntityTransaction tx = em.getTransaction();
    tx.begin();
    Je fais les traitements, initialise les objets à afficher par la JSP, puis ferme la connexion comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // fin transaction
    tx.commit();
    em.close();
    emf.close();
    Ceci fonctionne à peu près, mais je pense que ce n'est pas optimisé. En effet, après plusieurs requêtes successives ou simultannées, je finis par déclencher des exceptions du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    16:22:06,311 ERROR JDBCExceptionReporter:72 - Connections could not be acquired from the underlying database!
    16:22:06,322 ERROR [default]:253 - "Servlet.service()" pour la servlet default a généré une exception
    com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source.
    ou bien :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    javax.servlet.ServletException: org.hibernate.exception.GenericJDBCException: Cannot open connection
    	org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:515)
    	org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:419)
     
    cause mère
     
    javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Cannot open connection
    	org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:647)
    	org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:40)
    Puis l'application ne réponds plus à mes requêtes.
    Est-ce que je procède comme il faut ?

    J'en déduis que trop d'instances de connexion sont créées, alors que je pourrais peut-être les limiter en utilisant un Singleton.

    Ai-je raison ?
    Si oui, comment faire ?

    Je pense que c'est un problème de débutant, mais c'est pas évident à résoudre tout seul.

    merci de votre aide.

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 276
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 276
    Par défaut
    Ton entityManager ne doit pas être créé à chaque requête.
    Il doit être créé une seule fois.
    L'idée serait de s'inspirer de la classe HibernateUtil en remplaçant la sessionFactory par l'entityManager.
    Je dis ça, mais tu n'as peut-être pas utilisé Hibernate.

    Sinon, tu peux utiliser une ejb3 session comme couche de service, dans lequel tu injecteras ton entityManager, via une annotation.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
        @PersistenceContext(unitName="TestEJB3PU")
        private EntityManager entityManager;
     
     
     public void save(Personne personne){
            entityManager.persist(personne);
        }
    Du coup, tu appelerais la méthode save de ton ejb à partir de ton action struts.

  3. #3
    Membre confirmé
    Profil pro
    gnome
    Inscrit en
    Octobre 2004
    Messages
    142
    Détails du profil
    Informations personnelles :
    Localisation : El Salvador

    Informations professionnelles :
    Activité : gnome

    Informations forums :
    Inscription : Octobre 2004
    Messages : 142
    Par défaut
    L'idée serait de s'inspirer de la classe HibernateUtil en remplaçant la sessionFactory par l'entityManager.
    Je dis ça, mais tu n'as peut-être pas utilisé Hibernate.
    Effectivement je n'ai jamais utilisé Hibernate autrement que par JPA, tel que je l'ai décrit. Cette première solution m'inspire pourtant plus que la seconde (car j'ai déjà lu des idées similaires sur le net, sans les comprendre), mais je ne sais pas comment la mettre en oeuvre.

    Aurais-tu un exemple ?


    J'avais déjà essayé un code du style :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    EntityManagerFactory emf = Persistence.createEntityManagerFactory(ck.PERSISTENCE_UNIT);
    if (em == null) em = emf.createEntityManager();
    Tout fonctionne plus vite, em n'est plus instancié qu'une seule fois, mais une fois sur 10, j'ai une exception du genre "impossible de requêter sur une connexion fermée". Il doit donc y avoir des conflits entre les threads.

  4. #4
    Expert confirmé
    Avatar de djo.mos
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    4 666
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2004
    Messages : 4 666
    Par défaut
    Citation Envoyé par fr1man Voir le message
    Ton entityManager ne doit pas être créé à chaque requête.
    J'irais pas jusque là, mais je dirais plutôt que c'est vrai pour l'EntityMangerFactory, dont la création est couteuse et ne se fait qu'une fois en général.

    Après, il est assez fréquent de créer un EntityManager pour chaque requête (et le stocker dans un ThreadLocal).

    Dans ce genre de cas, comme l'a signalé fr1man, faut peut être envisager un conteneur, genre Spring par exemple ou EJB.

  5. #5
    Membre confirmé
    Profil pro
    gnome
    Inscrit en
    Octobre 2004
    Messages
    142
    Détails du profil
    Informations personnelles :
    Localisation : El Salvador

    Informations professionnelles :
    Activité : gnome

    Informations forums :
    Inscription : Octobre 2004
    Messages : 142
    Par défaut
    ok. Merci de vos réponses expertes !

    Sachant que je n'ai aucune envie, là en ce moment, de rajouter une couche EJB ou Spring (parce que je ne suis pas compétent dessus, et parce que je ne souhaite pas rajouter une couche suppléménetaire à une petite application qui, je pense, ne le nécessite pas), y-a-t-il une solution pour s'en sortir sans ?

    Comment ne créer qu'une fois l'EntityManagerFactory ?

    J'ai essayé ceci, et ça a l'air de marcher pas mal :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private EntityManager em;
    private static EntityManagerFactory emf;
     
    public String execute() throws Exception {
    	// Openning JPA connection
    	if (emf == null) emf = Persistence.createEntityManagerFactory(ck.PERSISTENCE_UNIT);
    	em = emf.createEntityManager();
    	EntityTransaction tx = em.getTransaction();
    	tx.begin();
            ...
     
    }
    Sans fermer l'emf en fin de méthode...
    Et avec l'emf déclaré static...

    Est-ce que ça vous semble acceptable comme méthode ?

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 276
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 276
    Par défaut
    Effectivement, comme l'a dit djo.mos, c'est l'EntityManagerFactory qui ne doit être créé qu'une fois. Il ne doit donc pas être fermé.

    Tu peux éventuellement charger cet objet dans une classe implémentant l'interface ServletContextListener et notamment la méthode contextInitialized qui est appelé par le serveur d'application au moment du chargement de ton application web.

    Sinon pour le reste, ça semble ok, ce que tu as fait.

  7. #7
    Membre confirmé
    Profil pro
    gnome
    Inscrit en
    Octobre 2004
    Messages
    142
    Détails du profil
    Informations personnelles :
    Localisation : El Salvador

    Informations professionnelles :
    Activité : gnome

    Informations forums :
    Inscription : Octobre 2004
    Messages : 142
    Par défaut
    Merci à vous.

    J'ai appris quelque chose de très intéressant aujourd'hui.

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

Discussions similaires

  1. [Data] Comment utiliser JPA et Spring ?
    Par fredege dans le forum Spring
    Réponses: 3
    Dernier message: 11/06/2009, 09h37
  2. comment utiliser l'attribut de type set hibernate
    Par switch1 dans le forum Hibernate
    Réponses: 2
    Dernier message: 30/04/2009, 11h17
  3. Utiliser JPA/Hibernate avec JBoss et Oracle
    Par GrooveRage dans le forum Wildfly/JBoss
    Réponses: 5
    Dernier message: 12/03/2009, 18h30
  4. Problème d'utilisation JPA+Hibernate+Spring + DB2
    Par menzlitsh dans le forum JPA
    Réponses: 9
    Dernier message: 27/02/2009, 11h19
  5. Réponses: 1
    Dernier message: 15/10/2007, 15h16

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