Requête de dépôt jpa de données de ressort dynamique avec des clauses arbitraires et

j'utilise Spring data jpa repositories, a Reçu une obligation de donner la fonction de recherche avec différents domaines. La saisie des champs avant la recherche est facultative.J'ai 5 champs de dire EmployeeNumber, Name,Married,Profession et DateOfBirth.

Ici, je dois interroger seulement avec les valeurs données par l'utilisateur et d'autres champs devraient être ignorés.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%';  

donc ici nous considérons les valeurs entrées et les questions. Dans ce cas, les données du ressort ont une limitation comme indiqué dans ce post (ce n'est Pas évolutive et de toutes les requêtes doivent être écrites) Je suis à l'aide de Querydsl, mais toujours le problème existe aussi null champs doit être ignorée et près de toutes les requêtes possibles doivent être développées. Dans ce case 31 queries. que faire si les champs de recherche sont <!--10 ??

Quelle est la meilleure approche pour implémenter l'option de recherche avec des champs optionnels ?

24
demandé sur Mr.Chowdary 2015-03-05 12:21:59

3 réponses

Veuillez noter qu'il pourrait y avoir des changements à faire pour utiliser la nouvelle version majeure de QueryDSL (4.x) et querydsl-jpa


Dans l'un de nos projets, nous avons utilisé 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();
  }

Et on peut l'utiliser dans les référentiels:

@Repository
public interface DataEntityRepository
  extends DaoRepository<DataEntity, Long> {

DaoRepository

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

car alors, vous pouvez utiliser des méthodes de prédicat de dépôt.

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

Pour obtenir de l' QClasses, vous avez besoin pour spécifier l' QueryDSL APT Maven plugin dans votre pom.XML.

  <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>

les dépendances sont

    <!-- 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>

Ou pour 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
}
19
répondu EpicPandaForce 2016-07-11 08:54:43

vous pouvez utiliser les spécifications que Spring-data vous donne hors de la boîte. et être capable d'utiliser l'API de critères pour construire des requêtes de manière programmatique.Pour prendre en charge les spécifications, vous pouvez étendre votre interface de dépôt avec L'interface JpaSpecificationExecutor

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

}

l'interface supplémentaire (JpaSpecificationExecutor) comporte des méthodes qui vous permettent d'exécuter les spécifications de différentes façons.

par exemple, la méthode findAll retournera toutes les entités qui correspondent la spécification:

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

La Spécification de l'interface se présente comme suit:

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

D'accord, alors quel est le cas d'utilisation typique? Les spécifications peuvent facilement être utilisées pour construire un ensemble extensible de prédicats sur une entité qui peut ensuite être combiné et utilisé avec JpaRepository sans la nécessité de déclarer une requête (méthode) pour chaque combinaison nécessaire. Voici un exemple: Exemple 2.15. Spécifications pour un client

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
            }
        };
    }
}

vous avez exprimé certains critères au niveau de l'abstraction des besoins de l'entreprise et créé des spécifications exécutables. Ainsi, un client peut utiliser une spécification comme suit:

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

vous pouvez également combiner L'exemple de spécification 2.17. Spécifications Combinées

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

comme vous pouvez le voir, les spécifications offrent quelques méthodes de code à coller pour enchaîner et combiner les spécifications. Ainsi, l'extension de votre couche d'accès aux données est il suffit de créer de nouvelles implémentations de spécification et en les combinant avec celles déjà existantes.

et vous pouvez créer des spécifications Complexes, voici un exemple

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[] {}));
            }
        };
    }
}

Ici JPA Repository de docs

21
répondu iamiddy 2015-03-05 13:13:54

à Partir de Spring Data JPA 1.10 il y a une autre option pour ce qui est Interrogation Par Exemple. Votre dépôt devrait implémenter en dehors de JpaRepository QueryByExampleExecutor interface où vous obtenez des méthodes comme:

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

alors vous créez le Exemple pour la recherche de la forme:

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

puis:

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

si certains paramètres sont nuls, ils ne seront pas pris en compte dans la clause WHERE so you obtenez des requêtes dynamiques.

pour affiner la correspondance des attributs des chaînes de caractères, jetez un coup d'oeil à ExampleMatcher

ExampleMatcher ce qui fait unlike par exemple:

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

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

15
répondu Robert Niestroj 2017-12-11 10:23:11