具有任意AND子句的动态spring数据jpa存储库查询

我正在使用Spring data jpa repositories ,需要为不同的字段提供搜索function。 在搜索之前输入字段是可选的。我有5个字段表示EmployeeNumberNameMarriedProfessionDateOfBirth
这里我只需要用户查询给定的值,其他字段应该被忽略.Ex,

 Input : EmployeeNumber: ,Name:St,Married: ,Professsion:IT,DateOfBirth: Query : Select * from Employee e where Name like 'St%' and Profession like 'IT%'; Input : EmployeeNumber:10,Name: ,Married: ,Professsion:IT,DateOfBirth: Query : Select * from Employee e where EmployeeNumber like '10%' and Profession like 'IT%'; 

所以我们在这里考虑输入和查询的值。 在这种情况下,Spring数据有一个限制,如本文所述( 不可扩展,所有可能的查询都应该写 )我正在使用Querydsl ,但仍然存在问题,因为应忽略null字段,几乎所有可能的查询都需要开发。 在这种case 31 queries 。 如果搜索字段是6,7,8...

使用可选字段实现搜索选项的最佳方法是什么?

请注意,使用新的主要版本的QueryDSL(4.x)和querydsl-jpa可能会有所更改


在我们的一个项目中,我们将QueryDSLQueryDslPredicateExecutor

  public Predicate createPredicate(DataEntity dataEntity) { QDataEntity qDataEntity = QDataEntity.dataEntity; BooleanBuilder booleanBuilder = new BooleanBuilder(); if (!StringUtils.isEmpty(dataEntity.getCnsiConsumerNo())) { booleanBuilder .or(qDataEntity.cnsiConsumerNo.contains(dataEntity.getCnsiConsumerNo())); } if (!StringUtils.isEmpty(dataEntity.getCnsiMeterNo())) { booleanBuilder.or(qDataEntity.cnsiMeterNo.contains(dataEntity.getCnsiMeterNo())); } return booleanBuilder.getValue(); } 

