All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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

}