2015-08-28 23 views
13

Pracuję nad dziwnym problemem, robiłem testy integracyjne, dzwoniąc do kontrolera, aby uzyskać obiekt z bazy danych, która nie istnieje.Dlaczego getOne (...) w repozytorium danych sprężynowych nie generuje wyjątku EntityNotFoundException?

public Optional<T> get(Long id) { 

    try { 
    return Optional.ofNullable(repository.getOne(id)); 
    } catch(EntityNotFoundException e) { 
    return Optional.empty(); 
    } 
} 

Kiedy getOne(…) nie jest w stanie znaleźć coś, spodziewałem się EntityNotFoundException ale właściwie nic. Jeśli sprawdzam mój wynik, widzę, że mam pusty obiekt z łącznikiem do niego obsługującym "wyrzucił EntityNotFoundException", ale nie wchodzimy w połówkę i zwracam opcjonalne tego dziwnego obiektu.

Nie mogę zrozumieć tego zachowania.

+0

Dlaczego oczekujesz wyrzucenia? Jawadoc nie mówi o wyjątku. – Jens

+0

To jest klasyczne zachowanie JPA podczas wywoływania getOne() na nieistniejących danych. – Seb

+0

Co więcej widzę, że ten wyjątek jest rzucany, ale jakoś poradzi sobie z danymi z wiosny? – Seb

Odpowiedz

18

Dzieje się tak ze względu na sposób, w jaki JPA określa, że ​​EntityManager.getReference(…) zadziała. Powinien zwrócić serwer proxy, który albo usunie obiekt, który ma zostać zwrócony przy pierwszym dostępie do właściwości, albo ostatecznie odrzuci zamknięty wyjątek.

Najprostszym sposobem obejścia tego problemu jest po prostu użycie findOne(…) zamiast tego, tak jak to Optional.ofNullable(repository.findOne(…)). findOne(…) zwróci null na wypadek, gdyby nie znaleziono wyniku.

Bardziej zaawansowanym sposobem rozwiązania tego problemu jest bezpośrednie utworzenie instancji zwracających instancje Optional. Można to osiągnąć, tworząc niestandardowy interfejs bazowego repozytorium, stosując Optional<T> jako typ zwrotu dla metod find….

interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> { 

    Optional<T> findOne(ID id); 

    // declare additional methods if needed 
} 

interface YourRepository extends BaseRepository<DomainClass, Long> { … } 

Znajdź pełny przykład tego w Spring Data examples repository.

+0

Wygląda na to, że jest to dobry pomysł! Spróbuję odstąpić od normy Ale ... co z osiągnięciem, czy FindOne jest tak dobre jak getOne? – Seb

+2

Istotnie istnieje różnica, ponieważ 'getOne (...)' nie od razu materializuje obiekt podczas gdy 'findOne (...)' robi. Minusem tego pierwszego jest to, że opóźnia to niepowodzenie w wyszukiwaniu, które cię tu ugryza. Ponadto 'findOne (...)' trafi do pamięci podręcznej pierwszego poziomu w 'EntityManager', więc w ramach konkretnej sesji nie trafisz dwukrotnie do bazy danych. Jeśli to naprawdę staje się problemem, możesz wyraźnie podłączyć pamięć podręczną, ale opóźniłbym to, dopóki pomiar naprawdę nie zostanie pokazany, jest problem. –

+2

bardzo jasne, dziękuję za pomoc! – Seb