Pourquoi Hibernate ne nécessite aucun argument constructeur?
Le constructeur sans argument est un exigences (outils comme L'hibernation) la réflexion sur ce constructeur instancier des objets).
j'ai eu cette réponse ondulée, mais quelqu'un pourrait-il m'en dire plus? Merci
9 réponses
hibernation, et code en général qui crée des objets par réflexion utilisez Class<T>.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 sans arg n'est pas un problème.
il y a des hacks basés sur la sérialisation qui peuvent fonctionner sans avoir de constructeur no-arg, puisque la sérialisation utilise la magie jvm pour créer des objets sans invoquer le constructeur. Mais ce n'est pas disponible dans toutes les MV. Par exemple, XStream peut créer des instances d'objets qui n'ont pas de constructeur public no-arg, mais seulement en tournant dans un mode dit" amélioré " qui n'est disponible que sur certaines VM. (Voir le lien pour plus de détails.) Les concepteurs de Hibernate ont certainement choisi de maintenir la compatibilité avec tous les Vm et donc d'éviter de telles astuces, et utilise la méthode de réflexion officiellement soutenue Class<T>.newInstance()
nécessitant une pas-arg constructeur.
Hibernate instancie vos objets. Il doit donc être en mesure de les instancier. S'il n'y a pas de constructeur no-arg, Hibernate ne saura pas comment pour l'instancier, c'est-à-dire quel argument passer.
Le hibernate documentation dit:
4.1.1. Implémenter un constructeur sans argument
toutes les classes persistantes doivent avoir une valeur par défaut constructeur (qui peut être non-public) afin que Hibernate puisse les instancier en utilisant Constructor.newInstance()
. Il est recommandé d'avoir un constructeur par défaut avec au moins une visibilité de paquet pour la génération proxy d'exécution en hibernation.
hibernate est un ORM qui prend en charge de champ ou de l'accès à la propriété de la stratégie. Cependant, il ne supporte pas la cartographie basée sur le constructeur-peut - être ce que vous aimeriez ? - en raison de certaines questions comme
1º Ce qui se passe si votre classe contient un grand nombre 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 voir, vous avez affaire à une question d'incohérence parce que Hibernate ne peut pas supposer quel constructeur devrait ê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 devrait appeler pour récupérer un objet personne ? Pouvez-vous voir ?
2º et enfin, en utilisant la réflexion, Hibernate peut instancier une classe à travers son constructeur no-arg. Donc quand vous appelez
Person person = (Person) session.get(Person.class, <IDENTIFIER>);
Hibernate va instancier votre Personne objet comme suit:
Person.class.newInstance();
qui selon la documentation API
la classe est instanciée comme si par une expression new avec une expression empty argument list
Morale de l'histoire
Person.class.newInstance();
est similaire à
new Person();
Rien d'autre
Erm, désolé tout le monde, mais Hibernate ne pas exiger que vos classes doivent avoir un constructeur sans paramètres. Le spécification JPA 2.0 l'exige, et c'est très boiteux au nom de JPA. D'autres cadres comme JAXB l'exigent également, ce qui est également très boiteux au nom de ces cadres.
(en fait, JAXB est supposé autoriser les usines entity, mais il insiste pour instancier ces usines par elles-mêmes , leur demandant d'avoir un --devinez quoi-- constructeur sans paramètres , qui dans mon livre est exactement aussi bon que de ne pas permettre les usines; comme c'est boiteux!)
mais L'hibernation ne nécessite pas une telle chose.
Hibernate supporte un mécanisme d'interception, (voir "Interceptor" dans la documentation ,) qui vous permet d'instancier vos objets avec n'importe quel constructeur les paramètres dont ils ont besoin.
fondamentalement, ce que vous faites est que lorsque vous mettez en place hibernate vous passer un objet mettant en œuvre l'interface org.hibernate.Interceptor
, et hibernate sera alors invoquer la méthode instantiate()
de cette interface chaque fois qu'il a besoin d'une nouvelle instance d'un objet de la vôtre, de sorte que votre mise en œuvre de cette méthode peut new
vos objets de quelque manière que vous le souhaitez.
je l'ai fait dans un projet et il fonctionne comme un charme. Dans ce projet que je fais les choses par JPA chaque fois que possible, et je ne l'utilise Hibernate fonctionnalités comme l'intercepteur quand je n'ai pas d'autre option.
Hibernate semble être quelque peu incertain à ce sujet, car pendant le démarrage il émet un message d'information pour chacune de mes classes d'entités , me disant INFO: HHH000182: No default (no-argument) constructor for class
et class must be instantiated by Interceptor
, mais plus tard je les instancie par interceptor, et il est heureux avec cela.
pour répondre à la partie" pourquoi " de la question pour les outils mis à part L'hibernation , la réponse est "pour absolument aucune bonne raison", et cela est prouvé par l'existence de l'intercepteur d'hibernation. Il existe de nombreux outils qui auraient pu supporter un mécanisme similaire pour l'instanciation des objets clients, mais ils ne le font pas, donc ils créent les objets eux-mêmes, donc ils doivent avoir besoin de constructeurs sans paramètres. Je suis tenté de croire que cela se produit parce que les créateurs de ces outils se considèrent comme programmeurs de systèmes ninja qui créent des cadres pleins de magie pour être utilisés par des programmeurs d'application ignorants, qui (si ils pensent) ne serait jamais dans leurs rêves les plus fous ont un besoin pour des constructions aussi avancées que le... Usine Modèle . (OK, je suis tenté de le penser. Je ne pas vraiment le pense. Je plaisante.)
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 des paramètres faux.
bien que ce soit possible, et je suppose que cela fonctionnerait et ne serait pas problématique, vous devrez être d'accord que c'est assez bizarre.
construire des objets comme Hibernate le fait (je crois qu'il invoque le constructeur 0-arg et puis il modifie probablement les champs de l'instance directement via la réflexion. Peut - être sait-il comment appeler setters) va un peu à l'encontre de comment un objet est censé être construit en Java-invoquer le constructeur avec les paramètres appropriés de sorte que le nouvel objet est l'objet que vous voulez. Je crois que l'instanciation d'un objet puis sa mutation est quelque peu "Anti-Java" (ou, je dirais, Anti pure Java théorique)- et certainement, si vous faites cela via la manipulation directe sur le terrain, il va encapsulation et tous ces trucs de fantaisie encapsulation.
je pense que la bonne façon de faire cela serait de définir dans la cartographie Hibernate comment un objet doit être instancié à partir des informations dans la rangée de base de données en utilisant le constructeur approprié... mais cela serait plus complexe-ce qui signifie que les deux hibernation serait encore plus complexe, la cartographie serait plus complexe... et tout cela pour être plus "pur"; et je ne pense pas que cela aurait un avantage par rapport à l'approche actuelle (autre que se sentir bien de faire les choses "de la bonne façon").
cela dit, et vu que L'approche D'hibernation n'est pas très "propre", l'obligation d'avoir un constructeur 0-arg n'est pas strictement nécessaire, mais je peux comprendre quelque peu l'exigence, bien que je crois qu'ils l'ont fait sur des motifs purement "de la bonne manière", quand ils se sont écartés de la "bonne manière" (bien que pour des raisons raisonnables) beaucoup avant cela.
Hibernate a besoin de créer des instances à la suite de vos requêtes (via la réflexion), Hibernate s'appuie sur le constructeur no-arg des entities pour cela, vous devez donc fournir un constructeur no-arg. Ce n'est pas clair?
il est beaucoup plus facile de créer un objet avec un constructeur sans paramètres par réflexion, puis de remplir ses propriétés avec des données par réflexion, que d'essayer d'apparier des données à des paramètres arbitraires d'un constructeur paramétré, avec des noms changeants/conflits de noms, une logique non définie à l'intérieur du constructeur, des ensembles de paramètres ne correspondant pas aux propriétés d'un objet, et cetera.
de nombreux ORM et sérialisateurs nécessitent des constructeurs sans paramètres, parce que paramétrisé les constructeurs par réflexion sont très fragiles, et les constructeurs sans paramètres offrent à la fois la stabilité de l'application et le contrôle du comportement de l'objet pour le développeur.
Hibernate utilise des mandataires pour le chargement paresseux. Si vous ne définissez pas un constructeur ou si vous le rendez privé, certaines choses peuvent encore fonctionner - celles qui ne dépendent pas du mécanisme de proxy. Par exemple, charger l'objet (sans constructeur) directement en utilisant l'API de requête.
mais, si vous utilisez session.méthode load () vous allez faire face InstantiationException de proxy generator lib en raison de la non-disponibilité du constructeur.
ce type a rapporté une situation similaire:
http://kristian-domagala.blogspot.com/2008/10/proxy-instantiation-problem-from.html
consultez cette section de la spécification du langage Java qui explique la différence entre les classes internes statiques et non statiques: http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3
Une statique à l'intérieur de la classe est conceptuellement différente de celle d'une générale ordinaire de la classe déclarée dans une .fichier java.
puisque Hibernate doit instancier ProjectPK indépendamment de L'instance du projet, ProjectPK soit doit être une classe intérieure statique, ou déclaré dans son propre .fichier java.
référence org.hiberner.InstantiationException: pas de constructeur par défaut