Spring Data JPA – mniej SQL, więcej produktywności

TL;DR: Spring Data JPA automatycznie generuje implementacje repository na podstawie nazw metod. Zamiast pisać SQL, definiujesz metodę findByNameAndAge() a Spring Data samo tworzy zapytanie. Mniej boilerplate code, szybszy development, mniej błędów.

Jeśli programujesz w Javie i pracujesz z bazami danych, prawdopodobnie pisałeś już setki linii kodu do podstawowych operacji CRUD. Spring Data JPA kończy z tym problemem – pozwala skupić się na logice biznesowej zamiast na pisaniu kolejnych implementacji DAO.

Dlaczego Spring Data JPA jest ważne

Tradycyjnie praca z bazą danych w Javie wymagała pisania dużej ilości repetytywnego kodu. Każda encja potrzebowała swojego DAO z metodami save(), findById(), findAll(). Spring Data JPA w 2016 roku to dojrzała technologia, która eliminuje ten problem raz na zawsze.

Spring Data JPA znacznie przyspiesza development aplikacji enterprise. Developerzy mogą skupić się na logice biznesowej zamiast na infrastrukturze bazodanowej. To przekłada się na krótszy time-to-market i mniejsze koszty projektu.

Co się nauczysz:

  • Jak skonfigurować Spring Data JPA w projekcie
  • Czym różni się od tradycyjnego DAO
  • Jak tworzyć query methods bez pisania SQL
  • Jakie są konwencje nazewnictwa metod
  • Kiedy używać @Query dla złożonych zapytań

Wymagania wstępne:

  • Podstawowa znajomość JPA/Hibernate
  • Doświadczenie z Spring Framework
  • Znajomość SQL i baz danych relacyjnych
  • Rozumienie wzorca Repository/DAO

Problem z tradycyjnym podejściem

Analogia: Tradycyjne DAO to jak pisanie każdego dokumentu od zera. Spring Data JPA to szablon dokumentu – wpisujesz tylko to co się różni, reszta jest już gotowa.

Zobaczmy jak wyglądał typowy kod DAO przed Spring Data JPA:

// Tradycyjne DAO - dużo boilerplate code
@Repository
public class UserDAOImpl implements UserDAO {
    
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public User save(User user) {
        if (user.getId() == null) {
            entityManager.persist(user);
            return user;
        } else {
            return entityManager.merge(user);
        }
    }
    
    @Override
    public User findById(Long id) {
        return entityManager.find(User.class, id);
    }
    
    @Override
    public List<User> findAll() {
        return entityManager.createQuery("SELECT u FROM User u", User.class)
                           .getResultList();
    }
    
    @Override
    public List<User> findByAge(int age) {
        return entityManager.createQuery(
            "SELECT u FROM User u WHERE u.age = :age", User.class)
            .setParameter("age", age)
            .getResultList();
    }
    
    @Override
    public void delete(User user) {
        entityManager.remove(user);
    }
}
Problem: Ten sam kod powtarza się dla każdej encji. Dużo pracy, dużo miejsca na błędy, mało wartości biznesowej.

Spring Data JPA – repository pattern

Spring Data JPA wprowadza wzorzec Repository, który automatycznie generuje implementacje na podstawie interfejsów:

// Spring Data JPA - mniej kodu, więcej funkcjonalności
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    
    // Podstawowe operacje CRUD są już dostępne!
    // save(), findById(), findAll(), delete() itp.
    
    // Query methods - Spring Data generuje implementację
    List<User> findByAge(int age);
    List<User> findByNameContaining(String name);
    List<User> findByAgeGreaterThan(int age);
    User findByEmail(String email);
    
    // Można łączyć warunki
    List<User> findByNameAndAge(String name, int age);
    List<User> findByAgeBetween(int minAge, int maxAge);
    
    // Sortowanie i paginacja
    List<User> findByAgeOrderByNameAsc(int age);
    Page<User> findByAge(int age, Pageable pageable);
}
Pro tip: Spring Data JPA generuje implementację w runtime na podstawie nazw metod. Nie musisz pisać ani linii kodu implementacji!

Konfiguracja w Spring Boot

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
# application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

Query methods – konwencje nazewnictwa

Spring Data JPA parsuje nazwy metod i automatycznie generuje zapytania SQL:

MetodaGenerowane zapytanie
findByName(String name)WHERE name = ?
findByNameContaining(String name)WHERE name LIKE %?%
findByAgeGreaterThan(int age)WHERE age > ?
findByNameAndAge(String name, int age)WHERE name = ? AND age = ?
findByNameOrEmail(String name, String email)WHERE name = ? OR email = ?
findByAgeBetween(int min, int max)WHERE age BETWEEN ? AND ?
findByNameOrderByAgeDesc(String name)WHERE name = ? ORDER BY age DESC

Słowa kluczowe w query methods

  • find…By, read…By, get…By – pobieranie danych
  • count…By – liczenie rekordów
  • delete…By, remove…By – usuwanie
  • exists…By – sprawdzanie istnienia
public interface UserRepository extends JpaRepository<User, Long> {
    
    // Różne sposoby pobrania danych
    List<User> findByAge(int age);
    List<User> readByAge(int age);
    List<User> getByAge(int age);
    
    // Zliczanie
    long countByAge(int age);
    
    // Sprawdzanie istnienia
    boolean existsByEmail(String email);
    
