TL; DR
T findOne(ID id)
(nazwa w starym API) / Optional<T> findById(ID id)
(nazwa w nowym API) polega na EntityManager.find()
tym, że wykonuje żądne ładowanie jednostki .
T getOne(ID id)
polega na EntityManager.getReference()
tym, że wykonuje leniwe ładowanie jednostki . Aby zapewnić efektywne ładowanie jednostki, wymagane jest wywołanie metody.
findOne()/findById()
jest naprawdę bardziej przejrzysty i prostszy w użyciu niż getOne()
.
Tak więc w większości przypadków bardzo sprzyjają findOne()/findById()
ponad getOne()
.
Zmiana interfejsu API
Od przynajmniej 2.0
wersji Spring-Data-Jpa
zmodyfikowanej findOne()
.
Wcześniej był zdefiniowany w CrudRepository
interfejsie jako:
T findOne(ID primaryKey);
Teraz jedyną findOne()
metodą, którą znajdziesz, CrudRepository
jest ta zdefiniowana w QueryByExampleExecutor
interfejsie jako:
<S extends T> Optional<S> findOne(Example<S> example);
Jest to ostatecznie realizowane przez SimpleJpaRepository
domyślną implementację CrudRepository
interfejsu.
Ta metoda jest zapytaniem przez przykładowe wyszukiwanie i nie chcesz tego jako zamiennika.
W rzeczywistości metoda o tym samym zachowaniu jest nadal dostępna w nowym interfejsie API, ale nazwa metody uległa zmianie.
Jego nazwa została zmieniona od findOne()
celu findById()
w CrudRepository
interfejsie:
Optional<T> findById(ID id);
Teraz zwraca Optional
. Którym nie jest tak źle zapobiegać NullPointerException
.
Tak więc rzeczywisty wybór jest teraz między Optional<T> findById(ID id)
a T getOne(ID id)
.
Dwie różne metody, które opierają się na dwóch różnych metodach pobierania JPA EntityManager
1) Optional<T> findById(ID id)
Javadoc stwierdza, że:
Pobiera jednostkę na podstawie jej identyfikatora.
Kiedy przyglądamy się implementacji, widzimy, że polega ona na EntityManager.find()
pobieraniu:
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));
}
I tu em.find()
jest EntityManager
metoda zadeklarowana jako:
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Jego javadoc stwierdza:
Znajdź według klucza podstawowego, używając określonych właściwości
Zatem pobranie załadowanej jednostki wydaje się oczekiwane.
2) Podczas gdy T getOne(ID id)
javadoc stwierdza (podkreślenie moje):
Zwraca odwołanie do jednostki o podanym identyfikatorze.
W rzeczywistości terminologia referencyjna to tak naprawdę płyta, a API JPA nie określa żadnej getOne()
metody.
Dlatego najlepszą rzeczą, jaką można zrobić, aby zrozumieć, co robi opakowanie Spring, jest sprawdzenie implementacji:
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Tutaj em.getReference()
jest EntityManager
metoda zadeklarowana jako:
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
I na szczęście EntityManager
javadoc lepiej zdefiniował swój zamiar (podkreślenie moje):
Uzyskaj instancję, której stan można leniwie pobrać . Jeśli żądane wystąpienie nie istnieje w bazie danych, EntityNotFoundException jest generowany podczas pierwszego dostępu do stanu wystąpienia . (Środowisko wykonawcze dostawcy trwałości może zgłosić wyjątek EntityNotFoundException po wywołaniu metody getReference) . Aplikacja nie powinna oczekiwać, że stan wystąpienia będzie dostępny po odłączeniu , chyba że aplikacja uzyskała do niego dostęp, gdy menedżer jednostek był otwarty.
Zatem wywołanie getOne()
może zwrócić leniwie pobraną jednostkę.
Tutaj leniwe pobieranie nie odnosi się do relacji jednostki, ale do samej jednostki.
Oznacza to, że jeśli wywołamy, getOne()
a następnie kontekst Persistence zostanie zamknięty, jednostka może nigdy nie zostać załadowana, a więc wynik jest naprawdę nieprzewidywalny.
Na przykład, jeśli obiekt proxy jest serializowany, możesz uzyskać null
odwołanie jako wynik zserializowany lub jeśli metoda jest wywoływana dla obiektu proxy, zostanie zgłoszony wyjątek, taki jak LazyInitializationException
.
Tak więc w tego rodzaju sytuacji wyrzucenie EntityNotFoundException
tego jest głównym powodem użycia getOne()
do obsługi wystąpienia, które nie istnieje w bazie danych, ponieważ sytuacja błędu może nigdy nie zostać wykonana, gdy jednostka nie istnieje.
W każdym razie, aby zapewnić jego załadowanie, musisz manipulować bytem podczas otwierania sesji. Możesz to zrobić, wywołując dowolną metodę na encji.
Lub lepsze alternatywne zastosowanie findById(ID id)
zamiast.
Dlaczego tak niejasny interfejs API?
Na koniec dwa pytania do programistów Spring-Data-JPA:
dlaczego nie mieć bardziej przejrzystej dokumentacji getOne()
? Leniwe ładowanie jednostki tak naprawdę nie jest szczegółem.
dlaczego musisz wprowadzić getOne()
do wrapa EM.getReference()
?
Dlaczego nie po prostu trzymać się zawiniętego metody: getReference()
? Ta metoda EM jest naprawdę bardzo szczególna, podczas gdy getOne()
przenosi tak proste przetwarzanie.