J'ai un cas d'utilisation où il appelle ce qui suit:
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl getUserControlById(Integer id){
return this.userControlRepository.getOne(id);
}
Observez le @Transactional
has Propagation.REQUIRES_NEW et le référentiel utilise getOne . Lorsque j'exécute l'application, je reçois le message d'erreur suivant:
Exception in thread "main" org.hibernate.LazyInitializationException:
could not initialize proxy - no Session
...
Mais si je change le getOne(id)
par findOne(id)
tout va bien de travaux.
BTW, juste avant que le cas d'utilisation n'appelle la méthode getUserControlById , il a déjà appelé la méthode insertUserControl
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl insertUserControl(UserControl userControl) {
return this.userControlRepository.save(userControl);
}
Les deux méthodes sont Propagation.REQUIRES_NEW car je fais un simple contrôle d' audit .
J'utilise la getOne
méthode car elle est définie dans l' interface JpaRepository et mon interface Repository s'étend à partir de là, je travaille avec JPA bien sûr.
L' interface JpaRepository s'étend de CrudRepository . La findOne(id)
méthode est définie dans CrudRepository
.
Mes questions sont:
- Pourquoi échouer la
getOne(id)
méthode? - Quand dois-je utiliser la
getOne(id)
méthode?
Je travaille avec d'autres référentiels et tous utilisent la getOne(id)
méthode et tout fonctionne bien, uniquement lorsque j'utilise Propagation.REQUIRES_NEW, cela échoue.
Selon l' API getOne :
Renvoie une référence à l'entité avec l'identifiant donné.
Selon l' API findOne :
Récupère une entité par son identifiant.
3) Quand dois-je utiliser la findOne(id)
méthode?
4) Quelle méthode est recommandée pour être utilisée?
Merci d'avance.
la source
Réponses:
TL; DR
T findOne(ID id)
(nom dans l'ancienne API) /Optional<T> findById(ID id)
(nom dans la nouvelle API) s'appuie surEntityManager.find()
qui effectue un chargement impatient d'entité .T getOne(ID id)
s'appuie surEntityManager.getReference()
qui effectue un chargement paresseux d'entité . Donc, pour assurer le chargement efficace de l'entité, il est nécessaire d'invoquer une méthode dessus.findOne()/findById()
est vraiment plus clair et simple à utiliser quegetOne()
.Ainsi , dans la plupart des cas très, favoriser
findOne()/findById()
plusgetOne()
.Changement d'API
Du moins, la
2.0
version,Spring-Data-Jpa
modifiéefindOne()
.Auparavant, il était défini dans l'
CrudRepository
interface comme:Maintenant, la seule
findOne()
méthode que vous trouverez dansCrudRepository
est celle définie dans l'QueryByExampleExecutor
interface comme:Cela est finalement implémenté par
SimpleJpaRepository
l'implémentation par défaut de l'CrudRepository
interface.Cette méthode est une recherche par exemple et vous ne voulez pas qu'elle remplace.
En fait, la méthode ayant le même comportement est toujours présente dans la nouvelle API mais le nom de la méthode a changé.
Il a été renommé de
findOne()
àfindById()
dans l'CrudRepository
interface:Maintenant, il renvoie un
Optional
. Ce qui n'est pas si mal à éviterNullPointerException
.Ainsi, le choix réel se situe désormais entre
Optional<T> findById(ID id)
etT getOne(ID id)
.Deux méthodes distinctes qui reposent sur deux méthodes de récupération JPA EntityManager distinctes
1) Le
Optional<T> findById(ID id)
javadoc déclare qu'il:En examinant l'implémentation, nous pouvons voir qu'elle s'appuie sur
EntityManager.find()
la récupération:Et voici
em.find()
uneEntityManager
méthode déclarée comme:Son javadoc déclare:
Ainsi, la récupération d'une entité chargée semble attendue.
2) Alors que les
T getOne(ID id)
javadoc états (souligné dans l' original ):En fait, la terminologie de référence est vraiment board et l'API JPA ne spécifie aucune
getOne()
méthode.Donc, la meilleure chose à faire pour comprendre ce que fait le wrapper Spring est d'examiner l'implémentation:
Voici
em.getReference()
uneEntityManager
méthode déclarée comme:Et heureusement, le
EntityManager
javadoc a mieux défini son intention (c'est moi qui souligne):Ainsi, l'invocation
getOne()
peut retourner 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 appelons
getOne()
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 une
null
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.Donc, dans ce genre de situation, le rejet de
EntityNotFoundException
cela est la principale raison à utilisergetOne()
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.Dans tous les cas, pour assurer son chargement, vous devez manipuler l'entité pendant que la session est ouverte. Vous pouvez le faire en appelant n'importe quelle méthode sur l'entité.
Ou une meilleure utilisation alternative
findById(ID id)
au lieu de.Pourquoi une API si peu claire?
Pour finir, deux questions pour les développeurs Spring-Data-JPA:
pourquoi ne pas avoir une documentation plus claire pour
getOne()
? Le chargement paresseux d'entité n'est vraiment pas un détail.pourquoi avez-vous besoin de présenter
getOne()
pour envelopperEM.getReference()
?Pourquoi ne pas simplement coller à la méthode enveloppée:
getReference()
? Cette méthode EM est vraiment très particulière tout engetOne()
véhiculant un traitement si simple.la source
getOne()
utilise le chargement paresseux et lance unEntityNotFoundException
si aucun élément n'est trouvé.findById()
se charge tout de suite, et renvoie null s'il n'est pas trouvé. Puisqu'il y a des situations imprévisibles avec getOne (), il est recommandé d'utiliser findById () à la place.La différence fondamentale est qu'elle
getOne
est chargée paresseusement etfindOne
ne l'est pas.Prenons l'exemple suivant:
la source
1. Pourquoi la méthode getOne (id) échoue-t-elle?
Consultez cette section dans la documentation . Le remplacement de la transaction déjà en place peut être à l'origine du problème. Cependant, sans plus d'informations, il est difficile de répondre.
2. Quand dois-je utiliser la méthode getOne (id)?
Sans creuser dans les éléments internes de Spring Data JPA, la différence semble résider dans le mécanisme utilisé pour récupérer l'entité.
Si vous regardez le JavaDoc pour
getOne(ID)
sous Voir aussi :il semble que cette méthode délègue simplement à l'implémentation du gestionnaire d'entité JPA.
Cependant, les documents pour
findOne(ID)
ne le mentionnent pas.L'indice se trouve également dans les noms des référentiels.
JpaRepository
est spécifique à JPA et peut donc déléguer des appels au gestionnaire d'entités si nécessaire.CrudRepository
est indépendant de la technologie de persistance utilisée. Regardez ici . Il est utilisé comme interface de marqueur pour plusieurs technologies de persistance telles que JPA, Neo4J, etc.Il n'y a donc pas vraiment de «différence» entre les deux méthodes pour vos cas d'utilisation, c'est juste que
findOne(ID)
c'est plus générique que la plus spécialiséegetOne(ID)
. Celui que vous utilisez dépend de vous et de votre projet, mais je m'en tiendrai personnellement aufindOne(ID)
car cela rend votre code moins spécifique à l'implémentation et ouvre les portes pour passer à des choses comme MongoDB, etc. à l'avenir sans trop de refactorisation :)la source
there's not really a 'difference' in the two methods
ici, car il y a vraiment une grande différence dans la façon dont l'entité est récupérée et ce que vous devez attendre de la méthode. La réponse plus bas de @davidxxx le met très bien en évidence, et je pense que tous ceux qui utilisent Spring Data JPA devraient en être conscients. Sinon, cela peut causer un peu de maux de tête.Les
getOne
méthodes ne retournent que la référence de DB (chargement différé). Donc, fondamentalement, vous êtes en dehors de la transaction (laTransactional
classe de service que vous avez déclarée n'est pas prise en compte) et l'erreur se produit.la source
Je trouve vraiment très difficile à partir des réponses ci-dessus. Du point de vue du débogage, j'ai presque passé 8 heures à connaître l'erreur stupide.
J'ai testé le projet spring + hibernate + dozer + Mysql. Pour être clair.
J'ai une entité utilisateur, une entité de livre. Vous faites les calculs de cartographie.
Les livres multiples étaient-ils liés à un seul utilisateur. Mais dans UserServiceImpl, j'essayais de le trouver par getOne (userId);
Le résultat Rest est
}
Le code ci-dessus n'a pas récupéré les livres qui sont lus par l'utilisateur, disons.
La bookList était toujours nulle à cause de getOne (ID). Après avoir changé pour findOne (ID). Le résultat est
}
la source
alors que spring.jpa.open-in-view était vrai, je n'ai eu aucun problème avec getOne mais après l'avoir défini sur false, j'ai obtenu LazyInitializationException. Ensuite, le problème a été résolu en le remplaçant par findById.
Bien qu'il existe une autre solution sans remplacer la méthode getOne, et qui est placée @Transactional à la méthode qui appelle repository.getOne (id). De cette façon, la transaction existera et la session ne sera pas fermée dans votre méthode et lors de l'utilisation de entity, il n'y aura pas d'exception LazyInitializationException.
la source
J'ai eu un problème similaire à comprendre pourquoi JpaRespository.getOne (id) ne fonctionne pas et génère une erreur.
Je suis allé changer pour JpaRespository.findById (id) qui vous oblige à retourner un facultatif.
C'est probablement mon premier commentaire sur StackOverflow.
la source