    // Usuwanie
    void deleteByAge(int age);
    
    // Pierwszy wynik
    User findFirstByAgeOrderByNameAsc(int age);
    
    // Top N wyników
    List<User> findTop10ByAgeOrderByNameAsc(int age);
}

Paginacja i sortowanie

Spring Data JPA ma wbudowane wsparcie dla paginacji:

@RestController
public class UserController {
    
    @Autowired
    private UserRepository userRepository;
    
    @GetMapping("/users")
    public Page<User> getUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(defaultValue = "name") String sortBy) {
        
        Pageable pageable = PageRequest.of(page, size, 
                                         Sort.by(sortBy).ascending());
        return userRepository.findAll(pageable);
    }
    
    @GetMapping("/users/age/{age}")
    public Page<User> getUsersByAge(@PathVariable int age, Pageable pageable) {
        return userRepository.findByAge(age, pageable);
    }
}
Pageable to interface Spring Data który enkapsuluje informacje o paginacji i sortowaniu. Spring Boot automatycznie parsuje parametry HTTP na obiekt Pageable.

Custom queries z @Query

Gdy query methods nie wystarczają, możesz użyć adnotacji @Query:

public interface UserRepository extends JpaRepository<User, Long> {
    
    // JPQL query
    @Query("SELECT u FROM User u WHERE u.age > :age AND u.active = true")
    List<User> findActiveUsersOlderThan(@Param("age") int age);
    
    // Native SQL query
    @Query(value = "SELECT * FROM users u WHERE u.registration_date > :date", 
           nativeQuery = true)
    List<User> findUsersRegisteredAfter(@Param("date") LocalDate date);
    
    // Update query
    @Modifying
    @Query("UPDATE User u SET u.active = false WHERE u.lastLogin < :date")
    int deactivateInactiveUsers(@Param("date") LocalDate date);
    
    // Projekcje - tylko wybrane pola
    @Query("SELECT u.name, u.email FROM User u WHERE u.age > :age")
    List<Object[]> findNameAndEmailByAge(@Param("age") int age);
}

Praktyczny przykład – blog

Zobaczmy Spring Data JPA w praktyce na przykładzie prostego bloga:

// Encja Post
@Entity
public class Post {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String title;
    private String content;
    private LocalDateTime createdDate;
    private boolean published;
    
    @ManyToOne
    private User author;
    
    // gettery i settery
}

// Repository dla postów
public interface PostRepository extends JpaRepository<Post, Long> {
    
    List<Post> findByPublishedTrue();
    List<Post> findByAuthor(User author);
    List<Post> findByTitleContainingAndPublishedTrue(String keyword);
    
    @Query("SELECT p FROM Post p WHERE p.createdDate >= :date ORDER BY p.createdDate DESC")
    List<Post> findRecentPosts(@Param("date") LocalDateTime date, Pageable pageable);
    
    @Query("SELECT COUNT(p) FROM Post p WHERE p.author = :author AND p.published = true")
    long countPublishedPostsByAuthor(@Param("author") User author);
}

Best practices i tipsy

Best practices:

  • Używaj query methods do prostych zapytań, @Query do złożonych
  • Preferuj JPQL nad native SQL dla przenośności
  • Używaj Pageable dla dużych zbiorów danych
  • Nazywaj metody opisowo: findActiveUsersByCity() lepiej niż findBy()
Uwaga na N+1 problem: Query methods mogą generować dodatkowe zapytania dla lazy loading. Użyj @EntityGraph lub JOIN FETCH w @Query.
Czy Spring Data JPA zastępuje Hibernate?

Nie, Spring Data JPA to warstwa abstrakcji nad JPA. Pod spodem nadal używa Hibernate (lub innej implementacji JPA) do komunikacji z bazą danych.

Jak debugować generowane zapytania?

Ustaw spring.jpa.show-sql=true i logging.level.org.hibernate.SQL=DEBUG w application.properties żeby zobaczyć generowane SQL.

Czy mogę mieszać query methods z @Query?

Tak! W jednym repository możesz mieć query methods, @Query, a nawet custom implementations. Spring Data JPA jest bardzo elastyczne.

Jakie są limity query methods?

Query methods mają ograniczenia dla bardzo złożonych zapytań z JOIN, subqueries czy window functions. W takich przypadkach użyj @Query.

Jak testować repository?

Użyj @DataJpaTest do testów integracyjnych repository z in-memory bazą H2. To szybkie i nie wymaga pełnego kontekstu Spring.

Czy Spring Data JPA jest szybkie?

Tak, ale musisz uważać na lazy loading i N+1 problem. Używaj @EntityGraph, JOIN FETCH lub Projection do optymalizacji.

Przydatne zasoby:

🚀 Zadanie dla Ciebie

Stwórz repository dla systemu biblioteki z encjami Book i Author:

  • Znajdź książki po tytule (częściowe dopasowanie)
  • Znajdź książki danego autora
  • Znajdź dostępne książki (nie wypożyczone)
  • Znajdź książki wydane w określonym roku
  • Policz ile książek napisał dany autor
  • Użyj paginacji dla listy wszystkich książek

Przetestuj wszystkie metody i sprawdź jakie SQL jest generowane.

Masz pytania o Spring Data JPA? Podziel się swoimi doświadczeniami w komentarzach – Spring Data znacznie przyspiesza development, ale ma swoje niuanse!

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry