75 phút
Spring Data JPA và Entity
Entity Mapping
Entity cơ bản
package com.example.demo.entity;
import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String password;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private UserRole role;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
}
enum UserRole {
USER, ADMIN
}
Relationships
One-to-Many / Many-to-One
@Entity
public class User {
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Post> posts = new ArrayList<>();
}
@Entity
public class Post {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
}
Many-to-Many
@Entity
public class Student {
@ManyToMany
@JoinTable(
name = "student_courses",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}
One-to-One
@Entity
public class User {
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private Profile profile;
}
@Entity
public class Profile {
@OneToOne
@JoinColumn(name = "user_id")
private User user;
}
Repositories
JpaRepository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByNameContainingIgnoreCase(String name);
boolean existsByEmail(String email);
@Query("SELECT u FROM User u WHERE u.role = :role")
List<User> findByRole(@Param("role") UserRole role);
@Query(value = "SELECT * FROM users WHERE created_at > :date", nativeQuery = true)
List<User> findRecentUsers(@Param("date") LocalDateTime date);
@Modifying
@Query("UPDATE User u SET u.role = :role WHERE u.id = :id")
int updateRole(@Param("id") Long id, @Param("role") UserRole role);
}
Derived query methods
// Các keyword phổ biến
findByFirstName(String firstName)
findByFirstNameAndLastName(String fn, String ln)
findByAgeGreaterThan(int age)
findByAgeBetween(int min, int max)
findByNameLike(String pattern)
findByNameContaining(String substring)
findByNameStartingWith(String prefix)
findByOrderByCreatedAtDesc()
findTop10ByOrderByCreatedAtDesc()
Pagination và Sorting
public interface UserRepository extends JpaRepository<User, Long> {
Page<User> findByNameContaining(String name, Pageable pageable);
}
// Sử dụng
Pageable pageable = PageRequest.of(0, 10, Sort.by("createdAt").descending());
Page<User> page = repository.findByNameContaining("John", pageable);
System.out.println(page.getTotalElements());
System.out.println(page.getTotalPages());
Service Layer
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
private final UserRepository userRepository;
@Transactional
public UserResponse create(CreateUserRequest request) {
if (userRepository.existsByEmail(request.getEmail())) {
throw new BusinessException("Email already exists");
}
User user = User.builder()
.name(request.getName())
.email(request.getEmail())
.password(passwordEncoder.encode(request.getPassword()))
.role(UserRole.USER)
.build();
User saved = userRepository.save(user);
return UserResponse.from(saved);
}
public UserResponse findById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new NotFoundException("User not found"));
return UserResponse.from(user);
}
public Page<UserResponse> findAll(Pageable pageable) {
return userRepository.findAll(pageable).map(UserResponse::from);
}
@Transactional
public UserResponse update(Long id, UpdateUserRequest request) {
User user = userRepository.findById(id)
.orElseThrow(() -> new NotFoundException("User not found"));
user.setName(request.getName());
return UserResponse.from(user);
}
@Transactional
public void delete(Long id) {
userRepository.deleteById(id);
}
}
DTOs
public record CreateUserRequest(
@NotBlank String name,
@Email @NotBlank String email,
@Size(min = 8) String password
) {}
public record UserResponse(
Long id,
String name,
String email,
String role,
LocalDateTime createdAt
) {
public static UserResponse from(User user) {
return new UserResponse(
user.getId(),
user.getName(),
user.getEmail(),
user.getRole().name(),
user.getCreatedAt()
);
}
}
Configuration
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: postgres
password: postgres
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: validate
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.PostgreSQLDialect
flyway:
enabled: true
locations: classpath:db/migration
Bài tập thực hành
Hãy tạo CRUD API cho User với JPA!