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