/*
 * Copyright 2013-2020 the original author or authors.
 *
 * 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
 *
 *      https://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 org.springframework.data.jpa.domain;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.PluralAttribute;

import org.springframework.data.domain.Sort;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

Sort option for queries that wraps JPA meta-model Attributes for sorting.
Author:Thomas Darimont, Oliver Gierke, Christoph Strobl, David Madden
/** * Sort option for queries that wraps JPA meta-model {@link Attribute}s for sorting. * * @author Thomas Darimont * @author Oliver Gierke * @author Christoph Strobl * @author David Madden */
public class JpaSort extends Sort { private static final long serialVersionUID = 1L;
Creates a new JpaSort for the given attributes with the default sort direction.
Params:
  • attributes – must not be null or empty.
/** * Creates a new {@link JpaSort} for the given attributes with the default sort direction. * * @param attributes must not be {@literal null} or empty. */
public JpaSort(Attribute<?, ?>... attributes) { this(DEFAULT_DIRECTION, attributes); }
Creates a new JpaSort instance with the given Paths.
Params:
  • paths – must not be null or empty.
/** * Creates a new {@link JpaSort} instance with the given {@link Path}s. * * @param paths must not be {@literal null} or empty. */
public JpaSort(JpaSort.Path<?, ?>... paths) { this(DEFAULT_DIRECTION, paths); }
Creates a new JpaSort for the given direction and attributes.
Params:
  • direction – the sorting direction.
  • attributes – must not be null or empty.
/** * Creates a new {@link JpaSort} for the given direction and attributes. * * @param direction the sorting direction. * @param attributes must not be {@literal null} or empty. */
public JpaSort(Direction direction, Attribute<?, ?>... attributes) { this(direction, paths(attributes)); }
Creates a new JpaSort for the given direction and Paths.
Params:
  • direction – the sorting direction.
  • paths – must not be null or empty.
/** * Creates a new {@link JpaSort} for the given direction and {@link Path}s. * * @param direction the sorting direction. * @param paths must not be {@literal null} or empty. */
public JpaSort(Direction direction, Path<?, ?>... paths) { this(direction, Arrays.asList(paths)); } private JpaSort(Direction direction, List<Path<?, ?>> paths) { this(Collections.<Order> emptyList(), direction, paths); } private JpaSort(List<Order> orders, @Nullable Direction direction, List<Path<?, ?>> paths) { super(combine(orders, direction, paths)); } private JpaSort(List<Order> orders) { super(orders); }
Returns a new JpaSort with the given sorting criteria added to the current one.
Params:
  • direction – can be null.
  • attributes – must not be null.
Returns:
/** * Returns a new {@link JpaSort} with the given sorting criteria added to the current one. * * @param direction can be {@literal null}. * @param attributes must not be {@literal null}. * @return */
public JpaSort and(@Nullable Direction direction, Attribute<?, ?>... attributes) { Assert.notNull(attributes, "Attributes must not be null!"); return and(direction, paths(attributes)); }
Returns a new JpaSort with the given sorting criteria added to the current one.
Params:
  • direction – can be null.
  • paths – must not be null.
Returns:
/** * Returns a new {@link JpaSort} with the given sorting criteria added to the current one. * * @param direction can be {@literal null}. * @param paths must not be {@literal null}. * @return */
public JpaSort and(@Nullable Direction direction, Path<?, ?>... paths) { Assert.notNull(paths, "Paths must not be null!"); List<Order> existing = new ArrayList<Order>(); for (Order order : this) { existing.add(order); } return new JpaSort(existing, direction, Arrays.asList(paths)); }
Returns a new JpaSort with the given sorting criteria added to the current one.
Params:
  • direction – can be null.
  • properties – must not be null or empty.
Returns:
/** * Returns a new {@link JpaSort} with the given sorting criteria added to the current one. * * @param direction can be {@literal null}. * @param properties must not be {@literal null} or empty. * @return */
public JpaSort andUnsafe(@Nullable Direction direction, String... properties) { Assert.notEmpty(properties, "Properties must not be empty!"); List<Order> orders = new ArrayList<Order>(); for (Order order : this) { orders.add(order); } for (String property : properties) { orders.add(new JpaOrder(direction, property)); } return new JpaSort(orders, direction, Collections.<Path<?, ?>> emptyList()); }
Turns the given Attributes into Paths.
Params:
  • attributes – must not be null or empty.
Returns:
/** * Turns the given {@link Attribute}s into {@link Path}s. * * @param attributes must not be {@literal null} or empty. * @return */
private static Path<?, ?>[] paths(Attribute<?, ?>[] attributes) { Assert.notNull(attributes, "Attributes must not be null!"); Assert.notEmpty(attributes, "Attributes must not be empty!"); Path<?, ?>[] paths = new Path[attributes.length]; for (int i = 0; i < attributes.length; i++) { paths[i] = path(attributes[i]); } return paths; } private static List<Order> combine(List<Order> orders, @Nullable Direction direction, List<Path<?, ?>> paths) { List<Order> result = new ArrayList<Sort.Order>(orders); for (Path<?, ?> path : paths) { result.add(new Order(direction, path.toString())); } return result; }
Creates a new Path for the given Attribute.
Params:
  • attribute – must not be null.
Returns:
/** * Creates a new {@link Path} for the given {@link Attribute}. * * @param attribute must not be {@literal null}. * @return */
public static <A extends Attribute<T, S>, T, S> Path<T, S> path(A attribute) { Assert.notNull(attribute, "Attribute must not be null!"); return new Path<>(Collections.singletonList(attribute)); }
Creates a new Path for the given PluralAttribute.
Params:
  • attribute – must not be null.
Returns:
/** * Creates a new {@link Path} for the given {@link PluralAttribute}. * * @param attribute must not be {@literal null}. * @return */
public static <P extends PluralAttribute<T, ?, S>, T, S> Path<T, S> path(P attribute) { Assert.notNull(attribute, "Attribute must not be null!"); return new Path<>(Collections.singletonList(attribute)); }
Creates new unsafe JpaSort based on given properties.
Params:
  • properties – must not be null or empty.
Returns:
/** * Creates new unsafe {@link JpaSort} based on given properties. * * @param properties must not be {@literal null} or empty. * @return */
public static JpaSort unsafe(String... properties) { return unsafe(Sort.DEFAULT_DIRECTION, properties); }
Creates new unsafe JpaSort based on given Direction and properties.
Params:
  • direction – must not be null.
  • properties – must not be null or empty.
Returns:
/** * Creates new unsafe {@link JpaSort} based on given {@link Direction} and properties. * * @param direction must not be {@literal null}. * @param properties must not be {@literal null} or empty. * @return */
public static JpaSort unsafe(Direction direction, String... properties) { Assert.notNull(direction, "Direction must not be null!"); Assert.notEmpty(properties, "Properties must not be empty!"); Assert.noNullElements(properties, "Properties must not contain null values!"); return unsafe(direction, Arrays.asList(properties)); }
Creates new unsafe JpaSort based on given Direction and properties.
Params:
  • direction – must not be null.
  • properties – must not be null or empty.
Returns:
/** * Creates new unsafe {@link JpaSort} based on given {@link Direction} and properties. * * @param direction must not be {@literal null}. * @param properties must not be {@literal null} or empty. * @return */
public static JpaSort unsafe(Direction direction, List<String> properties) { Assert.notEmpty(properties, "Properties must not be empty!"); List<Order> orders = new ArrayList<>(properties.size()); for (String property : properties) { orders.add(new JpaOrder(direction, property)); } return new JpaSort(orders); }
Value object to abstract a collection of Attributes.
Author:Oliver Gierke
/** * Value object to abstract a collection of {@link Attribute}s. * * @author Oliver Gierke */
public static class Path<T, S> { private final Collection<Attribute<?, ?>> attributes; private Path(List<? extends Attribute<?, ?>> attributes) { this.attributes = Collections.unmodifiableList(attributes); }
Collects the given Attribute and returning a new Path pointing to the attribute type.
Params:
  • attribute – must not be null.
Returns:
/** * Collects the given {@link Attribute} and returning a new {@link Path} pointing to the attribute type. * * @param attribute must not be {@literal null}. * @return */
public <A extends Attribute<S, U>, U> Path<S, U> dot(A attribute) { return new Path<S, U>(add(attribute)); }
Collects the given PluralAttribute and returning a new Path pointing to the attribute type.
Params:
  • attribute – must not be null.
Returns:
/** * Collects the given {@link PluralAttribute} and returning a new {@link Path} pointing to the attribute type. * * @param attribute must not be {@literal null}. * @return */
public <P extends PluralAttribute<S, ?, U>, U> Path<S, U> dot(P attribute) { return new Path<S, U>(add(attribute)); } private List<Attribute<?, ?>> add(Attribute<?, ?> attribute) { Assert.notNull(attribute, "Attribute must not be null!"); List<Attribute<?, ?>> newAttributes = new ArrayList<Attribute<?, ?>>(attributes.size() + 1); newAttributes.addAll(attributes); newAttributes.add(attribute); return newAttributes; } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder builder = new StringBuilder(); for (Attribute<?, ?> attribute : attributes) { builder.append(attribute.getName()).append("."); } return builder.length() == 0 ? "" : builder.substring(0, builder.lastIndexOf(".")); } }
Custom Order that keeps a flag to indicate unsafe property handling, i.e. the String provided is not necessarily a property but can be an arbitrary expression piped into the query execution. We also keep an additional ignoreCase flag around as the constructor of the superclass is private currently.
Author:Christoph Strobl, Oliver Gierke
/** * Custom {@link Order} that keeps a flag to indicate unsafe property handling, i.e. the String provided is not * necessarily a property but can be an arbitrary expression piped into the query execution. We also keep an * additional {@code ignoreCase} flag around as the constructor of the superclass is private currently. * * @author Christoph Strobl * @author Oliver Gierke */
public static class JpaOrder extends Order { private static final long serialVersionUID = 1L; private final boolean unsafe; private final boolean ignoreCase;
Creates a new JpaOrder instance. if order is null then order defaults to Sort.DEFAULT_DIRECTION
Params:
/** * Creates a new {@link JpaOrder} instance. if order is {@literal null} then order defaults to * {@link Sort#DEFAULT_DIRECTION} * * @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}. * @param property must not be {@literal null}. */
private JpaOrder(@Nullable Direction direction, String property) { this(direction, property, NullHandling.NATIVE); }
Creates a new Order instance. if order is null then order defaults to Sort.DEFAULT_DIRECTION.
Params:
/** * Creates a new {@link Order} instance. if order is {@literal null} then order defaults to * {@link Sort#DEFAULT_DIRECTION}. * * @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}. * @param property must not be {@literal null}. * @param nullHandlingHint can be {@literal null}, will default to {@link NullHandling#NATIVE}. */
private JpaOrder(@Nullable Direction direction, String property, NullHandling nullHandlingHint) { this(direction, property, nullHandlingHint, false, true); } private JpaOrder(@Nullable Direction direction, String property, NullHandling nullHandling, boolean ignoreCase, boolean unsafe) { super(direction, property, nullHandling); this.ignoreCase = ignoreCase; this.unsafe = unsafe; } /* * (non-Javadoc) * @see org.springframework.data.domain.Sort.Order#with(org.springframework.data.domain.Sort.Direction) */ @Override public JpaOrder with(Direction order) { return new JpaOrder(order, getProperty(), getNullHandling(), isIgnoreCase(), this.unsafe); } /* * (non-Javadoc) * @see org.springframework.data.domain.Sort.Order#with(org.springframework.data.domain.Sort.NullHandling) */ @Override public JpaOrder with(NullHandling nullHandling) { return new JpaOrder(getDirection(), getProperty(), nullHandling, isIgnoreCase(), this.unsafe); }
Creates new Sort with potentially unsafe Order instances.
Params:
  • properties – must not be null.
Returns:
/** * Creates new {@link Sort} with potentially unsafe {@link Order} instances. * * @param properties must not be {@literal null}. * @return */
public Sort withUnsafe(String... properties) { Assert.notEmpty(properties, "Properties must not be empty!"); Assert.noNullElements(properties, "Properties must not contain null values!"); List<Order> orders = new ArrayList<>(properties.length); for (String property : properties) { orders.add(new JpaOrder(getDirection(), property, getNullHandling(), isIgnoreCase(), this.unsafe)); } return Sort.by(orders); } /* * (non-Javadoc) * @see org.springframework.data.domain.Sort.Order#ignoreCase() */ @Override public JpaOrder ignoreCase() { return new JpaOrder(getDirection(), getProperty(), getNullHandling(), true, this.unsafe); } /* * (non-Javadoc) * @see org.springframework.data.domain.Sort.Order#isIgnoreCase() */ @Override public boolean isIgnoreCase() { return super.isIgnoreCase() || ignoreCase; }
Returns:true if JpaOrder created withUnsafe(String...).
/** * @return true if {@link JpaOrder} created {@link #withUnsafe(String...)}. */
public boolean isUnsafe() { return unsafe; } } }