[Java Backend Zero to Hello] BÀI 5.4: SPRING DATA JPA
📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 5: Spring Framework & Spring Boot | Bài 50/86
BÀI 5.4: SPRING DATA JPA
Mục tiêu
- Sử dụng Spring Data JPA
- Tạo Repository interface
- Method naming convention
- Custom query với @Query
1. SPRING DATA JPA LÀ GÌ?
Spring Data JPA là module của Spring Data, giúp đơn giản hóa việc truy cập database với JPA.
Lợi ích
- Ít code boilerplate
- Tự động generate query
- Hỗ trợ pagination, sorting
- Tích hợp với Spring Boot
2. DEPENDENCY
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
3. ENTITY
@Entity
@Table(name = "users")
@Data // Lombok
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Column(unique = true, nullable = false)
private String email;
private Integer age;
@Column(name = "created_at", updatable = false)
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
4. REPOSITORY
4.1 JpaRepository
public interface UserRepository extends JpaRepository<User, Long> {
// Tự động có sẵn:
// save(), findById(), findAll(), deleteById(), count(), ...
}
4.2 CrudRepository
public interface UserRepository extends CrudRepository<User, Long> {
// CRUD cơ bản
}
4.3 PagingAndSortingRepository
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
// Hỗ trợ pagination, sorting
}
4.4 Các method có sẵn
| Method | Mô tả |
|---|---|
save(entity) |
Lưu (insert/update) |
findById(id) |
Tìm theo ID |
findAll() |
Lấy tất cả |
findAllById(ids) |
Tìm theo nhiều ID |
count() |
Đếm |
existsById(id) |
Kiểm tra tồn tại |
deleteById(id) |
Xóa theo ID |
delete(entity) |
Xóa entity |
deleteAll() |
Xóa tất cả |
5. METHOD NAMING CONVENTION
Spring Data tự động generate query từ tên method.
5.1 Cú pháp
findBy + PropertyName + Condition + OrderBy + PropertyName + Direction
5.2 Ví dụ
public interface UserRepository extends JpaRepository<User, Long> {
// Tìm theo field
Optional<User> findByEmail(String email);
List<User> findByName(String name);
List<User> findByAge(Integer age);
// Điều kiện
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeLessThanEqual(Integer age);
List<User> findByNameLike(String pattern);
List<User> findByNameContaining(String keyword);
List<User> findByNameStartingWith(String prefix);
List<User> findByNameEndingWith(String suffix);
List<User> findByAgeBetween(Integer min, Integer max);
List<User> findByAgeIn(Collection<Integer> ages);
List<User> findByNameIsNull();
List<User> findByNameIsNotNull();
// Boolean
List<User> findByActiveTrue();
List<User> findByActiveFalse();
// Kết hợp AND/OR
List<User> findByNameAndEmail(String name, String email);
List<User> findByNameOrEmail(String name, String email);
// Sắp xếp
List<User> findByAgeOrderByNameAsc(Integer age);
List<User> findByAgeOrderByNameDesc(Integer age);
// Giới hạn
List<User> findFirst10ByOrderByAgeDesc();
User findFirstByOrderByCreatedAtDesc();
// Đếm
long countByAgeGreaterThan(Integer age);
boolean existsByEmail(String email);
// Xóa
long deleteByAgeLessThan(Integer age);
}
5.3 Từ khóa hỗ trợ
| Keyword | SQL |
|---|---|
Is, Equals |
= |
Not |
<> |
Like |
LIKE |
Containing |
LIKE %x% |
StartingWith |
LIKE x% |
EndingWith |
LIKE %x |
GreaterThan |
> |
LessThan |
< |
Between |
BETWEEN |
In |
IN |
IsNull |
IS NULL |
IsNotNull |
IS NOT NULL |
True |
= true |
False |
= false |
And |
AND |
Or |
OR |
OrderBy |
ORDER BY |
6. @QUERY - CUSTOM QUERY
6.1 JPQL
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmailCustom(@Param("email") String email);
@Query("SELECT u FROM User u WHERE u.age >= :minAge ORDER BY u.name")
List<User> findAdults(@Param("minAge") int minAge);
@Query("SELECT u.name, u.email FROM User u")
List<Object[]> findNameAndEmail();
@Query("SELECT new com.example.dto.UserDTO(u.name, u.email) FROM User u")
List<UserDTO> findAllDTO();
}
6.2 Native Query
@Query(value = "SELECT * FROM users WHERE email = :email", nativeQuery = true)
Optional<User> findByEmailNative(@Param("email") String email);
6.3 Update Query
@Modifying
@Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
int updateName(@Param("id") Long id, @Param("name") String name);
6.4 Delete Query
@Modifying
@Query("DELETE FROM User u WHERE u.age < :age")
int deleteOldUsers(@Param("age") int age);
7. PAGINATION
public interface UserRepository extends JpaRepository<User, Long> {
Page<User> findByAgeGreaterThan(int age, Pageable pageable);
Slice<User> findByNameContaining(String name, Pageable pageable);
}
Sử dụng
Pageable pageable = PageRequest.of(0, 10, Sort.by("name").ascending());
Page<User> page = userRepository.findByAgeGreaterThan(18, pageable);
List<User> content = page.getContent();
int totalPages = page.getTotalPages();
long totalElements = page.getTotalElements();
boolean hasNext = page.hasNext();
8. SORTING
List<User> findAll(Sort.by("name").ascending());
List<User> findAll(Sort.by(Sort.Order.asc("lastName"), Sort.Order.desc("firstName")));
9. CUSTOM REPOSITORY
9.1 Interface
public interface UserRepositoryCustom {
List<User> findByCustomQuery();
}
9.2 Implementation
@Repository
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepositoryCustom {
@PersistenceContext
private EntityManager em;
@Override
public List<User> findByCustomQuery() {
return em.createQuery("SELECT u FROM User u WHERE ...", User.class)
.getResultList();
}
}
9.3 Kết hợp
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
}
10. SPECIFICATIONS (Dynamic Query)
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
public class UserSpecs {
public static Specification<User> hasName(String name) {
return (root, query, cb) ->
name == null ? null : cb.like(root.get("name"), "%" + name + "%");
}
public static Specification<User> hasMinAge(Integer age) {
return (root, query, cb) ->
age == null ? null : cb.greaterThanOrEqualTo(root.get("age"), age);
}
}
// Sử dụng
Specification<User> spec = Specification.where(hasName("An"))
.and(hasMinAge(18));
List<User> users = userRepository.findAll(spec);
11. ENTITY GRAPH (Eager Loading)
@Entity
@NamedEntityGraph(name = "User.orders",
attributeNodes = @NamedAttributeNode("orders"))
public class User {
@OneToMany(mappedBy = "user")
private List<Order> orders;
}
@EntityGraph(value = "User.orders")
Optional<User> findById(Long id);
12. AUDITING
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class Auditable {
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
private String createdBy;
@LastModifiedBy
private String updatedBy;
}
Enable: @EnableJpaAuditing trên main class.
13. BÀI TẬP THỰC HÀNH
Bài 1: User Repository
Tạo UserRepository với:
- findByEmail
- findByAgeGreaterThan
- existsByUsername
- countByStatus
Bài 2: Custom Query
Viết query:
- Tìm user có nhiều orders nhất
- Tính tổng giá trị orders
Bài 3: Pagination
Implement API trả về user có phân trang.
14. TÓM TẮT
| Khái niệm | Mô tả |
|---|---|
| JpaRepository | Interface chính |
| Method Naming | Tự sinh query |
| @Query | Custom JPQL/Native |
| Pageable | Phân trang |
| Sort | Sắp xếp |
| Specification | Dynamic query |
| EntityGraph | Eager loading |
| Auditing | Tự động track |
Bài tiếp theo: 5.5 Spring Web MVC
🧭 Điều Hướng Series
⬅️ Bài trước: BÀI 5.3: BEAN & DEPENDENCY INJECTION
📋 Lộ trình tổng quan: Xem Toàn Bộ Series
➡️ Bài tiếp theo: BÀI 5.5: SPRING WEB MVC
All rights reserved