Skip to content

Commit

Permalink
Consider NULLS precedence using Sort for Criteria Queries.
Browse files Browse the repository at this point in the history
Closes: #3587
Original Pull Request: #3695
  • Loading branch information
mp911de authored and christophstrobl committed Dec 20, 2024
1 parent 5c205e3 commit 1c79fa8
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import jakarta.persistence.criteria.From;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Nulls;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.Attribute.PersistentAttributeType;
Expand Down Expand Up @@ -292,9 +293,8 @@ public static String applySorting(String query, Sort sort, @Nullable String alia
Set<String> selectionAliases = getFunctionAliases(query);
selectionAliases.addAll(getFieldAliases(query));

String orderClauses = sort.stream()
.map(order -> getOrderClause(joinAliases, selectionAliases, alias, order))
.collect(Collectors.joining(", "));
String orderClauses = sort.stream().map(order -> getOrderClause(joinAliases, selectionAliases, alias, order))
.collect(Collectors.joining(", "));

builder.append(orderClauses);

Expand Down Expand Up @@ -753,18 +753,25 @@ private static jakarta.persistence.criteria.Order toJpaOrder(Order order, From<?
PropertyPath property = PropertyPath.from(order.getProperty(), from.getJavaType());
Expression<?> expression = toExpressionRecursively(from, property);

if (order.getNullHandling() != Sort.NullHandling.NATIVE) {
throw new UnsupportedOperationException("Applying Null Precedence using Criteria Queries is not yet supported.");
}
Nulls nulls = toNulls(order.getNullHandling());

if (order.isIgnoreCase() && String.class.equals(expression.getJavaType())) {
Expression<String> upper = cb.lower((Expression<String>) expression);
return order.isAscending() ? cb.asc(upper) : cb.desc(upper);
return order.isAscending() ? cb.asc(upper, nulls) : cb.desc(upper, nulls);
} else {
return order.isAscending() ? cb.asc(expression) : cb.desc(expression);
return order.isAscending() ? cb.asc(expression, nulls) : cb.desc(expression, nulls);
}
}

private static Nulls toNulls(Sort.NullHandling nullHandling) {

return switch (nullHandling) {
case NULLS_LAST -> Nulls.LAST;
case NULLS_FIRST -> Nulls.FIRST;
case NATIVE -> Nulls.NONE;
};
}

static <T> Expression<T> toExpressionRecursively(From<?, ?> from, PropertyPath property) {
return toExpressionRecursively(from, property, false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import jakarta.persistence.criteria.From;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Nulls;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.spi.PersistenceProvider;
import jakarta.persistence.spi.PersistenceProviderResolver;
Expand Down Expand Up @@ -314,8 +315,8 @@ void toOrdersCanSortByJoinColumn() {
assertThat(orders).hasSize(1);
}

@Test // GH-3529
void nullPrecedenceThroughCriteriaApiNotYetSupported() {
@Test // GH-3529, GH-3587
void queryUtilsConsidersNullPrecedence() {

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Expand All @@ -324,8 +325,10 @@ void nullPrecedenceThroughCriteriaApiNotYetSupported() {

Sort sort = Sort.by(Sort.Order.desc("manager").nullsFirst());

assertThatExceptionOfType(UnsupportedOperationException.class)
.isThrownBy(() -> QueryUtils.toOrders(sort, join, builder));
List<jakarta.persistence.criteria.Order> orders = QueryUtils.toOrders(sort, join, builder);
for (jakarta.persistence.criteria.Order order : orders) {
assertThat(order.getNullPrecedence()).isEqualTo(Nulls.FIRST);
}
}

/**
Expand Down

0 comments on commit 1c79fa8

Please sign in to comment.