Skip to content

Commit 52e6aef

Browse files
committed
Polishing.
Use structured NullsPrecedence rendering in JPQL renderer. Throw UnsupportedOperationException for the time being until Nulls Precedence is supported through Criteria API. See #3529
1 parent 410152a commit 52e6aef

File tree

3 files changed

+35
-8
lines changed

3 files changed

+35
-8
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpqlQueryRenderer.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ public QueryTokenStream visitOrderby_item(JpqlParser.Orderby_itemContext ctx) {
796796
builder.append(QueryTokens.expression(ctx.DESC()));
797797
}
798798

799-
if(ctx.nullsPrecedence() != null) {
799+
if (ctx.nullsPrecedence() != null) {
800800
builder.append(visit(ctx.nullsPrecedence()));
801801
}
802802

@@ -805,8 +805,18 @@ public QueryTokenStream visitOrderby_item(JpqlParser.Orderby_itemContext ctx) {
805805

806806
@Override
807807
public QueryTokenStream visitNullsPrecedence(NullsPrecedenceContext ctx) {
808-
// return QueryTokenStream.concat(ctx.children, it-> QueryRendererBuilder.from(QueryTokens.token(it.getText())), TOKEN_SPACE);
809-
return QueryTokenStream.justAs(ctx.children, it-> QueryTokens.token(it.getText()));
808+
809+
QueryRendererBuilder builder = QueryRenderer.builder();
810+
811+
builder.append(TOKEN_NULLS);
812+
813+
if (ctx.FIRST() != null) {
814+
builder.append(TOKEN_FIRST);
815+
} else if (ctx.LAST() != null) {
816+
builder.append(TOKEN_LAST);
817+
}
818+
819+
return builder;
810820
}
811821

812822
@Override

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java

+4
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,10 @@ private static jakarta.persistence.criteria.Order toJpaOrder(Order order, From<?
753753
PropertyPath property = PropertyPath.from(order.getProperty(), from.getJavaType());
754754
Expression<?> expression = toExpressionRecursively(from, property);
755755

756+
if (order.getNullHandling() != Sort.NullHandling.NATIVE) {
757+
throw new UnsupportedOperationException("Applying Null Precedence using Criteria Queries is not yet supported.");
758+
}
759+
756760
if (order.isIgnoreCase() && String.class.equals(expression.getJavaType())) {
757761
Expression<String> upper = cb.lower((Expression<String>) expression);
758762
return order.isAscending() ? cb.asc(upper) : cb.desc(upper);

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java

+18-5
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import static java.util.Collections.singletonList;
19-
import static org.assertj.core.api.Assertions.assertThat;
20-
import static org.mockito.Mockito.doReturn;
21-
import static org.mockito.Mockito.times;
22-
import static org.mockito.Mockito.verify;
18+
import static java.util.Collections.*;
19+
import static org.assertj.core.api.Assertions.*;
20+
import static org.mockito.Mockito.*;
2321

2422
import jakarta.persistence.Entity;
2523
import jakarta.persistence.EntityManager;
@@ -48,6 +46,7 @@
4846
import org.junit.jupiter.api.Test;
4947
import org.junit.jupiter.api.extension.ExtendWith;
5048
import org.mockito.Mockito;
49+
5150
import org.springframework.data.domain.Sort;
5251
import org.springframework.data.domain.Sort.Direction;
5352
import org.springframework.data.jpa.domain.sample.Category;
@@ -315,6 +314,20 @@ void toOrdersCanSortByJoinColumn() {
315314
assertThat(orders).hasSize(1);
316315
}
317316

317+
@Test // GH-3529
318+
void nullPrecedenceThroughCriteriaApiNotYetSupported() {
319+
320+
CriteriaBuilder builder = em.getCriteriaBuilder();
321+
CriteriaQuery<User> query = builder.createQuery(User.class);
322+
Root<User> root = query.from(User.class);
323+
Join<User, User> join = root.join("manager", JoinType.LEFT);
324+
325+
Sort sort = Sort.by(Sort.Order.desc("manager").nullsFirst());
326+
327+
assertThatExceptionOfType(UnsupportedOperationException.class)
328+
.isThrownBy(() -> QueryUtils.toOrders(sort, join, builder));
329+
}
330+
318331
/**
319332
* This test documents an ambiguity in the JPA spec (or it's implementation in Hibernate vs EclipseLink) that we have
320333
* to work around in the test {@link #doesNotCreateJoinForOptionalAssociationWithoutFurtherNavigation()}. See also:

0 commit comments

Comments
 (0)