TL; DR
T findOne(ID id)
(nom dans l'ancienne API) / Optional<T> findById(ID id)
(nom dans la nouvelle API) s'appuie sur EntityManager.find()
qui effectue un chargement impatient d'entité .
T getOne(ID id)
s'appuie sur EntityManager.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 que getOne()
.
Ainsi , dans la plupart des cas très, favoriser findOne()/findById()
plus getOne()
.
Changement d'API
Du moins, la 2.0
version, Spring-Data-Jpa
modifiée findOne()
.
Auparavant, il était défini dans l' CrudRepository
interface comme:
T findOne(ID primaryKey);
Maintenant, la seule findOne()
méthode que vous trouverez dans CrudRepository
est celle définie dans l' QueryByExampleExecutor
interface comme:
<S extends T> Optional<S> findOne(Example<S> example);
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:
Optional<T> findById(ID id);
Maintenant, il renvoie un Optional
. Ce qui n'est pas si mal à éviter NullPointerException
.
Ainsi, le choix réel se situe désormais entre Optional<T> findById(ID id)
et T 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:
Récupère une entité par son identifiant.
En examinant l'implémentation, nous pouvons voir qu'elle s'appuie sur EntityManager.find()
la récupération:
public Optional<T> findById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
Class<T> domainType = getDomainClass();
if (metadata == null) {
return Optional.ofNullable(em.find(domainType, id));
}
LockModeType type = metadata.getLockModeType();
Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();
return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
}
Et voici em.find()
une EntityManager
méthode déclarée comme:
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Son javadoc déclare:
Rechercher par clé primaire, en utilisant les propriétés spécifiées
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 ):
Renvoie une référence à l'entité avec l'identifiant donné.
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:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Voici em.getReference()
une EntityManager
méthode déclarée comme:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
Et heureusement, le EntityManager
javadoc a mieux défini son intention (c'est moi qui souligne):
Obtenez une instance dont l'état peut être récupéré paresseusement . Si l'instance demandée n'existe pas dans la base de données, l'exception EntityNotFoundException est levée lors du premier accès à l'état de l'instance . (Le moteur d'exécution du fournisseur de persistance est autorisé à lever l'exception EntityNotFoundException lorsque getReference est appelé.) L'application ne doit pas s'attendre à ce que l'état de l'instance soit disponible lors du détachement , à moins qu'il n'ait été accédé par l'application alors que le gestionnaire d'entités était ouvert.
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 que LazyInitializationException
levée.
Donc, dans ce genre de situation, le rejet de EntityNotFoundException
cela est la principale raison à utiliser getOne()
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 envelopper EM.getReference()
?
Pourquoi ne pas simplement coller à la méthode enveloppée: getReference()
? Cette méthode EM est vraiment très particulière tout en getOne()
véhiculant un traitement si simple.