Spring Data JPA
1. JPA Entity
1.1 Entity Mapping
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(unique = true)
private String email;
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
@Enumerated(EnumType.STRING)
private UserStatus status;
// Getters and setters
}
1.2 Relationships
// One-to-One
@Entity
public class User {
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
}
// One-to-Many
@Entity
public class User {
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
}
@Entity
public class Order {
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
}
// Many-to-Many
@Entity
public class User {
@ManyToMany
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
}
2. JPA Repository
2.1 Repository Interface
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Derived Query Methods
List<User> findByLastName(String lastName);
Optional<User> findByEmail(String email);
List<User> findByStatusAndCreatedAtBefore(
UserStatus status, Date date);
// Query Methods with JPQL
@Query("SELECT u FROM User u WHERE u.status = :status")
List<User> findByStatus(@Param("status") UserStatus status);
// Native Query
@Query(value = "SELECT * FROM users WHERE status = ?1",
nativeQuery = true)
List<User> findByStatusNative(String status);
// Modifying Queries
@Modifying
@Transactional
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateUserStatus(@Param("id") Long id,
@Param("status") UserStatus status);
}
2.2 Pagination và Sorting
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public Page<User> findPaginated(int page, int size) {
return userRepository.findAll(
PageRequest.of(page, size,
Sort.by("lastName").ascending()
.and(Sort.by("firstName")))
);
}
public List<User> findAllSorted() {
return userRepository.findAll(
Sort.by("lastName").ascending()
);
}
}
3. Specifications
3.1 Custom Specifications
@Repository
public interface UserRepository extends
JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
public class UserSpecifications {
public static Specification<User> hasStatus(UserStatus status) {
return (root, query, cb) -> {
if (status == null) {
return cb.isTrue(cb.literal(true));
}
return cb.equal(root.get("status"), status);
};
}
public static Specification<User> emailContains(String email) {
return (root, query, cb) -> {
if (email == null) {
return cb.isTrue(cb.literal(true));
}
return cb.like(root.get("email"), "%" + email + "%");
};
}
}
@Service
public class UserService {
public List<User> findUsers(UserStatus status, String email) {
return userRepository.findAll(
Specification
.where(hasStatus(status))
.and(emailContains(email))
);
}
}
4. Auditing
4.1 Configuration
@Configuration
@EnableJpaAuditing
public class JpaConfig {
@Bean
public AuditorAware<String> auditorProvider() {
return new SpringSecurityAuditorAware();
}
}
public class SpringSecurityAuditorAware
implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
if (auth == null || !auth.isAuthenticated()) {
return Optional.empty();
}
return Optional.of(auth.getName());
}
}
4.2 Auditable Entity
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class Auditable {
@CreatedBy
protected String createdBy;
@CreatedDate
protected Instant createdDate;
@LastModifiedBy
protected String lastModifiedBy;
@LastModifiedDate
protected Instant lastModifiedDate;
// Getters and setters
}
@Entity
public class User extends Auditable {
// User specific fields
}
5. Transaction Management
5.1 Transaction Annotation
@Service
public class UserService {
@Transactional(readOnly = true)
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
@Transactional(
rollbackFor = Exception.class,
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED
)
public User createUser(User user) {
// Business logic
return userRepository.save(user);
}
}
5.2 Programmatic Transaction Management
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public User createUserProgrammatically(User user) {
return transactionTemplate.execute(status -> {
try {
// Business logic
return userRepository.save(user);
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
}
6. Best Practices
6.1 Lazy Loading
@Entity
public class User {
@OneToMany(
mappedBy = "user",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL
)
private List<Order> orders;
}
@Service
public class UserService {
@Transactional(readOnly = true)
public List<Order> getUserOrders(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(userId));
// Orders will be loaded here
return user.getOrders();
}
}
6.2 Batch Operations
```java
@Repository
public interface UserRepository extends JpaRepository
@Service
public class UserService {
@Transactional
public void deactivateUsers(List> batches = Lists.partition(userIds, 1000);
for (List