org.springframework.data.jpa.repository.support.SpecificationFactory Maven / Gradle / Ivy
The newest version!
package org.springframework.data.jpa.repository.support;
import static org.springframework.data.jpa.support.DataTablesUtils.ATTRIBUTE_SEPARATOR;
import static org.springframework.data.jpa.support.DataTablesUtils.ESCAPED_ATTRIBUTE_SEPARATOR;
import static org.springframework.data.jpa.support.DataTablesUtils.ESCAPED_NULL;
import static org.springframework.data.jpa.support.DataTablesUtils.ESCAPED_OR_SEPARATOR;
import static org.springframework.data.jpa.support.DataTablesUtils.ESCAPE_CHAR;
import static org.springframework.data.jpa.support.DataTablesUtils.NULL;
import static org.springframework.data.jpa.support.DataTablesUtils.OR_SEPARATOR;
import static org.springframework.data.jpa.support.DataTablesUtils.getLikeFilterValue;
import static org.springframework.data.jpa.support.DataTablesUtils.isBoolean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.persistence.Search;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.From;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute.PersistentAttributeType;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.support.Column;
import org.springframework.data.jpa.support.DataTablesInput;
import org.springframework.util.StringUtils;
class SpecificationFactory {
public static Specification createSpecification(final DataTablesInput input, final Search search) {
return new DataTablesSpecification(input, search);
}
private static class DataTablesSpecification implements Specification {
private final DataTablesInput input;
private final Search search;
public DataTablesSpecification(DataTablesInput input, Search search) {
this.input = input;
this.search = search;
}
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
Predicate predicate = cb.conjunction();
Expression booleanExpression;
Expression stringExpression;
List searchs = search == null ? Collections.emptyList() : Arrays.asList(search.value());
for (Column column : input.getColumns()) {
String filterValue = column.getSearch().getValue();
boolean isColumnSearchable = searchs.contains(column.getData()) && column.getSearchable() && StringUtils.hasText(filterValue);
if (!isColumnSearchable) {
continue;
}
if (filterValue.contains(OR_SEPARATOR)) {
boolean nullable = false;
List values = new ArrayList();
for (String value : filterValue.split(ESCAPED_OR_SEPARATOR)) {
if (NULL.equals(value)) {
nullable = true;
}
else {
values.add(ESCAPED_NULL.equals(value) ? NULL : value);
}
}
if (values.size() > 0 && isBoolean(values.get(0))) {
Object[] booleanValues = new Boolean[values.size()];
for (int i = 0; i < values.size(); i++) {
booleanValues[i] = Boolean.valueOf(values.get(i));
}
booleanExpression = getExpression(root, column.getData(), Boolean.class);
Predicate in = booleanExpression.in(booleanValues);
if (nullable) {
predicate = cb.and(predicate, cb.or(in, booleanExpression.isNull()));
}
else {
predicate = cb.and(predicate, in);
}
}
else {
stringExpression = getExpression(root, column.getData(), String.class);
if (values.isEmpty()) {
if (nullable) {
predicate = cb.and(predicate, stringExpression.isNull());
}
continue;
}
Predicate in = stringExpression.in(values);
if (nullable) {
predicate = cb.and(predicate, cb.or(in, stringExpression.isNull()));
}
else {
predicate = cb.and(predicate, in);
}
}
continue;
}
if (isBoolean(filterValue)) {
booleanExpression = getExpression(root, column.getData(), Boolean.class);
predicate = cb.and(predicate, cb.equal(booleanExpression, Boolean.valueOf(filterValue)));
continue;
}
stringExpression = getExpression(root, column.getData(), String.class);
if (NULL.equals(filterValue)) {
predicate = cb.and(predicate, stringExpression.isNull());
continue;
}
String likeFilterValue = getLikeFilterValue(ESCAPED_NULL.equals(filterValue) ? NULL : filterValue);
predicate = cb.and(predicate, cb.like(cb.lower(stringExpression), likeFilterValue, ESCAPE_CHAR));
}
String globalFilterValue = input.getSearch().getValue();
if (StringUtils.hasText(globalFilterValue)) {
Predicate matchOneColumnPredicate = cb.disjunction();
for (Column column : input.getColumns()) {
if (searchs.contains(column.getData()) && column.getSearchable()) {
Expression expression = getExpression(root, column.getData(), String.class);
matchOneColumnPredicate = cb.or(matchOneColumnPredicate, cb.like(cb.lower(expression), getLikeFilterValue(globalFilterValue), ESCAPE_CHAR));
}
}
predicate = cb.and(predicate, matchOneColumnPredicate);
}
boolean isCountQuery = query.getResultType() == Long.class;
if (isCountQuery) {
return predicate;
}
for (Column column : input.getColumns()) {
boolean isJoinable = searchs.contains(column.getData()) && column.getSearchable() && column.getData().contains(ATTRIBUTE_SEPARATOR);
if (!isJoinable) {
continue;
}
String[] values = column.getData().split(ESCAPED_ATTRIBUTE_SEPARATOR);
PersistentAttributeType type = root.getModel().getAttribute(values[0]).getPersistentAttributeType();
if (type != PersistentAttributeType.ONE_TO_ONE && type != PersistentAttributeType.MANY_TO_ONE) {
continue;
}
Fetch fetch = null;
for (int i = 0; i < values.length - 1; i++) {
fetch = (fetch == null ? root : fetch).fetch(values[i], JoinType.LEFT);
}
}
return predicate;
}
}
private static Expression getExpression(Root root, String columnData, Class clazz) {
if (!columnData.contains(ATTRIBUTE_SEPARATOR)) {
return root.get(columnData).as(clazz);
}
String[] values = columnData.split(ESCAPED_ATTRIBUTE_SEPARATOR);
if (root.getModel().getAttribute(values[0]).getPersistentAttributeType() == PersistentAttributeType.EMBEDDED) {
return root.get(values[0]).get(values[1]).as(clazz);
}
From from = root;
for (int i = 0; i < values.length - 1; i++) {
from = from.join(values[i], JoinType.LEFT);
}
return from.get(values[values.length - 1]).as(clazz);
}
}