126 votes

Pourquoi Hibernate ne requiert-il pas de constructeur d'argument ?

Le constructeur sans argument est une exigence (des outils comme Hibernate utilisent réflexion sur ce constructeur pour instancier les objets).

J'ai obtenu cette réponse à la main mais quelqu'un pourrait-il m'expliquer davantage ? Merci

12 votes

Pour info : l'affirmation selon laquelle The no-argument constructor is a requirement c'est faux et toutes les réponses qui expliquent pourquoi il en est ainsi sans se demander si c'est bien le cas (y compris la réponse acceptée, qui a même reçu une prime). ont tort . Voir cette réponse : stackoverflow.com/a/29433238/773113

2 votes

Il est nécessaire si vous utilisez Hibernate comme fournisseur pour JPA.

1 votes

@MikeNakis Vous avez tort, Mike. Hibernate a besoin d'un constructeur par défaut pour instancier les objets si vous utilisez Hibernate comme fournisseur pour JPA (Amalgovinus), sinon Hibernate signalera Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student comme dans le cas que je viens de rencontrer

158voto

mdma Points 33973

Hibernate, et le code en général qui crée des objets via la réflexion utilisent [Class<T>.newInstance()](http://java.sun.com/javase/6/docs/api/java/lang/Class.html#newInstance()) pour créer une nouvelle instance de vos classes. Cette méthode nécessite un constructeur public no-arg pour pouvoir instancier l'objet. Pour la plupart des cas d'utilisation, fournir un constructeur no-arg n'est pas un problème.

Il existe des astuces basées sur la sérialisation qui peuvent contourner l'absence d'un constructeur sans argument, puisque la sérialisation utilise la magie de JVM pour créer des objets sans invoquer le constructeur. Mais ceci n'est pas disponible sur toutes les VMs. Par exemple, XStream peut créer des instances d'objets qui n'ont pas de constructeur public sans argument, mais seulement en s'exécutant dans un mode dit "amélioré" qui n'est disponible que sur certaines VM (voir le lien pour plus de détails). (Voir le lien pour plus de détails.) Les concepteurs d'Hibernate ont sûrement choisi de maintenir la compatibilité avec toutes les VM et évitent donc de telles astuces, et utilisent la méthode de réflexion officiellement prise en charge Class<T>.newInstance() nécessitant un constructeur sans argument.

37 votes

Pour info : Le constructeur n'a pas besoin d'être public. Il peut avoir une visibilité de package et Hibernate devrait setAccessible(true) sur elle.

0 votes

Suis-je capable de créer un UserType personnalisé avec un constructeur non par défaut afin de définir un champ nécessaire pour ses opérations.

1 votes

A titre de référence ObjectInputStream fait quelque chose du genre sun.reflect.ReflectionFactory.getReflectionFactory().newCons‌​tructorForSerializat‌​ion(classToGetInstan‌​ceOf, Object.class.getConstructor()).newInstance() pour l'instanciation d'objets sans constructeur par défaut (JDK1.6 pour Windows)

51voto

Bozho Points 273663

Hibernate instancie vos objets. Il doit donc être capable de les instancier. S'il n'y a pas de constructeur no-arg, Hibernate ne saura pas que l'objet a été instancié. comment pour l'instancier, c'est à dire quel argument passer.

Le site documentation hibernate dit :

4.1.1. Implémenter un constructeur sans argument

Toutes les classes persistantes doivent avoir un constructeur par défaut (qui peut être non public) afin qu'Hibernate puisse les instancier à l'aide de la fonction Constructor.newInstance() . Il est recommandé de disposer d'un constructeur par défaut avec au moins la visibilité du package pour la génération de proxy à l'exécution dans Hibernate.

8 votes

En ce qui concerne la visibilité du constructeur, si vous utilisez JPA v2.0, vous remarquerez que l'attribut JSR-317 dit : Le constructeur sans argument doit être public ou protégé. .

0 votes

@Bozho hello sir, j'ai un doute sur le fait que si en interne hibernate utilise Constructor.newInstance() pour instancier l'objet, alors comment hibernate définit les valeurs dans les champs sans aucun setters défini ?

0 votes

Je ne comprends pas pourquoi je vois cet avertissement pour une sous-classe @Embeddable non-privée avec un constructeur public sans argument...

39voto

Arthur Ronald Points 19001

L'hibernate est un cadre ORM qui prend en charge la stratégie d'accès aux champs ou aux propriétés. Cependant, il ne prend pas en charge le mappage basé sur les constructeurs - ce que vous aimeriez peut-être ? - à cause de certains problèmes comme

Que se passe-t-il si votre classe contient beaucoup de constructeurs ?

public class Person {

    private String name;
    private Integer age;

    public Person(String name, Integer age) { ... }
    public Person(String name) { ... }
    public Person(Integer age) { ... }

}

Comme vous pouvez le constater, vous êtes confronté à un problème d'incohérence car Hibernate ne peut pas savoir quel constructeur doit être appelé. Par exemple, supposons que vous ayez besoin de récupérer un objet Personne stocké

Person person = (Person) session.get(Person.class, <IDENTIFIER>);

Quel constructeur Hibernate doit-il appeler pour récupérer un objet Personne ? Pouvez-vous voir ?

Enfin, en utilisant la réflexion, Hibernate peut instancier une classe par le biais de son constructeur sans argument. Ainsi, lorsque vous appelez

Person person = (Person) session.get(Person.class, <IDENTIFIER>);

Hibernate instancie votre objet Personne de la manière suivante

Person.class.newInstance();

Ce qui, selon la documentation de l'API

La classe est instanciée comme par une nouveau avec une expression vide liste d'arguments

Morale de l'histoire

Person.class.newInstance();

est similaire à

new Person();

Rien d'autre

2 votes

C'est de loin la plus excellente description que j'ai trouvée sur cette question. La plupart des réponses que j'avais trouvées utilisaient des termes techniques livresques et personne ne l'expliquait de manière souple comme vous l'avez fait. Bravo à vous et merci !

1 votes

Cela pourrait bien être le raisonnement de l'équipe Hibernate. Mais en réalité, les problèmes pourraient être résolus (1) en exigeant soit une annotation, soit l'utilisation d'un constructeur autre que celui par défaut s'il n'y a qu'un seul constructeur et (2) en utilisant class.getDeclaredConstructors. Et en utilisant Constructor.newInstance() au lieu de Class.newInstance(). Un mappage approprié en XML/annotations serait nécessaire, avant Java 8, mais c'est tout à fait faisable.

0 votes

Ok, donc Hibernate crée l'objet à partir du constructeur par défaut, et ensuite il utilise les setters pour les champs. name et age ? Si non, il utilise un autre constructeur plus tard ?

7voto

Pascal Thivent Points 295221

Hibernate a besoin de créer des instances comme résultat de vos requêtes (via la réflexion), Hibernate s'appuie sur le constructeur no-arg des entités pour cela, vous devez donc fournir un constructeur no-arg. Qu'est-ce qui n'est pas clair ?

0 votes

Dans quelles conditions un private incorrect ? Je vois java.lang.InstantiationException même avec un private pour mon entité JPA. référence .

0 votes

J'ai essayé une classe sans constructeur vide (mais avec constructeur args) et cela a fonctionné. J'ai obtenu l'INFO d'hibernate "INFO : HHH000182 : No default (no-argument) constructor for class and class must be instantiated by Interceptor", mais il n'y avait pas d'exception et l'objet a été reçu avec succès de la base de données.

6voto

alex Points 3964

En fait, vous pouvez instancier des classes qui n'ont pas de constructeur 0-args ; vous pouvez obtenir une liste des constructeurs d'une classe, en choisir un et l'invoquer avec de faux paramètres.

Bien que cela soit possible, et je suppose que cela fonctionnerait et ne poserait pas de problème, vous conviendrez que c'est plutôt bizarre.

La construction d'objets comme le fait Hibernate (je crois qu'il invoque le constructeur à 0 argument, puis modifie probablement les champs de l'instance directement via Reflection. Peut-être sait-il comment appeler les setters) va un peu à l'encontre de la façon dont un objet est censé être construit en Java - invoquer le constructeur avec les paramètres appropriés pour que le nouvel objet soit celui que vous voulez. Je pense que l'instanciation d'un objet et sa mutation sont en quelque sorte " anti-Java " (ou je dirais, anti-Java théorique pur) - et il est certain que si vous le faites via la manipulation directe des champs, cela va à l'encontre de l'encapsulation et de tous ces trucs d'encapsulation fantaisistes.

Je pense que la bonne façon de faire serait de définir dans le mapping Hibernate comment un objet devrait être instancié à partir des informations de la ligne de base de données en utilisant le constructeur approprié... mais cela serait plus complexe - ce qui signifie qu'Hibernate serait encore plus complexe, le mapping serait plus complexe... et tout cela pour être plus "pur" ; et je ne pense pas que cela aurait un avantage sur l'approche actuelle (autre que de se sentir bien de faire les choses "de la bonne façon").

Cela dit, et vu que l'approche d'Hibernate n'est pas très "propre", l'obligation d'avoir un constructeur à 0 argument n'est pas strictement nécessaire, mais je peux comprendre quelque peu cette exigence, même si je crois qu'ils l'ont fait pour des raisons purement "appropriées", alors qu'ils se sont éloignés de la "bonne méthode" (bien que pour des raisons raisonnables) bien avant cela.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X