web-dev-qa-db-ja.com

任意のAND句を使用した動的なSpringデータのJPAリポジトリクエリ

Spring data jpa repositoriesを使用しています。異なるフィールドで検索機能を提供する必要があります。検索前にフィールドに入力するかどうかは任意です。EmployeeNumberNameMarriedProfessionDateOfBirthという5つのフィールドがあります。
ここでは、ユーザーごとに指定された値のみを照会する必要があり、他のフィールドは無視する必要があります。

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データには this postに記載されているように制限があります。スケーラブルではなく、可能なクエリはすべて記述する必要があります )私はQuerydslを使用していますが、nullフィールドを無視する必要があり、ほとんどすべての可能なクエリを開発する必要があるため、依然として問題が存在します。このcase 31 queriesで。検索フィールドが6,7,8...である場合はどうなりますか??

オプションフィールドで検索オプションを実装する最良の方法は何ですか?

40
Mr.Chowdary

QueryDSL(4.x)の新しいメジャーバージョンと querydsl-jpa を使用するために変更が行われる可能性があることに注意してください。


プロジェクトの1つでは、QueryDSLQueryDslPredicateExecutor<T>を使用しました。

  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<DataEntity, Long> {

DaoRepository

@NoRepositoryBean
public interface DaoRepository<T, K extends Serializable>
  extends JpaRepository<T, K>,
  QueryDslPredicateExecutor<T> {
}

そのため、リポジトリの述語メソッドを使用できます。

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

QClassesを取得するには、pom.xmlで QueryDSL APT Mavenプラグイン を指定する必要があります。

  <build>
    <plugins>
      <plugin>
        <groupId>com.mysema.maven</groupId>
        <artifactId>maven-apt-plugin</artifactId>
        <version>1.0.4</version>
        <executions>
          <execution>
            <phase>generate-sources</phase>
            <goals>
              <goal>process</goal>
            </goals>
            <configuration>
              <outputDirectory>target/generated-sources</outputDirectory>
              <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
            </configuration>
          </execution>
        </executions>
      </plugin>

依存関係は

    <!-- querydsl -->
    <dependency>
        <groupId>com.mysema.querydsl</groupId>
        <artifactId>querydsl-core</artifactId>
        <version>${querydsl.version}</version>
    </dependency>
    <dependency>
        <groupId>com.mysema.querydsl</groupId>
        <artifactId>querydsl-apt</artifactId>
        <version>${querydsl.version}</version>
    </dependency>
    <dependency>
        <groupId>com.mysema.querydsl</groupId>
        <artifactId>querydsl-jpa</artifactId>
        <version>${querydsl.version}</version>
    </dependency>

または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
}
24
EpicPandaForce

Spring-dataが提供する仕様をそのまま使用できます。条件APIを使用してプログラムでクエリを作成できるようにします。仕様をサポートするには、リポジトリインターフェイスをJpaSpecificationExecutorインターフェイスで拡張します。

public interface CustomerRepository extends SimpleJpaRepository<T, ID>, JpaSpecificationExecutor {

}

追加のインターフェイス(JpaSpecificationExecutor)には、さまざまな方法で仕様を実行できるメソッドが含まれています。

たとえば、findAllメソッドは、仕様に一致するすべてのエンティティを返します。

List<T> findAll(Specification<T> spec);

仕様インターフェースは次のとおりです。

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

さて、典型的なユースケースは何ですか?仕様を簡単に使用して、拡張可能な一連の述語をエンティティの上に構築し、必要な組み合わせごとにクエリ(メソッド)を宣言することなくJpaRepositoryと組み合わせて使用​​できます。例は次のとおりです。例2.15。顧客の仕様

public class CustomerSpecs {
    public static Specification<Customer> isLongTermCustomer() {
        return new Specification<Customer>() {
            public Predicate toPredicate(
                Root<Customer> root, CriteriaQuery<?> query,
                CriteriaBuilder builder) {
                LocalDate date = new LocalDate().minusYears(2);
                return builder.lessThan(root.get('dateField'), date);
            }
        };
    }

    public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) {
        return new Specification<Customer>() {
            public Predicate toPredicate(
                Root<T> root, CriteriaQuery<?> query,
                CriteriaBuilder builder) {
                // build query here
            }
        };
    }
}

ビジネス要件の抽象化レベルに関するいくつかの基準を表明し、実行可能な仕様を作成しました。したがって、クライアントは次のように仕様を使用する場合があります。

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

仕様例2.17を組み合わせることもできます。組み合わせ仕様

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

ご覧のように、仕様には、仕様を連結して結合するためのグルーコード方式がいくつか用意されています。したがって、データアクセス層を拡張するには、新しい仕様の実装を作成し、既存の実装と組み合わせます。

そして、あなたは複雑な仕様を作成することができます、ここに例があります

public class WorkInProgressSpecification {
    public static Specification<WorkInProgress> findByCriteria(final SearchCriteria searchCriteria) {

        return new Specification<WorkInProgress>() {

            @Override
            public Predicate toPredicate(
                Root<WorkInProgress> root,
                CriteriaQuery<?> query, CriteriaBuilder cb) {

                List<Predicate> predicates = new ArrayList<Predicate>();

                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.<Date>get("plndStartDate"), searchCriteria.getPlannedStartDate()));
                }
                if (searchCriteria.getPlannedCompletionDate() != null) {
                    predicates.add(cb.lessThanOrEqualTo(root.<Date>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リポジトリのドキュメント

24
iamiddy

Spring Data JPA 1.10から、これに関する別のオプションがあります Query By Example です。リポジトリは、JpaRepositoryとは別に QueryByExampleExecutor インターフェースを実装する必要があります。

<S extends T> Iterable<S> findAll(Example<S> example)

次に、 を作成して、次のように検索します。

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句に取り込まれないため、動的クエリを取得できます。

文字列属性のマッチングを改善するには、 ExampleMatcher を見てください。

大文字と小文字を区別しないExampleMatcherを実行するlikeは、たとえば次のとおりです。

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

21
Robert Niestroj