Spring Data 2027 动态查询详解别叫我大神叫我 Alex 就好Spring Data 2027 带来了强大的动态查询功能让开发者能够更灵活地构建复杂的查询语句。动态查询是一种根据运行时条件构建查询的能力它在处理复杂的业务场景时非常有用。本文将详细介绍 Spring Data 2027 中的动态查询功能及其实践。1. 动态查询概述1.1 什么是动态查询动态查询是指在运行时根据不同的条件动态构建查询语句的过程。与静态查询不同动态查询的条件和结构可以根据用户输入、业务逻辑或其他因素在运行时发生变化。在 Spring Data 中动态查询通常用于以下场景多条件搜索用户可以根据多个条件组合进行搜索复杂过滤根据不同的业务规则进行复杂的过滤动态排序根据用户选择的字段进行排序条件聚合根据不同的条件进行数据聚合1.2 Spring Data 2027 的动态查询支持Spring Data 2027 提供了多种动态查询的实现方式Query By Example (QBE)基于示例对象构建查询Specification使用 JPA Criteria API 构建复杂查询QueryDSL使用类型安全的查询构建器Criteria API直接使用 JPA Criteria API动态 JPQL运行时构建 JPQL 查询2. Query By Example (QBE)2.1 原理Query By Example 是一种简单直观的动态查询方式它允许开发者通过创建一个示例对象来构建查询。Spring Data 会根据示例对象的非空属性生成查询条件。2.2 实践使用 Query By Example 实现动态查询// 实体类 Entity public class User { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String name; private String email; private int age; private String department; // getters and setters } // Repository 接口 public interface UserRepository extends JpaRepositoryUser, Long, QueryByExampleExecutorUser { } // 服务层 Service public class UserService { private final UserRepository userRepository; Autowired public UserService(UserRepository userRepository) { this.userRepository userRepository; } public ListUser searchUsers(String name, String email, Integer age, String department) { // 创建示例对象 User user new User(); if (name ! null) { user.setName(name); } if (email ! null) { user.setEmail(email); } if (age ! null) { user.setAge(age); } if (department ! null) { user.setDepartment(department); } // 创建 Example 对象 ExampleUser example Example.of(user); // 执行查询 return userRepository.findAll(example); } public ListUser searchUsersWithMatcher(String name, String email) { // 创建示例对象 User user new User(); if (name ! null) { user.setName(name); } if (email ! null) { user.setEmail(email); } // 创建匹配器 ExampleMatcher matcher ExampleMatcher.matching() .withIgnoreCase() // 忽略大小写 .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING); // 包含匹配 // 创建 Example 对象 ExampleUser example Example.of(user, matcher); // 执行查询 return userRepository.findAll(example); } }3. Specification3.1 原理Specification 是 Spring Data JPA 提供的一种更灵活的动态查询方式它基于 JPA Criteria API 构建查询。通过实现Specification接口开发者可以构建复杂的查询条件。3.2 实践使用 Specification 实现动态查询// Repository 接口 public interface UserRepository extends JpaRepositoryUser, Long, JpaSpecificationExecutorUser { } // Specification 工具类 public class UserSpecifications { public static SpecificationUser hasName(String name) { return (root, query, criteriaBuilder) - { if (name null) { return null; } return criteriaBuilder.like(root.get(name), % name %); }; } public static SpecificationUser hasEmail(String email) { return (root, query, criteriaBuilder) - { if (email null) { return null; } return criteriaBuilder.like(root.get(email), % email %); }; } public static SpecificationUser hasAgeGreaterThan(Integer age) { return (root, query, criteriaBuilder) - { if (age null) { return null; } return criteriaBuilder.greaterThan(root.get(age), age); }; } public static SpecificationUser hasDepartment(String department) { return (root, query, criteriaBuilder) - { if (department null) { return null; } return criteriaBuilder.equal(root.get(department), department); }; } } // 服务层 Service public class UserService { private final UserRepository userRepository; Autowired public UserService(UserRepository userRepository) { this.userRepository userRepository; } public ListUser searchUsers(String name, String email, Integer age, String department) { SpecificationUser spec Specification.where(UserSpecifications.hasName(name)) .and(UserSpecifications.hasEmail(email)) .and(UserSpecifications.hasAgeGreaterThan(age)) .and(UserSpecifications.hasDepartment(department)); return userRepository.findAll(spec); } public ListUser searchUsersWithSorting(String name, String email, String sortBy, Sort.Direction direction) { SpecificationUser spec Specification.where(UserSpecifications.hasName(name)) .and(UserSpecifications.hasEmail(email)); Sort sort Sort.by(direction, sortBy); return userRepository.findAll(spec, sort); } }4. QueryDSL4.1 原理QueryDSL 是一种类型安全的查询构建器它可以生成类型安全的查询代码。Spring Data 2027 对 QueryDSL 提供了很好的支持使得开发者可以更安全、更直观地构建查询。4.2 实践使用 QueryDSL 实现动态查询// 1. 添加依赖 // dependency // groupIdcom.querydsl/groupId // artifactIdquerydsl-jpa/artifactId // version5.0.0/version // /dependency // dependency // groupIdcom.querydsl/groupId // artifactIdquerydsl-apt/artifactId // version5.0.0/version // scopeprovided/scope // /dependency // 2. 生成 Q 类 // 运行 mvn compile 生成 QUser 类 // 3. Repository 接口 public interface UserRepository extends JpaRepositoryUser, Long, QuerydslPredicateExecutorUser { } // 服务层 Service public class UserService { private final UserRepository userRepository; private final QUser qUser QUser.user; Autowired public UserService(UserRepository userRepository) { this.userRepository userRepository; } public ListUser searchUsers(String name, String email, Integer age, String department) { BooleanBuilder builder new BooleanBuilder(); if (name ! null) { builder.and(qUser.name.containsIgnoreCase(name)); } if (email ! null) { builder.and(qUser.email.containsIgnoreCase(email)); } if (age ! null) { builder.and(qUser.age.gt(age)); } if (department ! null) { builder.and(qUser.department.eq(department)); } return userRepository.findAll(builder); } public ListUser searchUsersWithSorting(String name, String email, String sortBy, Direction direction) { BooleanBuilder builder new BooleanBuilder(); if (name ! null) { builder.and(qUser.name.containsIgnoreCase(name)); } if (email ! null) { builder.and(qUser.email.containsIgnoreCase(email)); } // 动态排序 PathBuilderUser pathBuilder new PathBuilder(User.class, user); OrderSpecifier? orderSpecifier; if (direction Direction.ASC) { orderSpecifier pathBuilder.getString(sortBy).asc(); } else { orderSpecifier pathBuilder.getString(sortBy).desc(); } return userRepository.findAll(builder, orderSpecifier); } }5. Criteria API5.1 原理JPA Criteria API 是一种编程式的查询构建方式它允许开发者通过代码构建查询而不是使用字符串形式的 JPQL。Spring Data 2027 支持直接使用 Criteria API 构建动态查询。5.2 实践使用 Criteria API 实现动态查询// 服务层 Service public class UserService { private final EntityManager entityManager; Autowired public UserService(EntityManager entityManager) { this.entityManager entityManager; } public ListUser searchUsers(String name, String email, Integer age, String department) { // 创建 CriteriaBuilder CriteriaBuilder cb entityManager.getCriteriaBuilder(); // 创建 CriteriaQuery CriteriaQueryUser cq cb.createQuery(User.class); // 创建 Root RootUser root cq.from(User.class); // 构建查询条件 ListPredicate predicates new ArrayList(); if (name ! null) { predicates.add(cb.like(root.get(name), % name %)); } if (email ! null) { predicates.add(cb.like(root.get(email), % email %)); } if (age ! null) { predicates.add(cb.greaterThan(root.get(age), age)); } if (department ! null) { predicates.add(cb.equal(root.get(department), department)); } // 添加查询条件 if (!predicates.isEmpty()) { cq.where(cb.and(predicates.toArray(new Predicate[0]))); } // 执行查询 TypedQueryUser query entityManager.createQuery(cq); return query.getResultList(); } public ListUser searchUsersWithSorting(String name, String email, String sortBy, Sort.Direction direction) { // 创建 CriteriaBuilder CriteriaBuilder cb entityManager.getCriteriaBuilder(); // 创建 CriteriaQuery CriteriaQueryUser cq cb.createQuery(User.class); // 创建 Root RootUser root cq.from(User.class); // 构建查询条件 ListPredicate predicates new ArrayList(); if (name ! null) { predicates.add(cb.like(root.get(name), % name %)); } if (email ! null) { predicates.add(cb.like(root.get(email), % email %)); } // 添加查询条件 if (!predicates.isEmpty()) { cq.where(cb.and(predicates.toArray(new Predicate[0]))); } // 添加排序 if (sortBy ! null) { if (direction Sort.Direction.ASC) { cq.orderBy(cb.asc(root.get(sortBy))); } else { cq.orderBy(cb.desc(root.get(sortBy))); } } // 执行查询 TypedQueryUser query entityManager.createQuery(cq); return query.getResultList(); } }6. 动态 JPQL6.1 原理动态 JPQL 是指在运行时根据条件动态构建 JPQL 查询语句。这种方式虽然灵活但需要注意 SQL 注入的风险。6.2 实践使用动态 JPQL 实现动态查询// 服务层 Service public class UserService { private final EntityManager entityManager; Autowired public UserService(EntityManager entityManager) { this.entityManager entityManager; } public ListUser searchUsers(String name, String email, Integer age, String department) { // 构建 JPQL 查询 StringBuilder jpql new StringBuilder(SELECT u FROM User u WHERE 11); ListObject parameters new ArrayList(); int paramIndex 1; if (name ! null) { jpql.append( AND u.name LIKE ?).append(paramIndex); parameters.add(% name %); } if (email ! null) { jpql.append( AND u.email LIKE ?).append(paramIndex); parameters.add(% email %); } if (age ! null) { jpql.append( AND u.age ?).append(paramIndex); parameters.add(age); } if (department ! null) { jpql.append( AND u.department ?).append(paramIndex); parameters.add(department); } // 创建查询 TypedQueryUser query entityManager.createQuery(jpql.toString(), User.class); // 设置参数 for (int i 0; i parameters.size(); i) { query.setParameter(i 1, parameters.get(i)); } // 执行查询 return query.getResultList(); } public ListUser searchUsersWithSorting(String name, String email, String sortBy, Sort.Direction direction) { // 构建 JPQL 查询 StringBuilder jpql new StringBuilder(SELECT u FROM User u WHERE 11); ListObject parameters new ArrayList(); int paramIndex 1; if (name ! null) { jpql.append( AND u.name LIKE ?).append(paramIndex); parameters.add(% name %); } if (email ! null) { jpql.append( AND u.email LIKE ?).append(paramIndex); parameters.add(% email %); } // 添加排序 if (sortBy ! null) { jpql.append( ORDER BY u.).append(sortBy); if (direction Sort.Direction.DESC) { jpql.append( DESC); } else { jpql.append( ASC); } } // 创建查询 TypedQueryUser query entityManager.createQuery(jpql.toString(), User.class); // 设置参数 for (int i 0; i parameters.size(); i) { query.setParameter(i 1, parameters.get(i)); } // 执行查询 return query.getResultList(); } }7. 综合实践7.1 组合使用多种动态查询方式在实际应用中我们可以根据具体场景选择合适的动态查询方式甚至组合使用多种方式Service public class UserService { private final UserRepository userRepository; private final EntityManager entityManager; private final QUser qUser QUser.user; Autowired public UserService(UserRepository userRepository, EntityManager entityManager) { this.userRepository userRepository; this.entityManager entityManager; } // 使用 Query By Example public ListUser searchUsersByExample(String name, String email) { User user new User(); if (name ! null) { user.setName(name); } if (email ! null) { user.setEmail(email); } ExampleMatcher matcher ExampleMatcher.matching() .withIgnoreCase() .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING); ExampleUser example Example.of(user, matcher); return userRepository.findAll(example); } // 使用 Specification public ListUser searchUsersBySpecification(String name, Integer age) { SpecificationUser spec Specification.where(UserSpecifications.hasName(name)) .and(UserSpecifications.hasAgeGreaterThan(age)); return userRepository.findAll(spec); } // 使用 QueryDSL public ListUser searchUsersByQueryDSL(String name, String department) { BooleanBuilder builder new BooleanBuilder(); if (name ! null) { builder.and(qUser.name.containsIgnoreCase(name)); } if (department ! null) { builder.and(qUser.department.eq(department)); } return userRepository.findAll(builder); } // 使用 Criteria API public ListUser searchUsersByCriteriaAPI(String email, Integer age) { CriteriaBuilder cb entityManager.getCriteriaBuilder(); CriteriaQueryUser cq cb.createQuery(User.class); RootUser root cq.from(User.class); ListPredicate predicates new ArrayList(); if (email ! null) { predicates.add(cb.like(root.get(email), % email %)); } if (age ! null) { predicates.add(cb.greaterThan(root.get(age), age)); } if (!predicates.isEmpty()) { cq.where(cb.and(predicates.toArray(new Predicate[0]))); } TypedQueryUser query entityManager.createQuery(cq); return query.getResultList(); } }7.2 实际应用场景7.2.1 电商系统在电商系统中动态查询可以用于商品搜索Service public class ProductService { private final ProductRepository productRepository; private final QProduct qProduct QProduct.product; Autowired public ProductService(ProductRepository productRepository) { this.productRepository productRepository; } public ListProduct searchProducts(String name, BigDecimal minPrice, BigDecimal maxPrice, String category, String brand, Sort.Direction direction) { BooleanBuilder builder new BooleanBuilder(); if (name ! null) { builder.and(qProduct.name.containsIgnoreCase(name)); } if (minPrice ! null) { builder.and(qProduct.price.goe(minPrice)); } if (maxPrice ! null) { builder.and(qProduct.price.loe(maxPrice)); } if (category ! null) { builder.and(qProduct.category.eq(category)); } if (brand ! null) { builder.and(qProduct.brand.eq(brand)); } // 排序 OrderSpecifier? orderSpecifier; if (direction Sort.Direction.ASC) { orderSpecifier qProduct.price.asc(); } else { orderSpecifier qProduct.price.desc(); } return productRepository.findAll(builder, orderSpecifier); } }7.2.2 人力资源系统在人力资源系统中动态查询可以用于员工信息查询Service public class EmployeeService { private final EmployeeRepository employeeRepository; Autowired public EmployeeService(EmployeeRepository employeeRepository) { this.employeeRepository employeeRepository; } public ListEmployee searchEmployees(String name, String department, LocalDate startDateFrom, LocalDate startDateTo, Integer minSalary, Integer maxSalary) { SpecificationEmployee spec Specification.where(EmployeeSpecifications.hasName(name)) .and(EmployeeSpecifications.hasDepartment(department)) .and(EmployeeSpecifications.hasStartDateBetween(startDateFrom, startDateTo)) .and(EmployeeSpecifications.hasSalaryBetween(minSalary, maxSalary)); return employeeRepository.findAll(spec); } }8. 性能优化8.1 动态查询性能优化使用索引为经常用于查询的字段创建索引限制结果集使用分页和限制返回的记录数优化查询条件避免使用复杂的查询条件特别是在大型表上使用缓存对于频繁执行的查询结果进行缓存批量操作对于批量查询使用批量操作减少数据库交互8.2 代码示例// 使用分页 public PageUser searchUsersWithPagination(String name, String email, Pageable pageable) { SpecificationUser spec Specification.where(UserSpecifications.hasName(name)) .and(UserSpecifications.hasEmail(email)); return userRepository.findAll(spec, pageable); } // 使用缓存 Cacheable(value users, key #name #email) public ListUser searchUsersWithCache(String name, String email) { ExampleUser example Example.of(new User(name, email)); return userRepository.findAll(example); } // 批量查询 public ListUser batchSearchUsers(ListLong ids) { return userRepository.findAllById(ids); }9. 监控与调试9.1 监控动态查询使用 Spring Boot Actuator 监控动态查询的执行情况RestController RequestMapping(/actuator/queries) public class QueryActuatorController { private final EntityManager entityManager; Autowired public QueryActuatorController(EntityManager entityManager) { this.entityManager entityManager; } GetMapping(/statistics) public MapString, Object getQueryStatistics() { MapString, Object stats new HashMap(); // 获取查询执行统计信息 Session session entityManager.unwrap(Session.class); Statistics statistics session.getSessionFactory().getStatistics(); stats.put(queryCount, statistics.getQueryExecutionCount()); stats.put(queryExecutionTime, statistics.getQueryExecutionTotalTime()); stats.put(entityLoadCount, statistics.getEntityLoadCount()); stats.put(collectionLoadCount, statistics.getCollectionLoadCount()); return stats; } }9.2 调试动态查询调试动态查询的技巧启用 SQL 日志在 application.properties 中设置spring.jpa.show-sqltrue使用 QueryLog使用 Hibernate 的 QueryLog 记录查询执行情况使用断点在查询构建过程中设置断点查看生成的查询语句使用数据库工具使用数据库工具如 MySQL Workbench分析查询执行计划10. 最佳实践10.1 选择合适的动态查询方式场景推荐方式原因简单条件查询Query By Example简单直观代码量少复杂条件查询Specification 或 QueryDSL更灵活支持复杂条件类型安全查询QueryDSL编译时检查避免拼写错误原生 SQL 查询动态 JPQL 或 Criteria API支持复杂的原生 SQL 功能10.2 代码最佳实践// 最佳实践 1使用工厂方法创建查询条件 public class UserQueryBuilder { public static SpecificationUser buildQuery(String name, String email, Integer age, String department) { return Specification.where(hasName(name)) .and(hasEmail(email)) .and(hasAgeGreaterThan(age)) .and(hasDepartment(department)); } private static SpecificationUser hasName(String name) { return (root, query, cb) - name ! null ? cb.like(root.get(name), % name %) : null; } // 其他条件方法... } // 最佳实践 2使用建造者模式构建查询 public class QueryBuilderT { private final ClassT entityClass; private final ListPredicate predicates new ArrayList(); private final CriteriaBuilder cb; private final CriteriaQueryT cq; private final RootT root; public QueryBuilder(EntityManager entityManager, ClassT entityClass) { this.entityClass entityClass; this.cb entityManager.getCriteriaBuilder(); this.cq cb.createQuery(entityClass); this.root cq.from(entityClass); } public QueryBuilderT eq(String field, Object value) { if (value ! null) { predicates.add(cb.equal(root.get(field), value)); } return this; } public QueryBuilderT like(String field, String value) { if (value ! null) { predicates.add(cb.like(root.get(field), % value %)); } return this; } public QueryBuilderT gt(String field, Comparable value) { if (value ! null) { predicates.add(cb.greaterThan(root.get(field), value)); } return this; } public ListT build(EntityManager entityManager) { if (!predicates.isEmpty()) { cq.where(cb.and(predicates.toArray(new Predicate[0]))); } return entityManager.createQuery(cq).getResultList(); } } // 最佳实践 3使用方法引用和 lambda 表达式 public ListUser searchUsers(String name, String email) { return userRepository.findAll((root, query, cb) - { ListPredicate predicates new ArrayList(); if (name ! null) { predicates.add(cb.like(root.get(name), % name %)); } if (email ! null) { predicates.add(cb.like(root.get(email), % email %)); } return predicates.isEmpty() ? null : cb.and(predicates.toArray(new Predicate[0])); }); }这其实可以更优雅一点Spring Data 2027 的动态查询功能让数据查询变得更加优雅代码更简洁使用声明式的方式构建查询类型更安全QueryDSL 提供编译时类型检查灵活性更高支持复杂的查询条件可维护性更好清晰的代码结构和逻辑性能更优优化的查询执行11. 未来发展趋势Spring Data 2027 的动态查询功能正在不断演进更智能的查询构建基于 AI 的智能查询构建更强大的类型安全增强的 QueryDSL 功能更高效的查询执行优化的查询执行计划更丰富的查询功能支持更多的查询类型和操作更集成的生态系统与其他 Spring 生态系统组件的深度集成12. 总结Spring Data 2027 的动态查询功能是构建灵活、高效数据访问层的关键。通过合理选择和使用 Query By Example、Specification、QueryDSL、Criteria API 和动态 JPQL 等方式我们可以构建复杂的查询条件满足各种业务场景的需求提高代码的可维护性清晰的代码结构和逻辑增强系统的性能优化的查询执行减少开发工作量简化查询构建的过程记住选择合适的动态查询方式取决于具体的业务场景和需求。在实际应用中我们应该根据查询的复杂度、性能要求和代码可维护性等因素选择最适合的动态查询方式。