我们可以在存储库中使用它:

 @Repository public interface DataEntityRepository extends DaoRepository { 

DaoRepository在哪里

 @NoRepositoryBean public interface DaoRepository extends JpaRepository, QueryDslPredicateExecutor { } 

因此,您可以使用存储库谓词方法。

 Iterable results = dataEntityRepository.findAll(dataEntityPredicateCreator.createPredicate(dataEntity)); 

要获取QClasses ,您需要在pom.xml中指定QueryDSL APT Maven插件 。

     com.mysema.maven maven-apt-plugin 1.0.4   generate-sources  process   target/generated-sources com.mysema.query.apt.jpa.JPAAnnotationProcessor     

依赖性是

    com.mysema.querydsl querydsl-core ${querydsl.version}   com.mysema.querydsl querydsl-apt ${querydsl.version}   com.mysema.querydsl querydsl-jpa ${querydsl.version}  

或者对于Gradle:

 sourceSets { generated } sourceSets.generated.java.srcDirs = ['src/main/generated'] configurations { querydslapt } dependencies { // other deps .... compile "com.mysema.querydsl:querydsl-jpa:3.6.3" compile "com.mysema.querydsl:querydsl-apt:3.6.3:jpa" } task generateQueryDSL(type: JavaCompile, group: 'build', description: 'Generates the QueryDSL query types') { source = sourceSets.main.java classpath = configurations.compile + configurations.querydslapt options.compilerArgs = [ "-proc:only", "-processor", "com.mysema.query.apt.jpa.JPAAnnotationProcessor" ] destinationDir = sourceSets.generated.java.srcDirs.iterator().next() } compileJava { dependsOn generateQueryDSL source generateQueryDSL.destinationDir } compileGeneratedJava { dependsOn generateQueryDSL classpath += sourceSets.main.runtimeClasspath } 

您可以使用Spring-data为您提供开箱即用的规格。 并且能够使用条件API以编程方式构建查询。要支持规范,您可以使用JpaSpecificationExecutor接口扩展存储库接口

 public interface CustomerRepository extends SimpleJpaRepository, JpaSpecificationExecutor { } 

附加接口(JpaSpecificationExecutor)包含允许您以各种方式执行规范的方法。

例如,findAll方法将返回与规范匹配的所有实体:

 List findAll(Specification spec); 

规范界面如下:

 public interface Specification { Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder); } 

好的,那么典型的用例是什么? 可以轻松地使用规范在实体之上构建可扩展的谓词集,然后可以将其与JpaRepository结合使用,而无需为每个所需组合声明查询(方法)。 这是一个例子:例2.15。 客户规格

 public class CustomerSpecs { public static Specification isLongTermCustomer() { return new Specification() { public Predicate toPredicate( Root root, CriteriaQuery query, CriteriaBuilder builder) { LocalDate date = new LocalDate().minusYears(2); return builder.lessThan(root.get('dateField'), date); } }; } public static Specification hasSalesOfMoreThan(MontaryAmount value) { return new Specification() { public Predicate toPredicate( Root root, CriteriaQuery query, CriteriaBuilder builder) { // build query here } }; } } 

您在业务需求抽象级别上表达了一些标准,并创建了可执行规范。 所以客户端可能会使用如下规范:

 List customers = customerRepository.findAll(isLongTermCustomer()); 

您还可以组合规范示例2.17。 组合规格

  MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR); List customers = customerRepository.findAll( where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount))); 

如您所见,规格提供了一些胶水代码方法来链接和组合规格。 因此,扩展数据访问层只需要创建新的规范实现并将它们与已存在的实现相结合。

您可以创建复杂规范,这是一个示例

 public class WorkInProgressSpecification { public static Specification findByCriteria(final SearchCriteria searchCriteria) { return new Specification() { @Override public Predicate toPredicate( Root root, CriteriaQuery query, CriteriaBuilder cb) { List predicates = new ArrayList(); if (searchCriteria.getView() != null && !searchCriteria.getView().isEmpty()) { predicates.add(cb.equal(root.get("viewType"), searchCriteria.getView())); } if (searchCriteria.getFeature() != null && !searchCriteria.getFeature().isEmpty()) { predicates.add(cb.equal(root.get("title"), searchCriteria.getFeature())); } if (searchCriteria.getEpic() != null && !searchCriteria.getEpic().isEmpty()) { predicates.add(cb.equal(root.get("epic"), searchCriteria.getEpic())); } if (searchCriteria.getPerformingGroup() != null && !searchCriteria.getPerformingGroup().isEmpty()) { predicates.add(cb.equal(root.get("performingGroup"), searchCriteria.getPerformingGroup())); } if (searchCriteria.getPlannedStartDate() != null) { System.out.println("searchCriteria.getPlannedStartDate():" + searchCriteria.getPlannedStartDate()); predicates.add(cb.greaterThanOrEqualTo(root.get("plndStartDate"), searchCriteria.getPlannedStartDate())); } if (searchCriteria.getPlannedCompletionDate() != null) { predicates.add(cb.lessThanOrEqualTo(root.get("plndComplDate"), searchCriteria.getPlannedCompletionDate())); } if (searchCriteria.getTeam() != null && !searchCriteria.getTeam().isEmpty()) { predicates.add(cb.equal(root.get("agileTeam"), searchCriteria.getTeam())); } return cb.and(predicates.toArray(new Predicate[] {})); } }; } } 

这是JPA Respositories文档

从Spring Data JPA 1.10开始,还有一个选项是Query By Example 。 您的存储库应该在JpaRepository和QueryByExampleExecutor接口之外实现,您可以在其中获取以下方法:

  Iterable findAll(Example example) 

然后创建示例来搜索like:

 Employee e = new Employee(); e.setEmployeeNumber(getEmployeeNumberSomewherFrom()); e.setName(getNameSomewhereFrom()); e.setMarried(getMarriedSomewhereFrom()); e.setProfession(getProfessionSomewhereFrom()); e.setDateOfBirth(getDateOfBirthSomewhereFrom()); 

接着:

 employeeRepository.findAll(Example.of(e)); 

如果某些参数为null,则不会将它们引入WHERE子句,因此您将获得动态查询。

要优化String属性的匹配,请查看ExampleMatcher

like一个不区分大小写的ExampleMatcher例如:

 ExampleMatcher matcher = ExampleMatcher.matching(). withMatcher("profession", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.CONTAINING).ignoreCase()); 

QBE示例: https : //github.com/spring-projects/spring-data-examples/tree/master/jpa/query-by-example