web-dev-qa-db-fra.com

Pourquoi Hibernate ne nécessite-t-il aucun constructeur d'arguments?

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 main-ondulée, mais quelqu'un pourrait-il expliquer davantage? Merci

90
unj2

Hibernate et le code en général qui crée des objets via une réflexion utilisent 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 éviter de ne pas avoir de constructeur sans argument, car la sérialisation utilise jvm magic pour créer des objets sans invoquer le constructeur. Mais ce n'est pas disponible sur tous les ordinateurs virtuels. 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é" disponible uniquement sur certaines machines virtuelles. (Voir le lien pour plus de détails.) Les concepteurs d'Hibernate ont certainement choisi de maintenir la compatibilité avec tous les ordinateurs virtuels, évitant ainsi de telles astuces et utilisant la méthode de réflexion officiellement prise en charge Class<T>.newInstance() nécessitant un constructeur sans argument.

125
mdma

Hibernate instancie vos objets. Il faut donc pouvoir les instancier. S'il n'y a pas de constructeur sans argument, Hibernate ne saura pas comment l'instancier, c'est-à-dire quel argument passer.

Le documentation d'hibernation dit:

4.1.1. Implémenter un constructeur sans argument

Toutes les classes persistantes doivent avoir un constructeur par défaut (qui peut ne pas être public) pour qu'Hibernate puisse les instancier avec Constructor.newInstance(). Il est recommandé de disposer d'un constructeur par défaut offrant au moins une visibilité sur les packages pour la génération de proxy d'exécution dans Hibernate.

44
Bozho

Euh, désolé de tout le monde, mais Hibernate exige pas que vos classes doivent avoir un constructeur sans paramètre. Le spécification JPA 2. en a besoin, ce qui est très boiteux de la part de JPA. D'autres frameworks tels que 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 --inatif what-- constructeur sans paramètre, ce qui dans mon livre est exactement aussi bon comme ne permettant pas les usines; comme c'est nul!

Mais Hibernate n’exige pas une telle chose.

Hibernate supporte un mécanisme d'interception (voir "Interceptor" dans la documentation ,) qui vous permet d'instancier vos objets avec les paramètres de constructeur dont ils ont besoin.

En gros, ce que vous faites est que lorsque vous configurez hibernate, vous transmettez un objet implémentant l'interface org.hibernate.Interceptor. Hibernate invoquera alors la méthode instantiate() de cette interface chaque fois qu'elle aura besoin d'une nouvelle instance. d’un de vos objets, ainsi votre implémentation de cette méthode peut new vos objets de la manière qui vous convient.

Je l'ai fait dans un projet et cela fonctionne comme un charme. Dans le cadre de ce projet, je fais des choses via JPA chaque fois que cela est possible et j'utilise uniquement les fonctionnalités d'Hibernate telles que l'intercepteur quand je n'ai pas d'autre option.

Hibernate semble quelque peu incertain à ce sujet, car lors du démarrage, il envoie un message d’information à chacune de mes classes d’entités, en me disant INFO: HHH000182: No default (no-argument) constructor for class et class must be instantiated by Interceptor, Mais je les instancie plus tard par intercepteur, et il est heureux avec cela.

Pour répondre à la partie "pourquoi" de la question concernant les outils autres que Hibernate, la réponse est "pour absolument aucune bonne raison", comme le prouve l'existence de l'intercepteur Hibernate. Il existe de nombreux outils qui auraient pu prendre en charge un mécanisme similaire d'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 magiques à l’usage de programmeurs d’applications ignorants qui, selon eux, n’auraient jamais dans leurs rêves les plus fous une besoin de constructions aussi avancées que le ... modèle d'usine . (Ok, je suis tenté de le penser. Je ne pense pas en fait. Je plaisante.)

36
Mike Nakis

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 le constructeur - peut-être ce que vous souhaitez? - à cause de 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 traitez 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 Personne stocké.

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

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

Et finalement, 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 Personne comme suit

Person.class.newInstance();

Selon la documentation de l'API

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

Morale de l'histoire

Person.class.newInstance();

est similaire à

new Person();

Rien d'autre

33
Arthur Ronald

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 devrez admettre que c'est assez étrange.

Construire des objets comme Hibernate le fait (je crois qu'il appelle le constructeur 0-arg et ensuite il modifie probablement les champs de l'instance directement via Reflection. Peut-être sait-il comment appeler les setters) va un peu contre la manière dont un objet est censé être construit Java: invoquez le constructeur avec les paramètres appropriés pour que le nouvel objet soit celui que vous souhaitez. Je pense qu'instancier un objet puis le muter est un peu "anti-Java" (ou plutôt anti-Java théorique pur) - et définitivement, si vous le faites via une manipulation de champ directe, cela passe par l'encapsulation et tout ce qui est fantaisiste. .

Je pense que la bonne façon de faire serait de définir dans le mappage Hibernate comment un objet devrait être instancié à partir des informations de la rangée de la base de données à l'aide du constructeur approprié ... mais cela serait plus complexe, ce qui signifie que les deux Hibernate seraient même plus complexe, la cartographie serait plus complexe ... et tout serait plus "pur"; et je ne pense pas que cela aurait un avantage par rapport à l'approche actuelle (mis à part le fait de bien 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 de disposer d’un constructeur 0-arg n’est pas strictement nécessaire, mais je peux comprendre un peu l’exigence, bien que je crois qu’ils l’ont fait de manière "convenable" "motifs, quand ils se sont écartés de la" voie appropriée "(bien que pour des raisons raisonnables) beaucoup avant cela.

6
alex

Hibernate doit créer des instances à la suite de vos requêtes (via une réflexion). Hibernate s'appuie sur le constructeur d'entités sans argument pour le faire, vous devez donc fournir un constructeur sans argument. Qu'est-ce qui n'est pas clair?

5
Pascal Thivent

Il est beaucoup plus facile de créer un objet avec un constructeur sans paramètre par réflexion, puis de renseigner ses propriétés avec des données par réflexion, que d'essayer de faire correspondre les données à des paramètres arbitraires d'un constructeur paramétré, en modifiant les noms/conflits de noms, la logique non définie dans le constructeur, jeux 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 offrent à la fois une stabilité à l'application et un contrôle du comportement de l'objet au développeur.

2
Kaerber

Hibernate utilise des mandataires pour le chargement paresseux. Si vous ne définissez pas de constructeur ou ne le rendez pas 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 à l'aide de l'API de requête.

Toutefois, si vous utilisez la méthode session.load (), vous rencontrerez InstantiationException du générateur de proxy lib en raison de la non-disponibilité du constructeur.

Ce gars a rapporté une situation similaire:

http://kristian-domagala.blogspot.com/2008/10/proxy-instantiation-problem-from.html

1
haps10

Consultez cette section de la 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. =

Sur le plan conceptuel, une classe interne statique n'est pas différente d'une classe générale régulière déclarée dans un fichier .Java.

Etant donné qu'Hibernate doit instancier ProjectPK indépendamment de l'instance de projet, ProjectPK doit être une classe interne statique ou être déclaré dans son propre fichier .Java.

référence org.hibernate.InstantiationException: aucun constructeur par défaut

0
Amitābha