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

Persistance des données Java Discussion :

Quelques questions sur l'utilisation d'Hibernate


Sujet :

Persistance des données Java

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    Février 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Février 2017
    Messages : 4
    Points : 1
    Points
    1
    Par défaut Quelques questions sur l'utilisation d'Hibernate
    Bonjour,

    J'ai fais un petit projet test pour apprendre Hibernate et il y a un certain nombre de choses que je ne sais pas comment faire correctement (en gros ça marche mais ça à l'air de tenir plus de la chance que de la technique).

    J'ai une couche contrôleur, 1 couche service et 1 couche accès aux données. Dans ma couche accès aux données j'accède aux objets persistés via Hibernate (jusque là ça va). J'ai mis certaines relations en lazy loading (mettons une relation Classe vers Eleves). Là où ça se complique c'est que des fois j'ai besoin des données des élèves et des fois non, suivant les méthodes de ma couche contrôleur.

    1) Si je veux faire un service qui liste les classes, pas de problème la relation est en lazy loading et je ne récupère donc pas les données des élèves en trop tant que je n'en ai pas besoin. Par contre une Classe à une relation "parent" vers une Ecole (qui elle liste toutes les classes). Comme le client de mon service n'a pas besoin de ce lien, je le mets à null dans la couche contrôleur pour qu'il ne soit pas sérialisé. Ca fonctionne mais je ne comprends pas pourquoi ça n'a pas d'impact sur la base de données, alors que je n'ai pas détaché mon entité. J'ai de la chance et je ne peux pas compter que ça fonctionnera à tous les coups ou il y a une vrai raison et ça fonctionnera tout le temps?
    Avec les traces activées, je vois que les requêtes d'écriture ne sont pas déclanchées tout de suite (dès fois une lecture les délanche). Je suppose donc que le framework choisit quand exécuter les requêtes et j'ai juste eu de la chance qu'il n'ai pas choisit d'exécuter ma modif??? Ou alors c'est parceque lorsque je veux vraiment faire une modif c'est fait dans une méthode annotée @Transactional et qu'en dehors de ces méthodes aucune mise à jour n'est prise en compte?

    2) Pour éviter d'avoir des impacts sur la base de données j'ai essayé de détacher systématiquement mes entités dans la couche de persistence. Sauf que ça fonctionne très bien dans le cas où je n'ai pas besoin des données en lazy loading mais nettement moins bien quand j'essaye d'y accéder. Je comprends la raison, mais comment je devrais faire? ne jamais détacher? Ou détacher plus haut (couche service ou couche contrôleur)? Ca me gène un peu d'exposer l'EntityManager dans les couches supérieures donc je ne pense pas que ce soit la bonne solution... Du coup je ne détache pas, ça fonctionne mais je tombe dans le cas 1).

    Merci pour les éclaircissements que vous pourriez m'apporter.

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Première question: pourquoi diable y-a-t-il sérialisation entre ton controlleur et ton dao? Ce n'est à priori pas nécessaire.

    En supposant ce problème retiré, ce que tu dois faire, c'est avoir une seul et unique transaction qui dure tant que ton controlleur a besoin de données. A priori ton controlleur travaille pour 1 demande de ta vue (donc 1 transaction) en effectuant un certain nombre d'opérations sur le dao.

    Pour ce qui est de l'exécution des requêtes d'hibernate, le moment où elle s'exécuteront dépendra de si tu utilise hibernate tout seul ou comme un provider JPA. En JPA toute modification est prise en compte et la transaction commit par défaut. En Hibernate, la transaction ne commit pas sauf si tu le demande. Dans tous les cas, hibernate modifiera la db dès qu'il estime que la requête que tu t'apprête à exécuter aura besoin de données présentes dans les pojos modifiés pour retourner un résultat correct.

    Exemple: tu change le nom d'un élève puis tu cherche tous les élève dont le nom commence par a. Les modifications seront flushées.

  3. #3
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    Février 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Février 2017
    Messages : 4
    Points : 1
    Points
    1
    Par défaut
    J'ai du mal m'exprimer. La sérialisation n'est bien évidemment pas entre le controleur et le DAO mais entre le contrôleur et la vue. Je récupère des données dpuis mon DAO, elles remontent jusqu'à mon contrôleur et c'est là que j'essaye de retirer tout ce dont la vue n'a pas besoin. Le problème c'est que si mon objet est détaché au niveau du DAO je peux retirer les données dont je n'ai pas besoin (pour que la sérialisation soit plus légère) mais dans ce cas là je ne peux plus accéder aux données en lazy loading.
    Et si je ne détache pas je peux charger les données en lazy loading lorsque j'y accède dans mon contrôleur mais quand je retire des données (dans le contrôleur) avant la sérialisation je ne vois aucune requête dans la console mais j'ai peur qu'elle puisse être effectuée quand même au moment ou la transaction est flushée...

    Ex: une méthode du contrôleur qui récupère La liste des Eleves d'une classe n'a pas besoin de renvoyer à la vue la relation vers la Classe car on sait déjà dans quelle classe il est. Donc je fais un eleve.setClasse(null) dans le contrôleur. Même si je n'ai pas détaché l'objet Eleve je n'ai pas de requête qui efface la relation entre l'élève et la classe mais ça je ne comprends pas pourquoi...

  4. #4
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Il faudrait que tu détaille un peu plus ton architecture. Pourquoi ce besoin de sérialisation? Ton DAO et ta vue sont sur des JVMs différentes? Si c'est le cas, en général on ne transfère pas directement les objets Business, on recours plutôt à des DAO intermédiaire qui collent plus aux méthodes du controlleur qu'à la structure DB. Ce qui permet par exemple d'avoir une méthode qui retourne des collections, d'autres juste des données sommaires.
    Il faudrait aussi que tu détaille comment tu démarque tes transactions et tes sessions. Est-ce que tu fais du JPA?

  5. #5
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    Février 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Février 2017
    Messages : 4
    Points : 1
    Points
    1
    Par défaut
    J'utilise Spring + JPA. La sérialisation c'est de la sérialisation JSON pour la vue et elle est gérée automatiquement par le controleur Spring.

    Mon controleur est annoté avec l'annotation @RestController.

    Dedans j'ai 2 méthodes:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Eleves[] getEleves(int classeId) {
      ... // récupération des données auprès de la couche service
      for (Eleves eleve : eleves) {
         eleve.setClasse(null);    //On efface la relation vers la classe pour alléger la sérialisation. Mauvaise idée???
      }
    }
     
    Classes[] getClasses() {
    ...  // récupération des données auprès de la couche service
    }
    Quand ma vue invoque getClasses() elle s'attend à avoir 1 tableau de Classe dont chacune possède une liste d'élèves.

    Quand ma vue invoque getEleves() elle s'attend à avoir 1 tableau d'élève mais pour alléger la sérialisation je voudrait mettre la backreference de chaque Eleve vers Classe à null (la vue sait déjà dans quelle classe on est puisqu'elle à passé le classeId).

    Que je mette ou pas l'annotation @Transactional ça fonctionne. Du coup je ne sais pas bien où l'objet est détaché (la transaction et la session sont ouvertes au niveau du contrôleur géré par Spring ???. Si c'est le cas pourquoi je ne vois pas passer de requête SQL quand je fais eleve.setClasse(null) puisque je suis encore dans le contrôleur?).

  6. #6
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Si c'est du JPA, la session a la durée de vie de la transaction, géré par le conteneur. Je ne connais pas assez spring et sa gestion transactionnelle pour savoir à quel moment elle est démarquée par rapport au conteneur ou si ça a le moindre rapport.


    Comme tu commence à le constater c'est une mauvaise idée de mapper directement tes entités sur la couche rest:

    tu expose éventuellement des clés privées sur la couche rest, pire quand tu va recevoir des données, tu va être obligé de vérifier que l'utilisateur n'utilise pas par exemple ton élève pour le changer de classe en catimini voir créerait tout un structure dans ton dos. Il faut que tu sépare en ayant des objet spécifiques à ta vue et ton controlleur rest se charge de transmettre ce qu'il y a à transmettre. Exemple: tu veux stocker une nouvelle note pour un élève, ta méthode devrait jsute accepter l'id de l'élève et la note.

  7. #7
    Nouveau Candidat au Club
    Homme Profil pro
    Développeur Java
    Inscrit en
    Février 2017
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Février 2017
    Messages : 4
    Points : 1
    Points
    1
    Par défaut
    D'accord. Donc ça veut dire créer obligatoirement des DTO? J'avais vu comprendre qu'avec Hibernate on pouvait justement se passer de DTO.

  8. #8
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Tes pojos hibernate sont effectivement tes DTO pour ta partie base de données oui. Mais ils mappent ta structure de données, qui n'est pas nécessairement la même que la structure que tu veux montrer à ton client (ici ton client REST). D'ailleurs c'est tout le sujet de la question: "tu veux exposer quelque chose qui ne correspond pas à ta DB: pas d'arbre, pas de parent". Pour ce faire le plus simple reste d'avoir des DTOs spécifiques à ton REST.

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    230
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 230
    Points : 104
    Points
    104
    Par défaut
    un peu tardive...
    en continuant, tu vas avoir des DTO pour chaque service , car chaque client de ton web service voudront telles ou telles datas et pas d'autres....

    une bonne liste de DTO dans tout ton projet !! table BD <-> entity java <-> DTO

  10. #10
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Septembre 2006
    Messages
    2 936
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 2 936
    Points : 4 356
    Points
    4 356
    Par défaut
    Citation Envoyé par tchize_ Voir le message
    Si c'est du JPA, la session a la durée de vie de la transaction, géré par le conteneur. Je ne connais pas assez spring et sa gestion transactionnelle pour savoir à quel moment elle est démarquée par rapport au conteneur ou si ça a le moindre rapport.
    org.springframework.transaction.annotation.Transactional != javax.transaction.Transactional

    Côté serveur JEE, les transactions font partie de la spécification générale et non pas de JPA en particulier qui n'est qu'un des possibles consommateurs des transactions gérées par le container.
    Côté Spring, le concept des transactions est géré indépendamment de la spécification JEE ce qui permet à une application Spring de fonctionner de manière "identique" (sic) sous un container JEE que sous un simple application container.
    Mais sous un serveur JEE, Spring "sait" qu'il est dans ce contexte particulier (non seulement JEE, mais aussi en fonction de la version 6/7… et du type de container et sa version… ) et donc choisit les classes qui implémentent ses propres abstractions en fonction du contexte et il est parfaitement capable de s'apercevoir si un Tx a été démarrée par le container ou non, et faire en sorte de respecter la "volonté" du développeur exprimée dans les paramètres des annotations.

    Tout cela se complique quand on mélange les 2 : des @EJB appelant des @Component Spring (et recip.) , mais Spring essaie de faire en sorte de propager correctement le contexte transactionnel d'un espace à l'autre
    + le fait que Spring a pas mal de paramètres pour gérer le "comment" de l'aspect transactionnel (de son abstraction :pas de celle du serveur JEEE…): directement via AspectJ (@Transactional sera respecté lors des appels de méthode du même bean) ou via des proxy (CGLIB ou JVM)
    (@Transactional ne fonctionne que pour les appels inter-beans…, l'un voulant @Transactional sur les implémentations et l'autre sur les interfaces…)

    ce qui fait en théorie pas mal de combinaisons possibles…

    (et sans parler des nested Tx, des read-only, du support de l'isolation level…)

    Pour une application Spring simple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
     
    // <tx:annotation-driven />
    @EnableTransactionManagement(proxyTargetClass =true)
    // <aop:aspectj-autoproxy />
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    et
    les org.springframework.transaction.annotation.Transactional sur les implémentations des méthodes des bean @Repository
    sans oublier
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    @Bean
        public PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
          return new PersistenceExceptionTranslationPostProcessor();
        }
    dans votre @Configuration (ou l'équivalent en XML) pour que la traduction des exceptions par le stéréotype @Repository fonctionne comme attendu.

    Les paramètres spécifiques à un environnement particulier sont alors généralement liés aux propriétés JPA utilisées lors de la création de l'entity manager (factory), exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    jpaProperties.put("javax.persistence.transactionType", "jta");
    jpaProperties.put("hibernate.transaction.jta.platform","org.hibernate.engine.transaction.jta.platform.internal.WeblogicJtaPlatform");
    +
    évidemment le JPA vendor adapter et la data source qui sont aussi spécifiques à votre environnement, exemples:

    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
     
    	@Value("${connection.show_sql:false}")
    	private String				showSql;
     
    	@Value("${connection.datasource}")
    	private String				jndiConnectionName;
     
    	/**
             * 
             * @return the JPA vendor
             */
    	@Bean
    	public JpaVendorAdapter jpaVendorAdapter() {
    		HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
    		adapter.setDatabase(Database.ORACLE);
    		adapter.setGenerateDdl(false);
    		adapter.setShowSql(ConversionUtils.isStringTrue(showSql));
    		adapter.setDatabasePlatform("org.hibernate.dialect.Oracle10gDialect");
    		return adapter;
    	}
     
    	/**
             * <jee:jndi-lookup id="dataSource" jndi-name="${connection.datasource}" />
             * 
             * @return the data source
             * @throws NamingException
             */
    	@Bean(destroyMethod = "")
    	public DataSource dataSource() throws NamingException {
    		final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
    		return dsLookup.getDataSource(jndiConnectionName);
    	}
     
    	/**
             * equivalent to the <tx:jta-transaction-manager /> XML
             * 
             * @return the transaction manager
             */
    	@Bean
    	public PlatformTransactionManager transactionManager() {
    		JtaTransactionManager txManager = new JtaTransactionManager();
    		txManager.afterPropertiesSet();
    		return txManager;
    	}

Discussions similaires

  1. Plugin Week Calendar, j'ai quelques question sur l'utilisation !
    Par Cvbdev dans le forum Bibliothèques & Frameworks
    Réponses: 0
    Dernier message: 21/06/2010, 14h00
  2. Question sur l'utilisation de wget
    Par berry dans le forum Réseau
    Réponses: 7
    Dernier message: 24/05/2007, 22h46
  3. Quelques question sur Win 32 Appli
    Par lvdnono dans le forum Windows
    Réponses: 5
    Dernier message: 15/06/2004, 12h37
  4. Quelques questions sur le TWebBrowser...
    Par CorO dans le forum Web & réseau
    Réponses: 3
    Dernier message: 17/01/2003, 21h23

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