Spring Data JPA详解:从入门到实战
前言
Spring Data JPA是Spring Data家族中最常用的模块之一,它在JPA规范之上提供了更高层次的抽象,极大地简化了数据访问层的开发。通过Spring Data JPA,开发者只需定义接口,无需编写实现类,即可完成常见的CRUD操作。本文将深入讲解Spring Data JPA的核心概念、使用方法和实战技巧。
一、Spring Data JPA概述
1.1 什么是Spring Data JPA
Spring Data JPA是Spring Data项目的一部分,它简化了基于JPA的数据访问层开发。
scss
Spring Data JPA架构
┌─────────────────────────────────────────┐
│ Application Layer │
│ (Service/Controller) │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ Spring Data JPA │
│ ┌───────────────────────────────────┐ │
│ │ Repository Interface │ │
│ │ - JpaRepository │ │
│ │ - CrudRepository │ │
│ │ - PagingAndSortingRepository │ │
│ └───────────────────────────────────┘ │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ JPA (Hibernate) │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ Database │
└─────────────────────────────────────────┘
1.2 Repository接口体系
scss
Repository接口继承关系
┌───────────────────────────────┐
│ Repository<T, ID> │ ← 标记接口
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ CrudRepository<T, ID> │ ← 基本CRUD
│ - save(), findById() │
│ - delete(), count() │
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ PagingAndSortingRepository │ ← 分页排序
│ - findAll(Pageable) │
│ - findAll(Sort) │
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ JpaRepository<T, ID> │ ← JPA特有
│ - flush(), saveAndFlush() │
│ - deleteInBatch() │
└───────────────────────────────┘
二、快速开始
2.1 Maven依赖
xml
<dependencies>
<!-- Spring Boot Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2.2 配置文件
yaml
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update # create/update/validate/none
show-sql: true
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQL8Dialect
2.3 实体类定义
java
/**
* 用户实体
*/
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String username;
@Column(nullable = false)
private String password;
@Column(unique = true)
private String email;
private Integer age;
@Enumerated(EnumType.STRING)
private UserStatus status;
@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
// 一对多关系
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders;
}
/**
* 用户状态枚举
*/
public enum UserStatus {
ACTIVE, INACTIVE, LOCKED
}
/**
* 订单实体
*/
@Entity
@Table(name = "orders")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String orderNo;
@Column(precision = 10, scale = 2)
private BigDecimal amount;
@Enumerated(EnumType.STRING)
private OrderStatus status;
// 多对一关系
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@CreationTimestamp
private LocalDateTime createdAt;
}
public enum OrderStatus {
PENDING, PAID, SHIPPED, COMPLETED, CANCELLED
}
2.4 Repository接口
java
/**
* 用户Repository
*/
public interface UserRepository extends JpaRepository<User, Long> {
// 基于方法名的查询
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
List<User> findByStatus(UserStatus status);
List<User> findByAgeBetween(Integer minAge, Integer maxAge);
}
/**
* 订单Repository
*/
public interface OrderRepository extends JpaRepository<Order, Long> {
Optional<Order> findByOrderNo(String orderNo);
List<Order> findByUserId(Long userId);
List<Order> findByStatus(OrderStatus status);
}
2.5 Service层
java
/**
* 用户服务
*/
@Service
@Transactional(readOnly = true)
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> findAll() {
return userRepository.findAll();
}
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
@Transactional
public User save(User user) {
return userRepository.save(user);
}
@Transactional
public void deleteById(Long id) {
userRepository.deleteById(id);
}
}
三、查询方法
3.1 方法名派生查询
java
/**
* 方法名派生查询示例
*/
public interface UserRepository extends JpaRepository<User, Long> {
// 等值查询
User findByUsername(String username);
List<User> findByStatus(UserStatus status);
// And/Or组合
List<User> findByUsernameAndEmail(String username, String email);
List<User> findByUsernameOrEmail(String username, String email);
// 比较查询
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeLessThanEqual(Integer age);
List<User> findByAgeBetween(Integer min, Integer max);
// Like查询
List<User> findByUsernameLike(String pattern);
List<User> findByUsernameContaining(String keyword);
List<User> findByUsernameStartingWith(String prefix);
List<User> findByUsernameEndingWith(String suffix);
// Null检查
List<User> findByEmailIsNull();
List<User> findByEmailIsNotNull();
// In查询
List<User> findByStatusIn(Collection<UserStatus> statuses);
List<User> findByIdIn(List<Long> ids);
// 排序
List<User> findByStatusOrderByCreatedAtDesc(UserStatus status);
// 限制结果
User findFirstByOrderByCreatedAtDesc();
List<User> findTop10ByStatus(UserStatus status);
// 统计
long countByStatus(UserStatus status);
boolean existsByUsername(String username);
// 删除
void deleteByStatus(UserStatus status);
}
3.2 @Query注解查询
java
/**
* @Query注解查询
*/
public interface UserRepository extends JpaRepository<User, Long> {
// JPQL查询
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmailJPQL(@Param("email") String email);
// 原生SQL查询
@Query(value = "SELECT * FROM users WHERE status = :status", nativeQuery = true)
List<User> findByStatusNative(@Param("status") String status);
// 带分页的查询
@Query("SELECT u FROM User u WHERE u.status = :status")
Page<User> findByStatusWithPage(@Param("status") UserStatus status, Pageable pageable);
// 投影查询
@Query("SELECT u.username, u.email FROM User u WHERE u.id = :id")
Object[] findUsernameAndEmailById(@Param("id") Long id);
// 更新操作
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateStatusById(@Param("id") Long id, @Param("status") UserStatus status);
// 删除操作
@Modifying
@Query("DELETE FROM User u WHERE u.status = :status")
int deleteByStatusJPQL(@Param("status") UserStatus status);
// 复杂条件查询
@Query("SELECT u FROM User u WHERE " +
"(:username IS NULL OR u.username LIKE %:username%) AND " +
"(:status IS NULL OR u.status = :status) AND " +
"(:minAge IS NULL OR u.age >= :minAge)")
List<User> searchUsers(@Param("username") String username,
@Param("status") UserStatus status,
@Param("minAge") Integer minAge);
// 关联查询
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);
// 统计查询
@Query("SELECT u.status, COUNT(u) FROM User u GROUP BY u.status")
List<Object[]> countByStatusGroup();
}
3.3 Specification动态查询
java
/**
* 启用JpaSpecificationExecutor
*/
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
/**
* 用户查询条件
*/
@Data
public class UserSearchCriteria {
private String username;
private String email;
private UserStatus status;
private Integer minAge;
private Integer maxAge;
private LocalDateTime startDate;
private LocalDateTime endDate;
}
/**
* 用户Specification构建器
*/
public class UserSpecifications {
public static Specification<User> withCriteria(UserSearchCriteria criteria) {
return (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
// 用户名模糊查询
if (StringUtils.hasText(criteria.getUsername())) {
predicates.add(cb.like(root.get("username"),
"%" + criteria.getUsername() + "%"));
}
// 邮箱精确查询
if (StringUtils.hasText(criteria.getEmail())) {
predicates.add(cb.equal(root.get("email"), criteria.getEmail()));
}
// 状态查询
if (criteria.getStatus() != null) {
predicates.add(cb.equal(root.get("status"), criteria.getStatus()));
}
// 年龄范围
if (criteria.getMinAge() != null) {
predicates.add(cb.greaterThanOrEqualTo(root.get("age"), criteria.getMinAge()));
}
if (criteria.getMaxAge() != null) {
predicates.add(cb.lessThanOrEqualTo(root.get("age"), criteria.getMaxAge()));
}
// 日期范围
if (criteria.getStartDate() != null) {
predicates.add(cb.greaterThanOrEqualTo(root.get("createdAt"),
criteria.getStartDate()));
}
if (criteria.getEndDate() != null) {
predicates.add(cb.lessThanOrEqualTo(root.get("createdAt"),
criteria.getEndDate()));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
}
// 组合Specification
public static Specification<User> hasStatus(UserStatus status) {
return (root, query, cb) -> cb.equal(root.get("status"), status);
}
public static Specification<User> usernameLike(String username) {
return (root, query, cb) -> cb.like(root.get("username"), "%" + username + "%");
}
public static Specification<User> ageBetween(Integer min, Integer max) {
return (root, query, cb) -> cb.between(root.get("age"), min, max);
}
}
/**
* 使用Specification查询
*/
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public Page<User> search(UserSearchCriteria criteria, Pageable pageable) {
Specification<User> spec = UserSpecifications.withCriteria(criteria);
return userRepository.findAll(spec, pageable);
}
public List<User> findActiveAdults() {
// 组合多个Specification
Specification<User> spec = Specification
.where(UserSpecifications.hasStatus(UserStatus.ACTIVE))
.and(UserSpecifications.ageBetween(18, 60));
return userRepository.findAll(spec);
}
}
四、分页与排序
4.1 分页查询
java
/**
* 分页查询示例
*/
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 基础分页查询
*/
public Page<User> findAllWithPage(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return userRepository.findAll(pageable);
}
/**
* 带排序的分页
*/
public Page<User> findAllWithPageAndSort(int page, int size) {
Sort sort = Sort.by(Sort.Direction.DESC, "createdAt");
Pageable pageable = PageRequest.of(page, size, sort);
return userRepository.findAll(pageable);
}
/**
* 多字段排序
*/
public Page<User> findWithMultiSort(int page, int size) {
Sort sort = Sort.by(
Sort.Order.desc("status"),
Sort.Order.asc("username"),
Sort.Order.desc("createdAt")
);
Pageable pageable = PageRequest.of(page, size, sort);
return userRepository.findAll(pageable);
}
/**
* 处理分页结果
*/
public PageResult<UserDTO> findUsersPage(int page, int size) {
Page<User> userPage = findAllWithPage(page, size);
return PageResult.<UserDTO>builder()
.content(userPage.getContent().stream()
.map(this::convertToDTO)
.collect(Collectors.toList()))
.totalElements(userPage.getTotalElements())
.totalPages(userPage.getTotalPages())
.currentPage(userPage.getNumber())
.pageSize(userPage.getSize())
.hasNext(userPage.hasNext())
.hasPrevious(userPage.hasPrevious())
.build();
}
private UserDTO convertToDTO(User user) {
return UserDTO.builder()
.id(user.getId())
.username(user.getUsername())
.email(user.getEmail())
.build();
}
}
/**
* 分页结果封装
*/
@Data
@Builder
public class PageResult<T> {
private List<T> content;
private long totalElements;
private int totalPages;
private int currentPage;
private int pageSize;
private boolean hasNext;
private boolean hasPrevious;
}
4.2 Slice分页
java
/**
* Slice分页(适用于无限滚动场景)
*/
public interface UserRepository extends JpaRepository<User, Long> {
Slice<User> findByStatus(UserStatus status, Pageable pageable);
}
@Service
public class UserService {
/**
* Slice分页查询
* 不查询总数,性能更好
*/
public Slice<User> findByStatusSlice(UserStatus status, int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return userRepository.findByStatus(status, pageable);
}
}
五、实体关系映射
5.1 一对多/多对一
java
/**
* 部门实体(一方)
*/
@Entity
@Table(name = "departments")
@Data
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 一对多:一个部门有多个员工
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employees = new ArrayList<>();
// 便捷方法
public void addEmployee(Employee employee) {
employees.add(employee);
employee.setDepartment(this);
}
public void removeEmployee(Employee employee) {
employees.remove(employee);
employee.setDepartment(null);
}
}
/**
* 员工实体(多方)
*/
@Entity
@Table(name = "employees")
@Data
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 多对一:多个员工属于一个部门
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Department department;
}
5.2 多对多
java
/**
* 学生实体
*/
@Entity
@Table(name = "students")
@Data
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// 多对多关系
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "student_courses",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
public void addCourse(Course course) {
courses.add(course);
course.getStudents().add(this);
}
public void removeCourse(Course course) {
courses.remove(course);
course.getStudents().remove(this);
}
}
/**
* 课程实体
*/
@Entity
@Table(name = "courses")
@Data
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}
5.3 一对一
java
/**
* 用户实体
*/
@Entity
@Table(name = "users")
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
// 一对一关系
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private UserProfile profile;
public void setProfile(UserProfile profile) {
this.profile = profile;
profile.setUser(this);
}
}
/**
* 用户详情
*/
@Entity
@Table(name = "user_profiles")
@Data
public class UserProfile {
@Id
private Long id;
private String avatar;
private String bio;
private String phone;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "user_id")
private User user;
}
六、审计功能
6.1 实体审计
java
/**
* 启用JPA审计
*/
@Configuration
@EnableJpaAuditing
public class JpaConfig {
@Bean
public AuditorAware<String> auditorProvider() {
return () -> {
// 从SecurityContext获取当前用户
// return Optional.of(SecurityContextHolder.getContext()
// .getAuthentication().getName());
return Optional.of("system");
};
}
}
/**
* 审计基类
*/
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Data
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String updatedBy;
}
/**
* 继承审计基类
*/
@Entity
@Table(name = "articles")
@Data
@EqualsAndHashCode(callSuper = true)
public class Article extends BaseEntity {
private String title;
@Lob
private String content;
private Boolean published;
}
6.2 软删除
java
/**
* 支持软删除的基类
*/
@MappedSuperclass
@Data
public abstract class SoftDeleteEntity extends BaseEntity {
@Column(name = "is_deleted")
private Boolean deleted = false;
private LocalDateTime deletedAt;
private String deletedBy;
}
/**
* 软删除Repository
*/
public interface SoftDeleteRepository<T extends SoftDeleteEntity, ID>
extends JpaRepository<T, ID> {
// 只查询未删除的数据
@Query("SELECT e FROM #{#entityName} e WHERE e.deleted = false")
List<T> findAllActive();
@Query("SELECT e FROM #{#entityName} e WHERE e.id = :id AND e.deleted = false")
Optional<T> findActiveById(@Param("id") ID id);
// 软删除
@Modifying
@Query("UPDATE #{#entityName} e SET e.deleted = true, " +
"e.deletedAt = CURRENT_TIMESTAMP WHERE e.id = :id")
void softDelete(@Param("id") ID id);
// 恢复
@Modifying
@Query("UPDATE #{#entityName} e SET e.deleted = false, " +
"e.deletedAt = null WHERE e.id = :id")
void restore(@Param("id") ID id);
}
七、实战案例
7.1 案例1:用户管理系统
java
/**
* 用户DTO
*/
@Data
@Builder
public class UserDTO {
private Long id;
private String username;
private String email;
private Integer age;
private String status;
private LocalDateTime createdAt;
}
/**
* 创建用户请求
*/
@Data
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50)
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 100)
private String password;
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 1)
@Max(value = 150)
private Integer age;
}
/**
* 用户服务
*/
@Service
@Transactional(readOnly = true)
@Slf4j
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 创建用户
*/
@Transactional
public UserDTO createUser(CreateUserRequest request) {
// 检查用户名是否存在
if (userRepository.existsByUsername(request.getUsername())) {
throw new BusinessException("用户名已存在");
}
User user = User.builder()
.username(request.getUsername())
.password(passwordEncoder.encode(request.getPassword()))
.email(request.getEmail())
.age(request.getAge())
.status(UserStatus.ACTIVE)
.build();
User saved = userRepository.save(user);
log.info("用户创建成功: {}", saved.getId());
return convertToDTO(saved);
}
/**
* 分页查询
*/
public PageResult<UserDTO> findUsers(UserSearchCriteria criteria,
int page, int size) {
Pageable pageable = PageRequest.of(page, size,
Sort.by(Sort.Direction.DESC, "createdAt"));
Page<User> userPage = userRepository.findAll(
UserSpecifications.withCriteria(criteria), pageable);
return PageResult.<UserDTO>builder()
.content(userPage.getContent().stream()
.map(this::convertToDTO)
.collect(Collectors.toList()))
.totalElements(userPage.getTotalElements())
.totalPages(userPage.getTotalPages())
.currentPage(page)
.pageSize(size)
.build();
}
/**
* 批量更新状态
*/
@Transactional
public int batchUpdateStatus(List<Long> ids, UserStatus status) {
return userRepository.batchUpdateStatus(ids, status);
}
private UserDTO convertToDTO(User user) {
return UserDTO.builder()
.id(user.getId())
.username(user.getUsername())
.email(user.getEmail())
.age(user.getAge())
.status(user.getStatus().name())
.createdAt(user.getCreatedAt())
.build();
}
}
/**
* 扩展Repository方法
*/
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
boolean existsByUsername(String username);
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id IN :ids")
int batchUpdateStatus(@Param("ids") List<Long> ids,
@Param("status") UserStatus status);
}
7.2 案例2:订单统计分析
java
/**
* 订单统计DTO
*/
@Data
@AllArgsConstructor
public class OrderStatistics {
private String status;
private Long count;
private BigDecimal totalAmount;
}
/**
* 日销售统计
*/
@Data
@AllArgsConstructor
public class DailySales {
private LocalDate date;
private Long orderCount;
private BigDecimal totalAmount;
}
/**
* 订单Repository
*/
public interface OrderRepository extends JpaRepository<Order, Long> {
// 按状态统计
@Query("SELECT new com.example.dto.OrderStatistics(o.status, COUNT(o), SUM(o.amount)) " +
"FROM Order o GROUP BY o.status")
List<OrderStatistics> getStatisticsByStatus();
// 日销售统计
@Query("SELECT new com.example.dto.DailySales(" +
"CAST(o.createdAt AS LocalDate), COUNT(o), SUM(o.amount)) " +
"FROM Order o " +
"WHERE o.createdAt BETWEEN :start AND :end " +
"GROUP BY CAST(o.createdAt AS LocalDate) " +
"ORDER BY CAST(o.createdAt AS LocalDate)")
List<DailySales> getDailySales(@Param("start") LocalDateTime start,
@Param("end") LocalDateTime end);
// 用户订单统计
@Query("SELECT u.username, COUNT(o), SUM(o.amount) " +
"FROM Order o JOIN o.user u " +
"WHERE o.status = 'COMPLETED' " +
"GROUP BY u.id, u.username " +
"ORDER BY SUM(o.amount) DESC")
List<Object[]> getTopCustomers(Pageable pageable);
// 原生SQL复杂查询
@Query(value = "SELECT DATE(created_at) as date, " +
"COUNT(*) as order_count, " +
"SUM(amount) as total_amount " +
"FROM orders " +
"WHERE created_at >= :start " +
"GROUP BY DATE(created_at) " +
"ORDER BY date DESC " +
"LIMIT :limit", nativeQuery = true)
List<Object[]> getRecentDailySales(@Param("start") LocalDateTime start,
@Param("limit") int limit);
}
/**
* 订单统计服务
*/
@Service
@Transactional(readOnly = true)
public class OrderStatisticsService {
@Autowired
private OrderRepository orderRepository;
/**
* 获取订单状态分布
*/
public Map<String, OrderStatistics> getStatusDistribution() {
return orderRepository.getStatisticsByStatus().stream()
.collect(Collectors.toMap(OrderStatistics::getStatus, Function.identity()));
}
/**
* 获取日销售报表
*/
public List<DailySales> getDailySalesReport(int days) {
LocalDateTime end = LocalDateTime.now();
LocalDateTime start = end.minusDays(days);
return orderRepository.getDailySales(start, end);
}
/**
* 获取Top客户
*/
public List<Map<String, Object>> getTopCustomers(int limit) {
Pageable pageable = PageRequest.of(0, limit);
List<Object[]> results = orderRepository.getTopCustomers(pageable);
return results.stream().map(row -> {
Map<String, Object> map = new HashMap<>();
map.put("username", row[0]);
map.put("orderCount", row[1]);
map.put("totalAmount", row[2]);
return map;
}).collect(Collectors.toList());
}
}
7.3 案例3:自定义Repository实现
java
/**
* 自定义Repository接口
*/
public interface CustomUserRepository {
List<User> findByComplexCriteria(UserSearchCriteria criteria);
void batchInsert(List<User> users);
}
/**
* 自定义Repository实现
*/
@Repository
public class CustomUserRepositoryImpl implements CustomUserRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<User> findByComplexCriteria(UserSearchCriteria criteria) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.hasText(criteria.getUsername())) {
predicates.add(cb.like(root.get("username"),
"%" + criteria.getUsername() + "%"));
}
if (criteria.getStatus() != null) {
predicates.add(cb.equal(root.get("status"), criteria.getStatus()));
}
query.where(predicates.toArray(new Predicate[0]));
query.orderBy(cb.desc(root.get("createdAt")));
return entityManager.createQuery(query).getResultList();
}
@Override
@Transactional
public void batchInsert(List<User> users) {
int batchSize = 50;
for (int i = 0; i < users.size(); i++) {
entityManager.persist(users.get(i));
if (i > 0 && i % batchSize == 0) {
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush();
entityManager.clear();
}
}
/**
* 组合自定义Repository
*/
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User>,
CustomUserRepository {
}
八、性能优化
8.1 N+1问题解决
java
/**
* N+1问题示例与解决
*/
public interface OrderRepository extends JpaRepository<Order, Long> {
// ❌ 会产生N+1问题
List<Order> findByStatus(OrderStatus status);
// ✓ 使用JOIN FETCH解决
@Query("SELECT o FROM Order o JOIN FETCH o.user WHERE o.status = :status")
List<Order> findByStatusWithUser(@Param("status") OrderStatus status);
// ✓ 使用EntityGraph
@EntityGraph(attributePaths = {"user"})
List<Order> findWithUserByStatus(OrderStatus status);
}
/**
* EntityGraph定义
*/
@Entity
@NamedEntityGraph(
name = "Order.withUser",
attributeNodes = @NamedAttributeNode("user")
)
public class Order {
// ...
}
// 使用命名EntityGraph
@EntityGraph(value = "Order.withUser")
List<Order> findByStatus(OrderStatus status);
8.2 批量操作优化
java
/**
* 批量操作配置
*/
// application.yml
/*
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
order_inserts: true
order_updates: true
*/
/**
* 批量操作服务
*/
@Service
public class BatchService {
@PersistenceContext
private EntityManager entityManager;
/**
* 批量插入
*/
@Transactional
public void batchInsert(List<User> users) {
int batchSize = 50;
for (int i = 0; i < users.size(); i++) {
entityManager.persist(users.get(i));
if ((i + 1) % batchSize == 0) {
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush();
entityManager.clear();
}
/**
* 批量更新(使用JPQL)
*/
@Transactional
public int batchUpdateStatus(List<Long> ids, UserStatus status) {
return entityManager.createQuery(
"UPDATE User u SET u.status = :status WHERE u.id IN :ids")
.setParameter("status", status)
.setParameter("ids", ids)
.executeUpdate();
}
/**
* 批量删除
*/
@Transactional
public int batchDelete(List<Long> ids) {
return entityManager.createQuery(
"DELETE FROM User u WHERE u.id IN :ids")
.setParameter("ids", ids)
.executeUpdate();
}
}
8.3 只读优化
java
/**
* 只读事务优化
*/
@Service
@Transactional(readOnly = true) // 类级别只读
public class ReportService {
@Autowired
private OrderRepository orderRepository;
/**
* 只读查询(不需要脏检查)
*/
public List<OrderStatistics> getReport() {
return orderRepository.getStatisticsByStatus();
}
/**
* 写操作需要覆盖
*/
@Transactional // 覆盖为读写事务
public void updateOrder(Order order) {
orderRepository.save(order);
}
}
/**
* 使用投影减少数据传输
*/
public interface UserProjection {
Long getId();
String getUsername();
String getEmail();
}
public interface UserRepository extends JpaRepository<User, Long> {
// 只查询需要的字段
List<UserProjection> findProjectedByStatus(UserStatus status);
// DTO投影
@Query("SELECT new com.example.dto.UserDTO(u.id, u.username, u.email) " +
"FROM User u WHERE u.status = :status")
List<UserDTO> findDTOByStatus(@Param("status") UserStatus status);
}
九、最佳实践
9.1 Repository设计原则
java
/**
* Repository最佳实践
*/
// 1. 使用合适的返回类型
public interface UserRepository extends JpaRepository<User, Long> {
// 单个结果用Optional
Optional<User> findByUsername(String username);
// 集合结果用List
List<User> findByStatus(UserStatus status);
// 存在性检查用boolean
boolean existsByEmail(String email);
// 计数用long
long countByStatus(UserStatus status);
}
// 2. 避免暴露实体,使用DTO
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT new com.example.dto.UserDTO(u.id, u.username, u.email) " +
"FROM User u WHERE u.id = :id")
Optional<UserDTO> findDTOById(@Param("id") Long id);
}
// 3. 合理使用懒加载
@Entity
public class Order {
@ManyToOne(fetch = FetchType.LAZY) // 默认EAGER,改为LAZY
@JoinColumn(name = "user_id")
private User user;
}
9.2 事务管理
java
/**
* 事务最佳实践
*/
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserRepository userRepository;
/**
* 正确的事务边界
*/
@Transactional
public Order createOrder(Long userId, CreateOrderRequest request) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException("用户不存在"));
Order order = Order.builder()
.orderNo(generateOrderNo())
.user(user)
.amount(request.getAmount())
.status(OrderStatus.PENDING)
.build();
return orderRepository.save(order);
}
/**
* 事务传播
*/
@Transactional(propagation = Propagation.REQUIRED)
public void processOrder(Long orderId) {
// 在同一事务中执行
updateOrderStatus(orderId, OrderStatus.PROCESSING);
sendNotification(orderId);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logOperation(String message) {
// 独立事务,不受外部事务影响
}
}
十、总结
核心知识点回顾
css
Spring Data JPA核心要点
│
├── 基础概念
│ ├── Repository接口体系
│ ├── 实体映射
│ └── 配置方式
│
├── 查询方法
│ ├── 方法名派生查询
│ ├── @Query注解
│ └── Specification动态查询
│
├── 分页排序
│ ├── Pageable/Page
│ ├── Sort
│ └── Slice
│
├── 实体关系
│ ├── 一对多/多对一
│ ├── 多对多
│ └── 一对一
│
├── 审计功能
│ ├── 创建/修改时间
│ ├── 创建/修改人
│ └── 软删除
│
├── 性能优化
│ ├── N+1问题解决
│ ├── 批量操作
│ └── 只读优化
│
└── 最佳实践
├── Repository设计
├── 事务管理
└── 投影查询
Spring Data JPA极大简化了数据访问层的开发,掌握其核心概念和使用技巧,能够帮助我们高效地开发数据库应用。在实际项目中,要注意性能优化和最佳实践的应用。