Le constructeur sans argument est une exigence (des outils comme Hibernate utilisent la réflexion sur ce constructeur pour instancier des objets).
J'ai eu cette réponse ondulée mais quelqu'un pourrait-il expliquer davantage? Merci
Le constructeur sans argument est une exigence (des outils comme Hibernate utilisent la réflexion sur ce constructeur pour instancier des objets).
J'ai eu cette réponse ondulée mais quelqu'un pourrait-il expliquer davantage? Merci
Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student
comme dans le cas où je viens de rencontrer
Réponses:
Hibernate, et le code en général qui crée des objets via la réflexion utilise Class<T>.newInstance()
pour créer une nouvelle instance de vos classes. Cette méthode nécessite un constructeur public sans argument pour pouvoir instancier l'objet. Pour la plupart des cas d'utilisation, fournir un constructeur sans argument n'est pas un problème.
Il existe des hacks basés sur la sérialisation qui peuvent contourner le fait de ne pas avoir de constructeur sans argument, car la sérialisation utilise la magie jvm pour créer des objets sans appeler le constructeur. Mais ce n'est pas disponible sur toutes les VM. Par exemple, XStream peut créer des instances d'objets qui n'ont pas de constructeur public no-arg, mais uniquement 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.) Les concepteurs d'Hibernate ont sûrement choisi de maintenir la compatibilité avec toutes les machines virtuelles et évite ainsi de telles astuces, et utilise la méthode de réflexion officiellement prise en charge Class<T>.newInstance()
nécessitant un constructeur sans argument.
setAccessible(true)
.
ObjectInputStream
fait quelque chose comme sun.reflect.ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToGetInstanceOf, Object.class.getConstructor()).newInstance()
pour instancier des objets sans constructeur par défaut (JDK1.6 pour Windows)
It can have package visibility and Hibernate should setAccessible(true)
. Cela it
signifie- t - il que la classe est instanciée par réflexion? Et qu'est-ce que ça Hibernate should setAccessible(true)
veut dire?
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 l' instancier, c'est-à-dire quel argument passer.
La documentation de mise en veille prolongée 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) pour qu'Hibernate puisse les instancier en utilisant Constructor.newInstance()
. Il est recommandé d'avoir un constructeur par défaut avec au moins une visibilité sur les packages pour la génération de proxy d'exécution dans Hibernate.
Euh, désolé tout le monde, mais Hibernate n'exige pas que vos classes aient un constructeur sans paramètre. La spécification JPA 2.0 exige, et c'est très boiteux de la part de JPA. D'autres frameworks comme JAXB l'exigent également, ce qui est également très boiteux de la part de ces frameworks.
(En fait, JAXB autorise soi-disant les usines d'entités, mais il insiste pour instancier ces usines par lui-même, en leur demandant d'avoir un - devinez quoi-- constructeur sans paramètre , ce qui dans mon livre est exactement aussi bon que de ne pas autoriser les usines; à quel point est-ce que !)
Mais Hibernate n'exige pas une telle chose.
Hibernate prend en charge un mécanisme d'interception, (voir «Intercepteur» dans la documentation ,) qui vous permet d'instancier vos objets avec les paramètres de constructeur dont ils ont besoin.
Fondamentalement, ce que vous faites est que lorsque vous configurez la mise en veille prolongée, vous lui transmettez un objet implémentant l' org.hibernate.Interceptor
interface, et la mise en veille prolongée appellera alors la instantiate()
méthode de cette interface chaque fois qu'elle a besoin d'une nouvelle instance d'un de vos objets, de sorte que votre implémentation de cette méthode peut new
vos objets comme vous le souhaitez.
Je l'ai fait dans un projet et cela fonctionne comme un charme. Dans ce projet, je fais les choses via JPA chaque fois que c'est possible, et je n'utilise les fonctionnalités Hibernate comme l'intercepteur que lorsque je n'ai pas d'autre option.
Hibernate semble être quelque peu incertain à ce sujet, car au démarrage, il émet un message d'information pour chacune de mes classes d'entité, me disant INFO: HHH000182: No default (no-argument) constructor for class
etclass must be instantiated by Interceptor
, mais plus tard, je les instancie par intercepteur, et il en est satisfait.
Pour répondre à la partie «pourquoi» de la question pour les outils autres qu'Hibernate , la réponse est «sans aucune raison valable», et cela est prouvé par l'existence de l'intercepteur d'hibernation. Il existe de nombreux outils qui auraient pu prendre en charge un mécanisme similaire pour l'instanciation d'objet client, mais ils ne le font pas, ils créent donc les objets eux-mêmes, ils doivent donc nécessiter des constructeurs sans paramètre. Je suis tenté de croire que cela se produit parce que les créateurs de ces outils se considèrent comme des programmeurs de systèmes ninja qui créent des cadres pleins de magie à utiliser par des programmeurs d'applications ignorants, qui (selon eux) n'auraient jamais dans leurs rêves les plus fous besoin de constructions avancées comme le ... Factory Pattern . (D'accord,penser ainsi. Je ne pense pas vraiment . Je plaisante.)
Caused by: org.hibernate.InstantiationException: No default constructor for entity: : hibernate.tutorial.Student
qui s'est produit récemment car il javax.persistence.*;
est utilisé et uniquement org.hibernate
lors de la création duSession, SessionFactory, and Configuration
La mise en veille prolongée est un framework 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 le constructeur - peut-être que vous aimeriez? - à cause de certains problèmes comme
1º 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 voir, vous gérez un problème d'incohérence car Hibernate ne peut pas supposer quel constructeur doit être appelé. Par exemple, supposons que vous ayez besoin de récupérer un objet Person stocké
Person person = (Person) session.get(Person.class, <IDENTIFIER>);
Quel constructeur doit appeler Hibernate pour récupérer un objet Person? Peux tu voir ?
2º Et enfin, en utilisant la réflexion, Hibernate peut instancier une classe via son constructeur sans argument. Alors quand tu appelles
Person person = (Person) session.get(Person.class, <IDENTIFIER>);
Hibernate instanciera votre objet Person comme suit
Person.class.newInstance();
Qui selon la documentation de l'API
La classe est instanciée comme par une nouvelle expression avec une liste d'arguments vide
Morale de l'histoire
Person.class.newInstance();
est similaire à
new Person();
Rien d'autre
name
et age
? Sinon, il utilise un autre constructeur plus tard?
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 serait pas problématique, vous devrez convenir que c'est assez étrange.
Construire des objets comme le fait Hibernate (je crois qu'il invoque le constructeur 0-arg, puis il modifie probablement les champs de l'instance directement via Reflection. Peut-être qu'il sait comment appeler des setters) va un peu à l'encontre de la façon dont un objet est censé être construit dans Java - invoquez le constructeur avec les paramètres appropriés pour que le nouvel objet soit l'objet souhaité. Je crois qu'instancier un objet puis le muter est un peu "anti-Java" (ou je dirais, anti pur Java théorique) - et certainement, si vous faites cela via une manipulation directe de champ, cela passe par l'encapsulation et tout ce truc d'encapsulation sophistiqué .
Je pense que la bonne façon de faire cela serait de définir dans le mappage Hibernate comment un objet devrait être instancié à partir des informations de la ligne de base de données en utilisant le constructeur approprié ... mais ce serait plus complexe - ce qui signifie que les deux Hibernate seraient même plus complexe, la cartographie 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 se sentir bien de faire les choses "de la bonne manière").
Cela dit, et vu que l'approche Hibernate 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, même si je pense qu'ils l'ont fait de manière purement "correcte "motifs, lorsqu'ils se sont écartés de la" bonne voie "(quoique pour des raisons raisonnables) bien avant cela.
Hibernate doit créer des instances à la suite de vos requêtes (via la réflexion), Hibernate s'appuie sur le constructeur d'entités no-arg pour cela, vous devez donc fournir un constructeur no-arg. Qu'est-ce qui n'est pas clair?
private
constructeur est-il incorrect? Je vois java.lang.InstantiationException
même avec un private
constructeur pour mon entité JPA. référence .
Il est beaucoup plus facile de créer un objet avec un constructeur sans paramètre par réflexion, puis de remplir ses propriétés de données par réflexion, que d'essayer de faire correspondre des données à des paramètres arbitraires d'un constructeur paramétré, avec des changements de noms / conflits de dénomination, une logique non définie à l'intérieur du constructeur, ensembles de paramètres ne correspondant pas aux propriétés d'un objet, et cetera.
De nombreux ORM et sérialiseurs nécessitent des constructeurs sans paramètre, car les constructeurs paramétrés par réflexion sont très fragiles et les constructeurs sans paramètre fournissent à la fois la stabilité de l'application et le contrôle du comportement de l'objet pour le développeur.
Hibernate utilise des proxies pour le chargement différé. Si vous ne définissez pas de constructeur ou ne le rendez pas privé, certaines choses peuvent toujours fonctionner - celles qui ne dépendent pas du mécanisme de proxy. Par exemple, charger l'objet (sans constructeur) directement à l'aide de l'API de requête.
Mais, si vous utilisez la méthode session.load (), vous serez confronté à InstantiationException de la bibliothèque du générateur de proxy en raison de la non-disponibilité du constructeur.
Ce type a signalé 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 classe interne statique n'est pas différente du point de vue conceptuel d'une classe générale ordinaire déclarée dans un fichier .java.
Étant donné qu'Hibernate doit instancier ProjectPK indépendamment de l'instance Project, ProjectPK doit soit être une classe interne statique, soit être déclaré dans son propre fichier .java.
reference org.hibernate.InstantiationException: aucun constructeur par défaut
The no-argument constructor is a requirement
est fausse , et toutes les réponses qui continuent à expliquer pourquoi il en est ainsi sans se demander s'il en est effectivement ainsi (y compris la réponse acceptée, qui a même reçu une prime,) sont fausses . Voir cette réponse: stackoverflow.com/a/29433238/773113