Spring数据查询
外观
Spring数据查询[编辑 | 编辑源代码]
概述[编辑 | 编辑源代码]
Spring数据查询是Spring Data模块的核心功能之一,它允许开发者通过简单的方法命名约定、注解或自定义查询语言(如JPQL、Native SQL)来访问和操作数据库。Spring Data提供了统一的抽象层,支持多种数据存储技术(如JPA、MongoDB、Redis等),显著减少了样板代码的编写。
Spring数据查询的主要特点包括:
- 方法名派生查询:通过解析方法名自动生成查询
- @Query注解:支持JPQL或原生SQL
- 动态查询:通过`Specification`或`QueryDSL`实现复杂条件组合
- 分页与排序:内置支持
查询方法类型[编辑 | 编辑源代码]
方法名派生查询[编辑 | 编辑源代码]
Spring Data可以通过解析Repository接口中的方法名自动生成查询。规则如下:
public interface UserRepository extends JpaRepository<User, Long> {
// 根据属性名查询
List<User> findByLastName(String lastName);
// 多条件查询
List<User> findByFirstNameAndLastName(String firstName, String lastName);
// 排序
List<User> findByAgeGreaterThan(int age, Sort sort);
// 分页
Page<User> findByActiveTrue(Pageable pageable);
}
命名约定:
- 关键字:`findBy`、`readBy`、`queryBy`、`countBy`、`deleteBy`
- 条件:`And`、`Or`、`Between`、`LessThan`、`Like`、`IgnoreCase`
- 排序:`OrderBy[属性]Asc/Desc`
@Query注解[编辑 | 编辑源代码]
对于复杂查询,可以使用`@Query`注解定义JPQL或原生SQL:
@Query("SELECT u FROM User u WHERE u.email LIKE %?1%")
List<User> findByEmailContaining(String emailPart);
@Query(value = "SELECT * FROM users WHERE registration_date > ?1", nativeQuery = true)
List<User> findRegisteredAfter(Date date);
动态查询[编辑 | 编辑源代码]
Specification接口[编辑 | 编辑源代码]
实现`JpaSpecificationExecutor`接口后,可以使用`Specification`构建动态查询:
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {}
// 使用示例
Specification<User> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (name != null) {
predicates.add(cb.like(root.get("name"), "%" + name + "%"));
}
if (minAge != null) {
predicates.add(cb.ge(root.get("age"), minAge));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
List<User> users = userRepository.findAll(spec);
QueryDSL集成[编辑 | 编辑源代码]
更类型安全的动态查询方案:
QUser user = QUser.user;
BooleanExpression predicate = user.firstName.contains("John")
.and(user.age.between(20, 30));
Iterable<User> users = userRepository.findAll(predicate);
分页与排序[编辑 | 编辑源代码]
Spring Data提供开箱即用的分页支持:
Pageable pageable = PageRequest.of(0, 10, Sort.by("lastName").ascending());
Page<User> page = userRepository.findByActiveTrue(pageable);
// 获取结果
List<User> content = page.getContent();
int totalPages = page.getTotalPages();
投影与DTO[编辑 | 编辑源代码]
可以定义接口投影或类投影来返回部分字段:
// 接口投影
public interface NameOnly {
String getFirstName();
String getLastName();
}
List<NameOnly> users = userRepository.findByDepartment("IT");
// 类投影
@Query("SELECT new com.example.UserDto(u.firstName, u.lastName) FROM User u")
List<UserDto> findUserDtos();
性能优化[编辑 | 编辑源代码]
实体图[编辑 | 编辑源代码]
通过`@EntityGraph`定义关联加载策略:
@EntityGraph(attributePaths = {"addresses", "roles"})
List<User> findAllWithAddressesAndRoles();
批量处理[编辑 | 编辑源代码]
使用`@Modifying`进行批量更新:
@Modifying
@Query("UPDATE User u SET u.active = ?2 WHERE u.id IN ?1")
int updateActiveStatus(List<Long> ids, boolean active);
实际案例[编辑 | 编辑源代码]
电商平台商品搜索[编辑 | 编辑源代码]
实现代码:
public Page<Product> searchProducts(String name,
BigDecimal minPrice,
BigDecimal maxPrice,
String category,
Pageable pageable) {
Specification<Product> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.hasText(name)) {
predicates.add(cb.like(root.get("name"), "%" + name + "%"));
}
if (minPrice != null) {
predicates.add(cb.ge(root.get("price"), minPrice));
}
if (maxPrice != null) {
predicates.add(cb.le(root.get("price"), maxPrice));
}
if (StringUtils.hasText(category)) {
predicates.add(cb.equal(root.get("category").get("name"), category));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
return productRepository.findAll(spec, pageable);
}
数学表达式示例[编辑 | 编辑源代码]
Spring Data JPA中的分页总页数计算:
其中:
- = 总记录数
- = 每页大小
总结[编辑 | 编辑源代码]
Spring数据查询提供了多种灵活的方式来访问数据:
- 简单查询优先使用方法名派生
- 复杂查询使用`@Query`或`Specification`
- 动态查询考虑`QueryDSL`
- 性能敏感场景使用投影和实体图
- 大数据量操作使用批量处理
通过合理组合这些技术,可以在保持代码简洁的同时满足各种业务查询需求。