Spring Data JPA 2.x

Wstęp do Spring Data JPA wykonaliśmy w poprzednim rozdziale Spring Data JPA 1.x - Podstawowe funkcjonalności, 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.

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;
    }
}
Nasza rekomendacja
Czy warto znaleźć trochę czasu na przejście ze Spring Boot-a 1 na 2, a więc i rozpocząć korzystanie ze Spring Data JPA 2.x? Na pewno jest to rozwiązanie, które powinniśmy wybrać tworząc nowe projekty. W przypadku starych, jak zwykle zależy to od tego czy planujemy jeszcze je rozwijać. Jeśli tak to warto poświęcić czas na dostosowanie kodu, co na pewno docenią programiści, którzy przyjdą po nas i będą wykonywać dalszy rozwój aplikacji.

Ogólnie przepisanie Springa wraz z innymi projektami na Javę 8 bardzo nam się podoba. Samo wprowadzenie nowej wersji Spring Data JPA 2 zaowocowało jednak dodatkową pracą, którą i my musimy wykonać (przygotowanie wersji aplikacji opartej o Spring Boot 2).
Używamy w StartAPPa


W kodzie kursów używamy wersji Spring Data JPA 1.x. Zgodnie z tym co pisaliśmy wyżej, w przyszłości udostępnimy możliwość pobrania modułów również w wersji 2.x. Chcemy jednak pozostawić wersję 1.x, tak aby dać Wam wybór podczas pobierania, ponieważ wiemy, że w wielu firmach nadal rozwijane są projekty oparte o pierwszą wersję. Zresztą jest to normalne, biorąc pod uwagę to, że Spring Boot 2 jest ciągle stosunkowo "młodym" rozwiązaniem.
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.
Linki
https://docs.spring.io/spring-data/jpa/docs/2.0.11.RELEASE/reference/html

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:
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 .