tech.jhipster.service.QueryService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jhipster-framework Show documentation
Show all versions of jhipster-framework Show documentation
Server-side library used by applications created with the JHipster generator, see https://www.jhipster.tech/ for more information on JHipster
/*
* Copyright 2016-2024 the original author or authors from the JHipster project.
*
* This file is part of the JHipster project, see https://www.jhipster.tech/
* for more information.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package tech.jhipster.service;
import jakarta.persistence.criteria.CriteriaBuilder.In;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.SetJoin;
import jakarta.persistence.metamodel.SetAttribute;
import jakarta.persistence.metamodel.SingularAttribute;
import java.util.Collection;
import java.util.Set;
import java.util.function.Function;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;
import tech.jhipster.service.filter.Filter;
import tech.jhipster.service.filter.RangeFilter;
import tech.jhipster.service.filter.StringFilter;
/**
* Base service for constructing and executing complex queries.
*
* @param the type of the entity which is queried.
*/
@Transactional(readOnly = true)
public abstract class QueryService {
/**
* Helper function to return a specification for filtering on a single field, where equality, and null/non-null
* conditions are supported.
*
* @param filter the individual attribute filter coming from the frontend.
* @param field the JPA static metamodel representing the field.
* @param The type of the attribute which is filtered.
* @return a Specification
*/
protected Specification buildSpecification(Filter filter, SingularAttribute super ENTITY, X> field) {
return buildSpecification(filter, root -> root.get(field));
}
/**
* Helper function to return a specification for filtering on a single field, where equality, and null/non-null
* conditions are supported.
*
* @param filter the individual attribute filter coming from the frontend.
* @param metaclassFunction the function, which navigates from the current entity to a column, for which the filter applies.
* @param The type of the attribute which is filtered.
* @return a Specification
*/
protected Specification buildSpecification(Filter filter, Function, Expression> metaclassFunction) {
if (filter.getEquals() != null) {
return equalsSpecification(metaclassFunction, filter.getEquals());
} else if (filter.getIn() != null) {
return valueIn(metaclassFunction, filter.getIn());
} else if (filter.getNotIn() != null) {
return valueNotIn(metaclassFunction, filter.getNotIn());
} else if (filter.getNotEquals() != null) {
return notEqualsSpecification(metaclassFunction, filter.getNotEquals());
} else if (filter.getSpecified() != null) {
return byFieldSpecified(metaclassFunction, filter.getSpecified());
}
return null;
}
/**
* Helper function to return a specification for filtering on a {@link java.lang.String} field, where equality, containment,
* and null/non-null conditions are supported.
*
* @param filter the individual attribute filter coming from the frontend.
* @param field the JPA static metamodel representing the field.
* @return a Specification
*/
protected Specification buildStringSpecification(StringFilter filter, SingularAttribute super ENTITY, String> field) {
return buildSpecification(filter, root -> root.get(field));
}
/**
* Helper function to return a specification for filtering on a {@link java.lang.String} field, where equality, containment,
* and null/non-null conditions are supported.
*
* @param filter the individual attribute filter coming from the frontend.
* @param metaclassFunction lambda, which based on a Root<ENTITY> returns Expression - basicaly picks a column
* @return a Specification
*/
protected Specification buildSpecification(StringFilter filter, Function, Expression> metaclassFunction) {
if (filter.getEquals() != null) {
return equalsSpecification(metaclassFunction, filter.getEquals());
} else if (filter.getIn() != null) {
return valueIn(metaclassFunction, filter.getIn());
} else if (filter.getNotIn() != null) {
return valueNotIn(metaclassFunction, filter.getNotIn());
} else if (filter.getContains() != null) {
return likeUpperSpecification(metaclassFunction, filter.getContains());
} else if (filter.getDoesNotContain() != null) {
return doesNotContainSpecification(metaclassFunction, filter.getDoesNotContain());
} else if (filter.getNotEquals() != null) {
return notEqualsSpecification(metaclassFunction, filter.getNotEquals());
} else if (filter.getSpecified() != null) {
return byFieldSpecified(metaclassFunction, filter.getSpecified());
}
return null;
}
/**
* Helper function to return a specification for filtering on a single {@link java.lang.Comparable}, where equality, less
* than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are
* supported.
*
* @param The type of the attribute which is filtered.
* @param filter the individual attribute filter coming from the frontend.
* @param field the JPA static metamodel representing the field.
* @return a Specification
*/
protected > Specification buildRangeSpecification(
RangeFilter filter,
SingularAttribute super ENTITY, X> field
) {
return buildSpecification(filter, root -> root.get(field));
}
/**
* Helper function to return a specification for filtering on a single {@link java.lang.Comparable}, where equality, less
* than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are
* supported.
*
* @param The type of the attribute which is filtered.
* @param filter the individual attribute filter coming from the frontend.
* @param metaclassFunction lambda, which based on a Root<ENTITY> returns Expression - basicaly picks a column
* @return a Specification
*/
protected > Specification buildSpecification(
RangeFilter filter,
Function, Expression> metaclassFunction
) {
if (filter.getEquals() != null) {
return equalsSpecification(metaclassFunction, filter.getEquals());
} else if (filter.getIn() != null) {
return valueIn(metaclassFunction, filter.getIn());
}
Specification result = Specification.where(null);
if (filter.getSpecified() != null) {
result = result.and(byFieldSpecified(metaclassFunction, filter.getSpecified()));
}
if (filter.getNotEquals() != null) {
result = result.and(notEqualsSpecification(metaclassFunction, filter.getNotEquals()));
}
if (filter.getNotIn() != null) {
result = result.and(valueNotIn(metaclassFunction, filter.getNotIn()));
}
if (filter.getGreaterThan() != null) {
result = result.and(greaterThan(metaclassFunction, filter.getGreaterThan()));
}
if (filter.getGreaterThanOrEqual() != null) {
result = result.and(greaterThanOrEqualTo(metaclassFunction, filter.getGreaterThanOrEqual()));
}
if (filter.getLessThan() != null) {
result = result.and(lessThan(metaclassFunction, filter.getLessThan()));
}
if (filter.getLessThanOrEqual() != null) {
result = result.and(lessThanOrEqualTo(metaclassFunction, filter.getLessThanOrEqual()));
}
return result;
}
/**
* Helper function to return a specification for filtering on one-to-one or many-to-one reference. Usage:
*
* Specification<Employee> specByProjectId = buildReferringEntitySpecification(criteria.getProjectId(),
* Employee_.project, Project_.id);
* Specification<Employee> specByProjectName = buildReferringEntitySpecification(criteria.getProjectName(),
* Employee_.project, Project_.name);
*
*
* @param filter the filter object which contains a value, which needs to match or a flag if nullness is
* checked.
* @param reference the attribute of the static metamodel for the referring entity.
* @param valueField the attribute of the static metamodel of the referred entity, where the equality should be
* checked.
* @param The type of the referenced entity.
* @param The type of the attribute which is filtered.
* @return a Specification
*/
protected Specification buildReferringEntitySpecification(
Filter filter,
SingularAttribute super ENTITY, OTHER> reference,
SingularAttribute super OTHER, X> valueField
) {
return buildSpecification(filter, root -> root.get(reference).get(valueField));
}
/**
* Helper function to return a specification for filtering on one-to-many or many-to-many reference. Usage:
*
* Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(criteria.getEmployeId(),
* Project_.employees, Employee_.id);
* Specification<Employee> specByEmployeeName = buildReferringEntitySpecification(criteria.getEmployeName(),
* Project_.project, Project_.name);
*
*
* @param filter the filter object which contains a value, which needs to match or a flag if emptiness is
* checked.
* @param reference the attribute of the static metamodel for the referring entity.
* @param valueField the attribute of the static metamodel of the referred entity, where the equality should be
* checked.
* @param The type of the referenced entity.
* @param The type of the attribute which is filtered.
* @return a Specification
*/
protected Specification buildReferringEntitySpecification(
Filter filter,
SetAttribute reference,
SingularAttribute valueField
) {
return buildReferringEntitySpecification(filter, root -> root.join(reference), entity -> entity.get(valueField));
}
/**
* Helper function to return a specification for filtering on one-to-many or many-to-many reference.Usage:
* Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(
* criteria.getEmployeId(),
* root -> root.get(Project_.company).join(Company_.employees),
* entity -> entity.get(Employee_.id));
* Specification<Employee> specByProjectName = buildReferringEntitySpecification(
* criteria.getProjectName(),
* root -> root.get(Project_.project)
* entity -> entity.get(Project_.name));
*
*
* @param filter the filter object which contains a value, which needs to match or a flag if emptiness is
* checked.
* @param functionToEntity the function, which joins he current entity to the entity set, on which the filtering is applied.
* @param entityToColumn the function, which of the static metamodel of the referred entity, where the equality should be
* checked.
* @param The type of the referenced entity.
* @param The type of the entity which is the last before the OTHER in the chain.
* @param The type of the attribute which is filtered.
* @return a Specification
*/
protected Specification buildReferringEntitySpecification(
Filter filter,
Function, SetJoin> functionToEntity,
Function, Expression> entityToColumn
) {
if (filter.getEquals() != null) {
return equalsSpecification(functionToEntity.andThen(entityToColumn), filter.getEquals());
} else if (filter.getSpecified() != null) {
// Interestingly, 'functionToEntity' doesn't work, we need the longer lambda formula
return byFieldSpecified(functionToEntity::apply, filter.getSpecified());
}
return null;
}
/**
* Helper function to return a specification for filtering on one-to-many or many-to-many reference.Where equality, less
* than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are
* supported. Usage:
*
* Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(criteria.getEmployeId(),
* Project_.employees, Employee_.id);
* Specification<Employee> specByEmployeeName = buildReferringEntitySpecification(criteria.getEmployeName(),
* Project_.project, Project_.name);
*
*
* @param The type of the attribute which is filtered.
* @param filter the filter object which contains a value, which needs to match or a flag if emptiness is
* checked.
* @param reference the attribute of the static metamodel for the referring entity.
* @param valueField the attribute of the static metamodel of the referred entity, where the equality should be
* checked.
* @param The type of the referenced entity.
* @return a Specification
*/
protected > Specification buildReferringEntitySpecification(
RangeFilter filter,
SetAttribute reference,
SingularAttribute valueField
) {
return buildReferringEntitySpecification(filter, root -> root.join(reference), entity -> entity.get(valueField));
}
/**
* Helper function to return a specification for filtering on one-to-many or many-to-many reference.Where equality, less
* than, greater than and less-than-or-equal-to and greater-than-or-equal-to and null/non-null conditions are
* supported. Usage:
*
* Specification<Employee> specByEmployeeId = buildReferringEntitySpecification(
* criteria.getEmployeId(),
* root -> root.get(Project_.company).join(Company_.employees),
* entity -> entity.get(Employee_.id));
* Specification<Employee> specByProjectName = buildReferringEntitySpecification(
* criteria.getProjectName(),
* root -> root.get(Project_.project)
* entity -> entity.get(Project_.name));
*
*
*
* @param The type of the attribute which is filtered.
* @param filter the filter object which contains a value, which needs to match or a flag if emptiness is
* checked.
* @param functionToEntity the function, which joins he current entity to the entity set, on which the filtering is applied.
* @param entityToColumn the function, which of the static metamodel of the referred entity, where the equality should be
* checked.
* @param The type of the referenced entity.
* @param The type of the entity which is the last before the OTHER in the chain.
* @return a Specification
*/
protected > Specification buildReferringEntitySpecification(
RangeFilter filter,
Function, SetJoin> functionToEntity,
Function, Expression> entityToColumn
) {
Function, Expression> fused = functionToEntity.andThen(entityToColumn);
if (filter.getEquals() != null) {
return equalsSpecification(fused, filter.getEquals());
} else if (filter.getIn() != null) {
return valueIn(fused, filter.getIn());
}
Specification result = Specification.where(null);
if (filter.getSpecified() != null) {
// Interestingly, 'functionToEntity' doesn't work, we need the longer lambda formula
result = result.and(byFieldSpecified(functionToEntity::apply, filter.getSpecified()));
}
if (filter.getNotEquals() != null) {
result = result.and(notEqualsSpecification(fused, filter.getNotEquals()));
}
if (filter.getNotIn() != null) {
result = result.and(valueNotIn(fused, filter.getNotIn()));
}
if (filter.getGreaterThan() != null) {
result = result.and(greaterThan(fused, filter.getGreaterThan()));
}
if (filter.getGreaterThanOrEqual() != null) {
result = result.and(greaterThanOrEqualTo(fused, filter.getGreaterThanOrEqual()));
}
if (filter.getLessThan() != null) {
result = result.and(lessThan(fused, filter.getLessThan()));
}
if (filter.getLessThanOrEqual() != null) {
result = result.and(lessThanOrEqualTo(fused, filter.getLessThanOrEqual()));
}
return result;
}
/**
* Generic method, which based on a Root<ENTITY> returns an Expression which type is the same as the given 'value' type.
*
* @param metaclassFunction function which returns the column which is used for filtering.
* @param value the actual value to filter for.
* @param The type of the attribute which is filtered.
* @return a Specification.
*/
protected Specification equalsSpecification(Function, Expression> metaclassFunction, X value) {
return (root, query, builder) -> builder.equal(metaclassFunction.apply(root), value);
}
/**
* Generic method, which based on a Root<ENTITY> returns an Expression which type is the same as the given 'value' type.
*
* @param metaclassFunction function which returns the column which is used for filtering.
* @param value the actual value to exclude for.
* @param The type of the attribute which is filtered.
* @return a Specification.
*/
protected Specification notEqualsSpecification(Function, Expression> metaclassFunction, X value) {
return (root, query, builder) -> builder.not(builder.equal(metaclassFunction.apply(root), value));
}
/**
* likeUpperSpecification.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param value a {@link java.lang.String} object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected Specification likeUpperSpecification(Function, Expression> metaclassFunction, String value) {
return (root, query, builder) -> builder.like(builder.upper(metaclassFunction.apply(root)), wrapLikeQuery(value));
}
/**
* doesNotContainSpecification.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param value a {@link java.lang.String} object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected Specification doesNotContainSpecification(
Function, Expression> metaclassFunction,
String value
) {
return (root, query, builder) -> builder.not(builder.like(builder.upper(metaclassFunction.apply(root)), wrapLikeQuery(value)));
}
/**
* byFieldSpecified.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param specified a boolean.
* @param a X object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected Specification byFieldSpecified(Function, Expression> metaclassFunction, boolean specified) {
return specified
? (root, query, builder) -> builder.isNotNull(metaclassFunction.apply(root))
: (root, query, builder) -> builder.isNull(metaclassFunction.apply(root));
}
/**
* byFieldEmptiness.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param specified a boolean.
* @param a X object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected Specification byFieldEmptiness(Function, Expression>> metaclassFunction, boolean specified) {
return specified
? (root, query, builder) -> builder.isNotEmpty(metaclassFunction.apply(root))
: (root, query, builder) -> builder.isEmpty(metaclassFunction.apply(root));
}
/**
* valueIn.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param values a {@link java.util.Collection} object.
* @param a X object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected Specification valueIn(Function, Expression> metaclassFunction, Collection values) {
return (root, query, builder) -> {
In in = builder.in(metaclassFunction.apply(root));
for (X value : values) {
in = in.value(value);
}
return in;
};
}
/**
* valueNotIn.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param values a {@link java.util.Collection} object.
* @param a X object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected Specification valueNotIn(Function, Expression> metaclassFunction, Collection values) {
return (root, query, builder) -> {
In in = builder.in(metaclassFunction.apply(root));
for (X value : values) {
in = in.value(value);
}
return builder.not(in);
};
}
/**
* greaterThanOrEqualTo.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param value a X object.
* @param a X object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected > Specification greaterThanOrEqualTo(
Function, Expression> metaclassFunction,
X value
) {
return (root, query, builder) -> builder.greaterThanOrEqualTo(metaclassFunction.apply(root), value);
}
/**
* greaterThan.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param value a X object.
* @param a X object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected > Specification greaterThan(
Function, Expression> metaclassFunction,
X value
) {
return (root, query, builder) -> builder.greaterThan(metaclassFunction.apply(root), value);
}
/**
* lessThanOrEqualTo.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param value a X object.
* @param a X object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected > Specification lessThanOrEqualTo(
Function, Expression> metaclassFunction,
X value
) {
return (root, query, builder) -> builder.lessThanOrEqualTo(metaclassFunction.apply(root), value);
}
/**
* lessThan.
*
* @param metaclassFunction a {@link java.util.function.Function} object.
* @param value a X object.
* @param a X object.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected > Specification lessThan(
Function, Expression> metaclassFunction,
X value
) {
return (root, query, builder) -> builder.lessThan(metaclassFunction.apply(root), value);
}
/**
* wrapLikeQuery.
*
* @param txt a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
*/
protected String wrapLikeQuery(String txt) {
return "%" + txt.toUpperCase() + '%';
}
/**
* distinct.
*
* @param distinct a boolean.
* @return a {@link org.springframework.data.jpa.domain.Specification} object.
*/
protected Specification distinct(boolean distinct) {
return (root, query, cb) -> {
query.distinct(distinct);
return null;
};
}
}