Consulta dinámica del repositorio jpa de datos de primavera con cláusulas arbitrarias y


Estoy usando Spring data jpa repositories, Tengo un requisito para dar función de búsqueda con diferentes campos. Introducir campos antes de la búsqueda es opcional.Tengo 5 campos de decir EmployeeNumber, Name, Married,Profession y DateOfBirth.
Aquí necesito consultar solo con los valores dados por el usuario y otros campos deben ignorarse.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%';  

Así que aquí estamos considerando los valores introducidos y la consulta. En este caso, los datos de primavera tienen una limitación como se menciona en este post (No escalable y todas las consultas posibles debe ser escrito) Estoy usando Querydsl, pero aún así el problema existe ya que los campos null deben ignorarse y casi todas las consultas posibles deben desarrollarse. En este case 31 queries. ¿qué pasa si los campos de búsqueda son 6,7,8...??

¿Cuál es el mejor enfoque para implementar la opción de búsqueda con campos opcionales ?

Author: Mr.Chowdary, 2015-03-05

3 answers

Tenga en cuenta que puede haber cambios que hacer para utilizar la nueva versión principal de QueryDSL (4.x) y querydsl-jpa


En uno de nuestros proyectos, usamos QueryDSL con QueryDslPredicateExecutor<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();
  }

Y podríamos usar esto en los repositorios:

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

Donde DaoRepository es

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

Porque entonces, puede usar métodos de predicado de repositorio.

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

Para obtener QClasses, debe especificar el complemento QueryDSL APT Maven en su 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>

Las dependencias son

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

O para 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
Author: EpicPandaForce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-11 08:54:43

Puede usar las especificaciones que Spring-data le proporciona de inmediato. y ser capaz de usar la API de criterios para crear consultas programmatically.To especificaciones de soporte puede ampliar su interfaz de repositorio con la interfaz JpaSpecificationExecutor

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

}

La interfaz adicional(JpaSpecificationExecutor ) lleva métodos que le permiten ejecutar Especificaciones de una variedad de maneras.

Por ejemplo, el método findAll devolverá todas las entidades que coincidan con especificación:

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

La interfaz de especificación es la siguiente:

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

Bien, entonces, ¿cuál es el caso de uso típico? Las especificaciones se pueden usar fácilmente para construir un conjunto extensible de predicados sobre una entidad que luego se puede combinar y usar con JpaRepository sin la necesidad de declarar una consulta (método) para cada combinación necesaria. Aquí hay un ejemplo: Ejemplo 2.15. Especificaciones para un cliente

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

Usted expresó algunos criterios sobre un requisito de negocio nivel de abstracción y especificaciones ejecutables creadas. Así que un cliente podría usar una Especificación de la siguiente manera:

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

También puede combinar el Ejemplo de especificación 2.17. Especificaciones combinadas

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

Como puede ver, Specifications ofrece algunos métodos de código de pegamento para encadenar y combinar Especificaciones. Por lo tanto, extender su capa de acceso a datos es solo es cuestión de crear nuevas implementaciones de especificaciones y combinándolos con otros ya existente.

Y puede crear Especificaciones Complejas, aquí hay un ejemplo

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

Aquí están los Respositorios de JPA docs

 21
Author: iamiddy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-05 13:13:54

Desde Spring Data JPA 1.10 hay otra opción para esto es Consulta Por Ejemplo. Su repositorio debe implementar aparte de JpaRepository también la interfaz QueryByExampleExecutor donde obtiene métodos como:

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

Luego crea el Ejemplo para buscar como:

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

Y luego:

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

Si algunos parámetros son null no se tomarán en la cláusula WHERE para obtener consultas dinámicas.

Para refinar la coincidencia de Atributos de cadena echa un vistazo a ExampleMatcher's

Un ExampleMatcher que no distingue mayúsculas de minúsculas like es por ejemplo:

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

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

 15
Author: Robert Niestroj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-11 10:23:11