TL; DR
T findOne(ID id)
(nome nella vecchia API) / Optional<T> findById(ID id)
(nome nella nuova API) si basa sul fatto EntityManager.find()
che esegue un caricamento desideroso di entità .
T getOne(ID id)
fa affidamento sul fatto EntityManager.getReference()
che esegue un caricamento lento dell'entità . Quindi, per garantire l'effettivo caricamento dell'entità, è necessario invocare un metodo su di essa.
findOne()/findById()
è davvero più chiaro e semplice da usare rispetto a getOne()
.
Così nella maggior parte dei casi molto, favorire findOne()/findById()
sopra getOne()
.
Cambio API
Almeno, la 2.0
versione, Spring-Data-Jpa
modificata findOne()
.
In precedenza, era definito CrudRepository
nell'interfaccia come:
T findOne(ID primaryKey);
Ora, il singolo findOne()
metodo in cui troverai CrudRepository
è quello definito QueryByExampleExecutor
nell'interfaccia come:
<S extends T> Optional<S> findOne(Example<S> example);
Questo è finalmente implementato dall'implementazione SimpleJpaRepository
predefinita CrudRepository
dell'interfaccia.
Questo metodo è una query di ricerca di esempio e non si desidera sostituirlo.
In effetti, il nuovo metodo con lo stesso comportamento è ancora presente nella nuova API ma il nome del metodo è cambiato.
E 'stato ribattezzato dalla findOne()
a findById()
alla CrudRepository
interfaccia di:
Optional<T> findById(ID id);
Ora restituisce un Optional
. Che non è così male da prevenire NullPointerException
.
Quindi, la scelta effettiva è ora tra Optional<T> findById(ID id)
e T getOne(ID id)
.
Due metodi distinti che si basano su due distinti metodi di recupero di EntityManager JPA
1) Il Optional<T> findById(ID id)
javadoc afferma che:
Recupera un'entità in base al suo ID.
Mentre esaminiamo l'implementazione, possiamo vedere che si affida EntityManager.find()
per fare il recupero:
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));
}
Ed ecco em.find()
un EntityManager
metodo dichiarato come:
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Il suo javadoc afferma:
Trova per chiave primaria, usando le proprietà specificate
Quindi, il recupero di un'entità caricata sembra previsto.
2) Mentre il T getOne(ID id)
javadoc afferma (l'enfasi è mia):
Restituisce un riferimento all'entità con l'identificatore specificato.
In effetti, la terminologia di riferimento è davvero la scheda e l'API JPA non specifica alcun getOne()
metodo.
Quindi la cosa migliore da fare per capire cosa fa il wrapper Spring è esaminare l'implementazione:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Ecco em.getReference()
un EntityManager
metodo dichiarato come:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
E per fortuna, il EntityManager
javadoc ha definito meglio la sua intenzione (l'enfasi è mia):
Ottieni un'istanza, il cui stato può essere recuperato pigramente . Se l'istanza richiesta non esiste nel database, EntityNotFoundException viene generata al primo accesso allo stato dell'istanza . (Il runtime del provider di persistenza è autorizzato a lanciare EntityNotFoundException quando viene chiamato getReference.) L'applicazione non dovrebbe aspettarsi che lo stato dell'istanza sia disponibile al momento del distacco , a meno che non sia stato accessibile dall'applicazione mentre il gestore entità era aperto.
Quindi, invocare getOne()
può restituire un'entità pigramente recuperata.
Qui, il recupero pigro non si riferisce alle relazioni dell'entità ma all'entità stessa.
Significa che se invochiamo getOne()
e quindi il contesto di Persistenza viene chiuso, l'entità potrebbe non essere mai caricata e quindi il risultato è davvero imprevedibile.
Ad esempio, se l'oggetto proxy è serializzato, è possibile ottenere un null
riferimento come risultato serializzato o se un metodo viene invocato sull'oggetto proxy, LazyInitializationException
viene generata un'eccezione come .
Quindi, in questo tipo di situazione, la EntityNotFoundException
causa principale è quella di utilizzare getOne()
un'istanza che non esiste nel database poiché una situazione di errore non può mai essere eseguita mentre l'entità non esiste.
In ogni caso, per assicurarne il caricamento devi manipolare l'entità mentre la sessione è aperta. Puoi farlo invocando qualsiasi metodo sull'entità.
O un uso alternativo migliore findById(ID id)
invece di.
Perché un'API così poco chiara?
Per finire, due domande per gli sviluppatori Spring-Data-JPA:
perché non avere una documentazione più chiara per getOne()
? Il caricamento lento dell'entità non è in realtà un dettaglio.
perché è necessario presentare getOne()
per avvolgere EM.getReference()
?
Perché non è sufficiente attenersi al metodo avvolto: getReference()
? Questo metodo EM è davvero molto particolare mentre getOne()
trasmette un'elaborazione così semplice.