Spring Data JPA 2 / 3

Wstęp do Spring Data JPA wykonaliśmy w poprzednim rozdziale Spring Data JPA - Podstawy, więc tutaj ograniczymy się jedynie do przedstawienia interfejsów, z zaznaczeniem zmian jakie nastąpiły w stosunku do poprzedniej wersji. Skąd w ogóle te zmiany? Wprowadzone zostały ze względu na to, że Spring 5, z którego korzysta Spring Boot 2, został napisany w Javie 8, która jak wiemy jest rewolucją w stosunku do swoich poprzednich wersji. Poskutkowało to tym, że część metod udostępnianych przez interfejsy zaczęła używać nieco innej konwencji nazewniczej oraz wprowadziła do użytku klasę Optional.

Wracając do tematu, tak samo jak opisywaliśmy w poprzednim rozdziale, tutaj również dodajemy odpowiedni szablon startowy do Spring Boot-a:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Po zbudowaniu projektu możemy przystąpić do przyjrzenia się kilku istotnym interfejsom, które dostajemy razem ze starterem. Będziemy z nich korzystać (w szczególności z jednego z nich) podczas wykonywania operacji na danych w systemie.

Tak samo prezentuje się to w Spring Boot 3.

Podstawowy interfejs - CrudRepository

Dostarcza podstawowe wersje metod dla operacji typu CRUD (Create, Read, Update, Delete):
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {

    <S extends T> S save(S entity);

    <S extends T> Iterable<S> saveAll(Iterable<S> entities);

    Optional<T> findById(ID id);

    boolean existsById(ID id);

    Iterable<T> findAll();

    Iterable<T> findAllById(Iterable<ID> ids);

    long count();

    void deleteById(ID id);

    void delete(T entity);

    void deleteAll(Iterable<? extends T> entities);
    
    void deleteAll();
}
Wytłumaczenie kolejnych metod wraz z porównaniem do wersji 1.x:

Spring Boot Data 1.x Spring Boot Data 2.x
save(S) save - zapisuje pojedynczą encję w bazie
save(Iterable) saveAll - zapisuje w bazie cały zbiór encji
T findOne Optional<T> findById - wyszukuje encje po id
exists existsById - sprawdza po id czy encja istnieje
findAll findAll - pobiera wszystkie encje zwracając interfejs Iterable
findAll findAllById - pobiera wszystkie encje po podaniu zbioru idików
  i zwraca interfejs Iterable
count count - zlicza wszystkie encje
delete(ID) deleteById - usuwa encje po podanym id
delete(T) delete - usuwa encje po podanym obiekcie
delete(Iterable) deleteAll(Iterable) - usuwa encje po podanym zbiorze obiektów
deleteAll deleteAll - usuwa wszystkie encje
Możemy zauważyć, że nazwy metody związanych tematycznie, które do tej pory wyglądały tak samo ale różniły się parametrami, zostały doprecyzowane poprzez dodanie postfixu All lub ById. Dzięki temu od razu, gdy tylko widzimy nazwę metody, wiemy jakie jest jej przeznaczenie. Nie musimy przyglądać się parametrom, co jest bardzo wygodne.

Pomijając nową konwencję nazewniczą, nastąpiła również duża zmiana w kontekście metody findOne, która do tej pory zwracała bezpośrednio obiekt klasy. Teraz została ona zamieniona na findById, która zwraca klasę Optional<T> opakowującą rezultat operacji pobrania (może zawierać obiekt lub nie). Tak, właśnie wygląda to dzięki Javie 8.

PagingAndSortingRepository rozszerzający CrudRepository

Dorzuca - w stosunku do tego co ma CrudRepository - metody wyciągające encje w sposób posortowany i stronicowany:
public interface PagingAndSortingRepository <T, ID extends Serializable>
                                                        extends CrudRepository<T, ID> {

    Iterable<T> findAll(Sort sort);

    Page<T> findAll(Pageable pageable);
}
Metody te rozumiemy w ten sposób:

findAll(Sort sort) - pobiera wszystkie encje (z uwzględnieniem sortowania)
  i zwraca interfejs Iterable
findAll(Pageable pageable) - pobiera wszystkie encje (z uwzględnieniem stronicowania)
  i zwraca interfejs strony: Page
Widzimy, że w porównaniu do wersji 1.x nazwy metod jak i parametrów nie uległy zmianie. Interfejs wygląda tak jak poprzednio.

JPARepository rozszerzający PagingAndSortingRepository

Dostarcza bardziej precyzyjne wersje metod, np. poprzez zwracanie interfejsu List zamiast Iterable oraz dodaje nowe metody:
public interface JpaRepository<T, ID extends Serializable>
        extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

    List<T> findAll();

    List<T> findAll(Sort sort);

    List<T> findAllById(Iterable<ID> ids);

    <S extends T> List<S> saveAll(Iterable<S> entities);

    void flush();

    <S extends T> S saveAndFlush(S entity);

    void deleteInBatch(Iterable<T> entities);

    void deleteAllInBatch();

    T getOne(ID id);

    @Override
    <S extends T> List<S> findAll(Example<S> example);

    @Override
    <S extends T> List<S> findAll(Example<S> example, Sort sort);
}
W tym przypadku wytłumaczenie kolejnych kategorii metod wygląda tak:

