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.
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
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); } }
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); }
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:
Metoda | Generowane 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
- 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()
Nie, Spring Data JPA to warstwa abstrakcji nad JPA. Pod spodem nadal używa Hibernate (lub innej implementacji JPA) do komunikacji z bazą danych.
Ustaw spring.jpa.show-sql=true
i logging.level.org.hibernate.SQL=DEBUG
w application.properties żeby zobaczyć generowane SQL.
Tak! W jednym repository możesz mieć query methods, @Query, a nawet custom implementations. Spring Data JPA jest bardzo elastyczne.
Query methods mają ograniczenia dla bardzo złożonych zapytań z JOIN, subqueries czy window functions. W takich przypadkach użyj @Query.
Użyj @DataJpaTest do testów integracyjnych repository z in-memory bazą H2. To szybkie i nie wymaga pełnego kontekstu Spring.
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!