Mam insertOrUpdate
metodę, która wstawia, Entity
gdy nie istnieje, lub aktualizuje ją, jeśli tak. Aby to włączyć, muszę findByIdAndForeignKey
, jeśli zwróciło, null
wstaw, jeśli nie, to zaktualizuj. Problem w tym, jak sprawdzić, czy istnieje? Więc spróbowałem getSingleResult
. Ale zgłasza wyjątek, jeśli
public Profile findByUserNameAndPropertyName(String userName, String propertyName) {
String namedQuery = Profile.class.getSimpleName() + ".findByUserNameAndPropertyName";
Query query = entityManager.createNamedQuery(namedQuery);
query.setParameter("name", userName);
query.setParameter("propName", propertyName);
Object result = query.getSingleResult();
if (result == null) return null;
return (Profile) result;
}
ale getSingleResult
rzuca Exception
.
Dzięki
getSingleResult()
jest używane w sytuacjach typu: „ Jestem całkowicie pewien, że ta płyta istnieje. Zastrzel mnie, jeśli jej nie ma ”. Nie chcę testować zanull
każdym razem, gdy używam tej metody, ponieważ jestem pewien, że jej nie zwróci. W przeciwnym razie powoduje to wiele schematycznych i defensywnych programów. A jeśli rekord naprawdę nie istnieje (w przeciwieństwie do tego, co założyliśmy), znacznie lepiej jest goNoResultException
porównać zNullPointerException
kilkoma wierszami później. Oczywiście posiadanie dwóch wersjigetSingleResult()
byłoby super, ale gdybym miał odebrać jedną ...Logikę zawarłem w poniższej metodzie pomocniczej.
źródło
Spróbuj tego w Javie 8:
źródło
.orElse(null)
Oto dobra opcja, aby to zrobić:
źródło
TypedQuery<T>
jednak, w takim przypadkugetResultList()
jest już poprawnie wpisany jakoList<T>
.fetch()
jednostką może nie zostać wypełniona w całości. Zobacz stackoverflow.com/a/39235828/661414setMaxResults()
ma płynny interfejs, dzięki czemu możesz pisaćquery.setMaxResults(1).getResultList().stream().findFirst().orElse(null)
. Powinien to być najbardziej efektywny schemat połączeń w Javie 8+.Spring ma do tego użyteczną metodę :
źródło
Zrobiłem (w Javie 8):
źródło
Z JPA 2.2 zamiast
.getResultList()
sprawdzania, czy lista jest pusta lub tworzenia strumienia, możesz zwrócić strumień i pobrać pierwszy element.źródło
Jeśli chcesz użyć mechanizmu try / catch do rozwiązania tego problemu, możesz go użyć do działania jak if / else. Użyłem try / catch, aby dodać nowy rekord, gdy nie znalazłem istniejącego.
źródło
Oto wersja typowana / generyczna oparta na implementacji Rodrigo IronMana:
źródło
Jest alternatywa, którą bym polecił:
Zabezpiecza to przed wyjątkiem zerowego wskaźnika, gwarantuje, że zostanie zwrócony tylko 1 wynik.
źródło
Więc nie rób tego!
Masz dwie możliwości:
Uruchom selekcję, aby uzyskać LICZBĘ swojego zestawu wyników i pobieraj dane tylko wtedy, gdy ta liczba jest różna od zera; lub
Użyj innego rodzaju zapytania (otrzymującego zestaw wyników) i sprawdź, czy ma 0 lub więcej wyników. Powinien mieć 1, więc wyciągnij go z kolekcji wyników i gotowe.
Pójdę z drugą sugestią, w porozumieniu z Cletusem. Daje lepszą wydajność niż (potencjalnie) 2 zapytania. Mniej pracy.
źródło
Łącząc przydatne bity istniejących odpowiedzi (ograniczając liczbę wyników, sprawdzając, czy wynik jest unikalny) i używając ustalonej nazwy metody (Hibernate), otrzymujemy:
źródło
Nieudokumentowana metoda
uniqueResultOptional
w org.hibernate.query.Query powinna załatwić sprawę. Zamiast łapaćNoResultException
, możesz po prostu zadzwonićquery.uniqueResultOptional().orElse(null)
.źródło
Rozwiązałem to, używając
List<?> myList = query.getResultList();
i sprawdzając, czymyList.size()
jest równe zero.źródło
Oto ta sama logika, co sugerowali inni (pobierz resultList, zwróć jej jedyny element lub null), używając Google Guava i TypedQuery.
Zwróć uwagę, że Guava zwróci nieintuicyjny wyjątek IllegalArgumentException, jeśli zestaw wyników zawiera więcej niż jeden wynik. (Wyjątek ma sens dla klientów metody getOnlyElement (), ponieważ przyjmuje listę wyników jako argument, ale jest mniej zrozumiały dla klientów metody getSingleResultOrNull ().)
źródło
Oto kolejne rozszerzenie, tym razem w Scali.
Z tym alfonsem:
źródło
Spójrz na ten kod:
return query.getResultList().stream().findFirst().orElse(null);
Kiedy
findFirst()
jest wywoływana, może zostać wyrzucony wyjątek NullPointerException.najlepszym podejściem jest:
return query.getResultList().stream().filter(Objects::nonNull).findFirst().orElse(null);
źródło
Tak więc wszystkie rozwiązania „spróbuj przepisać bez wyjątku” na tej stronie mają drobny problem. Albo nie zgłasza wyjątku NonUnique, albo też w niektórych niewłaściwych przypadkach (patrz poniżej).
Myślę, że właściwym rozwiązaniem jest (może) to:
Zwraca wartość null, jeśli na liście znajduje się element 0, zwraca wartość nieunikalną, jeśli na liście są różne elementy, ale nie zwraca wartości nieunikalnej, gdy jeden z wybranych elementów nie jest poprawnie zaprojektowany i zwraca ten sam obiekt więcej niż jeden razy.
Zapraszam do komentowania.
źródło
Osiągnąłem to, uzyskując listę wyników, a następnie sprawdzając, czy jest pusta
To jest tak irytujące, że
getSingleResult()
rzuca wyjątkiRzuty:
źródło
To mi pasuje:
źródło