Je suis tombé sur une situation (que je trouve étrange mais qui est peut-être tout à fait normale) où j'utilise EntityManager.getReference (LObj.getClass (), LObj.getId ()) pour obtenir une entité de base de données, puis passer l'objet retourné à être persisté dans une autre table.
Donc, fondamentalement, le flux était comme ceci:
classe TFacade { createT (FObj, AObj) { T TObj = nouveau T (); TObj.setF (FObj); TObj.setA (AObj); ... EntityManager.persist (TObj); ... L LObj = A.getL (); FObj.setL (LObj); FFacade.editF (FObj); } } @ TransactionAttributeType.REQUIRES_NEW classe FFacade { editF (FObj) { L LObj = FObj.getL (); LObj = EntityManager.getReference (LObj.getClass (), LObj.getId ()); ... EntityManager.merge (FObj); ... FLHFacade.create (FObj, LObj); } } @ TransactionAttributeType.REQUIRED classe FLHFacade { createFLH (FObj, LObj) { FLH FLHObj = nouveau FLH (); FLHObj.setF (FObj); FLHObj.setL (LObj); .... EntityManager.persist (FLHObj); ... } }
J'obtenais l'exception suivante "java.lang.IllegalArgumentException: Unknown entity: com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0"
Après l'avoir examiné pendant un moment, j'ai finalement compris que c'était parce que j'utilisais la méthode EntityManager.getReference () que j'obtenais l'exception ci-dessus car la méthode renvoyait un proxy.
Cela me fait me demander, quand est-il conseillé d'utiliser la méthode EntityManager.getReference () au lieu de la méthode EntityManager.find () ?
EntityManager.getReference () lève une EntityNotFoundException s'il ne trouve pas l'entité recherchée, ce qui est très pratique en soi. La méthode EntityManager.find () renvoie simplement null si elle ne trouve pas l'entité.
En ce qui concerne les limites des transactions, il me semble que vous auriez besoin d'utiliser la méthode find () avant de passer la nouvelle entité trouvée à une nouvelle transaction. Si vous utilisez la méthode getReference (), vous vous retrouverez probablement dans une situation similaire à la mienne avec l'exception ci-dessus.
la source
Réponses:
J'utilise généralement la méthode getReference lorsque je n'ai pas besoin d'accéder à l'état de la base de données (je veux dire la méthode getter). Juste pour changer d'état (je veux dire la méthode setter). Comme vous devez le savoir, getReference renvoie un objet proxy qui utilise une fonctionnalité puissante appelée vérification automatique des informations incorrectes. Supposons ce qui suit
Si j'appelle la méthode de recherche , le fournisseur JPA, dans les coulisses, appellera
Si j'appelle la méthode getReference , le fournisseur JPA, dans les coulisses, appellera
Et vous savez pourquoi ???
Lorsque vous appelez getReference, vous obtiendrez un objet proxy. Quelque chose comme celui-ci (le fournisseur JPA se charge de l'implémentation de ce proxy)
Ainsi, avant la validation de la transaction, le fournisseur JPA verra l'indicateur stateChanged afin de mettre à jour OU NON l'entité personne. Si aucune ligne n'est mise à jour après l'instruction de mise à jour, le fournisseur JPA lèvera EntityNotFoundException conformément à la spécification JPA.
Cordialement,
la source
SELECT
avantUPDATE
, peu importe lequel desfind()
/getReference()
j'utilise. Ce qui est pire,SELECT
traverse les relations NON-LAZY (émission de nouvellesSELECTS
), bien que je veuille simplement mettre à jour un seul champ dans une entité.Comme je l'ai expliqué dans cet article , en supposant que vous ayez une
Post
entité parente et un enfant,PostComment
comme illustré dans le diagramme suivant:Si vous appelez
find
lorsque vous essayez de définir l'@ManyToOne
post
association:Hibernate exécutera les instructions suivantes:
La requête SELECT est inutile cette fois car nous n'avons pas besoin de récupérer l'entité Post. Nous voulons uniquement définir la colonne de clé étrangère post_id sous-jacente.
Maintenant, si vous utilisez à la
getReference
place:Cette fois, Hibernate émettra uniquement l'instruction INSERT:
Contrairement à
find
, legetReference
ne renvoie qu'une entité Proxy qui n'a que l'identifiant défini. Si vous accédez au proxy, l'instruction SQL associée sera déclenchée tant que l'EntityManager est toujours ouvert.Cependant, dans ce cas, nous n'avons pas besoin d'accéder à l'entité Proxy. Nous voulons uniquement propager la clé étrangère à l'enregistrement de table sous-jacent, donc le chargement d'un proxy est suffisant pour ce cas d'utilisation.
Lors du chargement d'un proxy, vous devez être conscient qu'une exception LazyInitializationException peut être levée si vous essayez d'accéder à la référence du proxy après la fermeture de EntityManager. Pour plus de détails sur la gestion de
LazyInitializationException
, consultez cet article .la source
hibernate.jpa.compliance.proxy
propriété de configuration , vous pouvez donc choisir la conformité JPA ou de meilleures performances d'accès aux données.getReference
est nécessaire du tout s'il suffit juste de définir une nouvelle instance de modèle avec PK set. Qu'est-ce que je rate?Comme une référence est «gérée», mais pas hydratée, elle peut également vous permettre de supprimer une entité par ID, sans avoir besoin de la charger en mémoire au préalable.
Comme vous ne pouvez pas supprimer une entité non gérée, il est tout simplement ridicule de charger tous les champs en utilisant find (...) ou createQuery (...), uniquement pour le supprimer immédiatement.
la source
EntityManager.getReference()
est vraiment une méthode sujette aux erreurs et il y a vraiment très peu de cas où un code client a besoin de l'utiliser.Personnellement, je n'ai jamais eu besoin de l'utiliser.
EntityManager.getReference () et EntityManager.find (): pas de différence en termes de surcharge
Je ne suis pas d'accord avec la réponse acceptée et en particulier:
Ce n'est pas le comportement que j'obtiens avec Hibernate 5 et le javadoc de
getReference()
ne dit pas une telle chose:EntityManager.getReference()
épargne une requête pour récupérer l'entité dans deux cas:1) si l'entité est stockée dans le contexte de persistance, c'est le cache de premier niveau.
Et ce comportement n'est pas spécifique à
EntityManager.getReference()
,EntityManager.find()
épargnera également une requête pour récupérer l'entité si l'entité est stockée dans le contexte de persistance.Vous pouvez vérifier le premier point avec n'importe quel exemple.
Vous pouvez également vous fier à l'implémentation réelle d'Hibernate.
En effet,
EntityManager.getReference()
s'appuie sur lacreateProxyIfNecessary()
méthode de laorg.hibernate.event.internal.DefaultLoadEventListener
classe pour charger l'entité.Voici sa mise en œuvre:
La partie intéressante est:
2) Si nous ne manipulons pas efficacement l'entité, en écho à la récupération paresseuse du javadoc.
En effet, pour assurer le chargement efficace de l'entité, il est nécessaire d'invoquer une méthode sur celle-ci.
Le gain serait donc lié à un scénario où l'on souhaite charger une entité sans avoir besoin de l'utiliser? Dans le cadre des applications, ce besoin est vraiment rare et en plus le
getReference()
comportement est également très trompeur si vous lisez la partie suivante.Pourquoi privilégier EntityManager.find () à EntityManager.getReference ()
En termes de frais généraux, ce
getReference()
n'est pas mieux quefind()
comme discuté au point précédent.Alors pourquoi utiliser l'un ou l'autre?
L'appel
getReference()
peut renvoyer une entité extraite paresseusement.Ici, la récupération paresseuse ne fait pas référence aux relations de l'entité mais à l'entité elle-même.
Cela signifie que si nous invoquons
getReference()
et que le contexte de persistance est fermé, l'entité peut ne jamais être chargée et le résultat est donc vraiment imprévisible. Par exemple, si l'objet proxy est sérialisé, vous pouvez obtenir unenull
référence en tant que résultat sérialisé ou si une méthode est appelée sur l'objet proxy, une exception telle queLazyInitializationException
levée.Cela signifie que le rejet de
EntityNotFoundException
cela est la principale raison à utilisergetReference()
pour gérer une instance qui n'existe pas dans la base de données, car une situation d'erreur peut ne jamais être effectuée alors que l'entité n'existe pas.EntityManager.find()
n'a pas l'ambition de jeterEntityNotFoundException
si l'entité n'est pas trouvée. Son comportement est à la fois simple et clair. Vous n'aurez jamais de surprise car elle renvoie toujours une entité chargée ounull
(si l'entité n'est pas trouvée) mais jamais une entité sous la forme d'un proxy qui risque de ne pas être chargée efficacement.Il
EntityManager.find()
devrait donc être favorisé dans la plupart des cas.la source
Je ne suis pas d'accord avec la réponse sélectionnée, et comme davidxxx l'a correctement souligné, getReference ne fournit pas un tel comportement de mises à jour dynamiques sans select. J'ai posé une question concernant la validité de cette réponse, voir ici - ne peut pas mettre à jour sans émettre select on using setter après getReference () of hibernate JPA .
Honnêtement, je n'ai vu personne qui ait réellement utilisé cette fonctionnalité. NULLE PART. Et je ne comprends pas pourquoi il est si voté.
Maintenant tout d'abord, peu importe ce que vous appelez sur un objet proxy d'hibernation, un setter ou un getter, un SQL est déclenché et l'objet est chargé.
Mais alors j'ai pensé, alors que se passe-t-il si le proxy JPA getReference () ne fournit pas cette fonctionnalité. Je peux juste écrire mon propre proxy.
Maintenant, nous pouvons tous affirmer que les sélections sur les clés primaires sont aussi rapides qu'une requête peut être obtenue et que ce n'est pas vraiment quelque chose à éviter. Mais pour ceux d'entre nous qui ne peuvent pas le gérer pour une raison ou une autre, vous trouverez ci-dessous une implémentation d'un tel proxy. Mais avant de voir l'implémentation, voyez son utilisation et sa simplicité d'utilisation.
USAGE
Et cela déclencherait la requête suivante -
et même si vous souhaitez insérer, vous pouvez toujours faire PersistenceService.save (new Order ("a", 2)); et il déclencherait un insert comme il se doit.
LA MISE EN OEUVRE
Ajoutez ceci à votre pom.xml -
Faites de cette classe pour créer un proxy dynamique -
Créez une interface avec toutes les méthodes -
Maintenant, faites un intercepteur qui vous permettra d'implémenter ces méthodes sur votre proxy -
Et la classe d'exception -
Un service à sauvegarder en utilisant ce proxy -
la source