Spring Boot Data 1.x Spring Boot Data 2.x
findAll findAll - pobiera wszystkie encje (ew. sortowane)
  i zwraca interfejs List
findAll(Iterable) findAllById - pobiera encje po zbiorze idików i zwraca interfejs List
save saveAll - zapisuje encje w bazie, podane w postaci zbioru
  bazującego na interfejsie Iterable
flush flush - wymusza odzwierciedlenie wykonanych operacji
  (na encjach) na bazie
saveAndFlush saveAndFlush - zapisuje encje i wymusza odzwierciedlenie
  wykonanych operacji (na encjach) na bazie
deleteInBatch deleteInBatch - usuwa całą partię encji, podaną w postaci zbioru
  bazującego na interfejsie Iterable
deleteAllInBatch deleteAllInBatch - usuwa wszystkie encje w jednej partii
getOne getOne - pobiera encję po id
findAll(Example...) findAll(Example...) - pobiera encje po podanej klasie przykładu
  (możliwe w wersji z sortowaniem)
W przypadku tego interfejsu tylko dwie metody doczekały się zmian. Mowa o metodzie findAll pobierającej dane po zbiorze idików, która zamieniła się na findAllById oraz metodzie save zapisującej zbiór danych w bazie, zmienionej na saveAll. Zmiany te są logiczne i porządkują kod.

Podobnie jak w wersji 1.x mamy tutaj metody, które pobierają obiekty po klasie przykładu (Example). Jak już wiemy znajdują się one w tym interfejsie nieprzypadkowo, bowiem interfejs JPARepository dziedziczy nie tylko z PagingAndSortingRepository, ale również z interfejsu QueryByExampleExecutor. Ten ostatni jest odpowiedzialny za dostarczenie deklaracji metod operujących właśnie na klasie przykładu.

JPARepository w akcji

Na koniec przedstawiamy przykład identyczny jak w rozdziale 1.x, ale z odpowiednimi zmianami dostosowanymi do wymagań wersji 2.x Teraz metoda (getAppaItem) w naszym serwisie wykona operację pobrania encji po id używając nowej nazwy metody. Dodatkowo, widzimy, że metoda zwraca nam klasę Optional, opakowującą rezultat:
@Service
public class ItemServiceImpl implements ItemService {

    private final Logger LOG = LoggerFactory.getLogger(ItemServiceImpl.class);
    
    private ItemRepository itemRepository;
    
    ...
    
    @Autowired
    public ItemServiceImpl(ItemRepository itemRepository, ...) {
    	this.itemRepository = itemRepository;
    }

    @Override
    public ItemResponseDTO getAppaItem(Long id) {
    
    	Optional<Item> appaItemOpt = itemRepository.findById(id);
        if (!appaItemOpt.isPresent()) {
            throw new AppaFormProcessException("Appa item does not exist");
        }
    
        Item appaItem = appaItemOpt.get();
        
    	...
    
    	return appaItemResponseDTO;
    }
}
Rekomendacja
Czy warto zaktualizować projekt z Spring Boot 1 na 2 i rozważyć korzystanie ze Spring Data JPA 2 lub 3? To zdecydowanie korzystny wybór dla nowych projektów. W przypadku istniejących projektów zależy to od planów dotyczących dalszego rozwoju. Jeśli przewidujesz kontynuację prac, inwestycja czasu w dostosowanie kodu będzie doceniona przez przyszłych programistów.

Przejście na Java 8 i wdrożenie Spring Data JPA 2 / 3 niesie za sobą korzyści, ale wiąże się z dodatkową pracą, szczególnie podczas migracji do Spring Boot 2 / 3. Ogólnie rzecz biorąc, dostosowanie się do tych aktualizacji jest zgodne z nowoczesnymi praktykami programistycznymi.
Praktyka


W kodzie kurs Kurs Aplikacji Web - Mega pakiet używamy wersji Spring Data JPA 3.
Zdjęcie autora
Autor: Jarek Klimas
Data: 03 stycznia 2024
Labele: Backend, Podstawowy, Java
W tej strefie znajdziesz wszystko co niezbędne, aby komfortowo uczyć się Hibernate'a. Doskonale opisany kod nie zawiera zbędnych komplikacji, tylko samą esencję w postaci praktycznych przykładów. Tutaj odnajdziesz wszystko co jest istotne w danym temacie. Otrzymujesz pakiet złożony z kilku projektów wraz z obszernym wytłumaczeniem kodu.
Topowe Materiały
Spring IO: Spring Data
Baeldung: Introduction to Spring Data JPA

Udemy: [NEW] Spring Boot 3, Spring 6 & Hibernate for Beginners  —  polskie napisy

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy: stackshare
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .


Pozycjonowanie stron: Grupa TENSE