Skip to content

Commit 6247207

Browse files
committed
Consistently use InvalidDataAccessApiUsageException for JpaOrder validation.
Closes #4062
1 parent b6f3a7f commit 6247207

File tree

8 files changed

+32
-25
lines changed

8 files changed

+32
-25
lines changed

spring-data-envers/src/main/java/org/springframework/data/envers/repository/support/EnversRevisionRepositoryImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.data.history.RevisionMetadata;
4545
import org.springframework.data.history.RevisionSort;
4646
import org.springframework.data.history.Revisions;
47+
import org.springframework.data.jpa.repository.query.QueryUtils;
4748
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
4849
import org.springframework.data.repository.core.EntityInformation;
4950
import org.springframework.data.repository.history.RevisionRepository;
@@ -166,6 +167,7 @@ private List<AuditOrder> mapPropertySort(Sort sort) {
166167
List<AuditOrder> result = new ArrayList<>();
167168
for (Sort.Order order : sort) {
168169

170+
QueryUtils.checkSortExpression(order);
169171
AuditProperty<Object> property = AuditEntity.property(order.getProperty());
170172
AuditOrder auditOrder = order.getDirection().isAscending() //
171173
? property.asc() //

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

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@
88
import java.util.Set;
99
import java.util.regex.Pattern;
1010

11-
import org.springframework.dao.InvalidDataAccessApiUsageException;
12-
1311
import org.jspecify.annotations.Nullable;
12+
1413
import org.springframework.data.domain.Sort;
1514
import org.springframework.data.domain.Sort.NullHandling;
16-
import org.springframework.data.jpa.domain.JpaSort;
1715
import org.springframework.util.ObjectUtils;
1816

1917
/**
@@ -61,7 +59,7 @@ List<QueryToken> orderBy(@Nullable String primaryFromAlias, Sort sort) {
6159

6260
sort.forEach(order -> {
6361

64-
checkSortExpression(order);
62+
QueryUtils.checkSortExpression(order);
6563

6664
StringBuilder builder = new StringBuilder();
6765

@@ -94,23 +92,6 @@ List<QueryToken> orderBy(@Nullable String primaryFromAlias, Sort sort) {
9492
return tokens;
9593
}
9694

97-
/**
98-
* Check any given {@link JpaSort.JpaOrder#isUnsafe()} order for presence of at least one property offending the
99-
* {@link #PUNCTUATION_PATTERN} and throw an {@link Exception} indicating potential unsafe order by expression.
100-
*
101-
* @param order
102-
*/
103-
private void checkSortExpression(Sort.Order order) {
104-
105-
if (order instanceof JpaSort.JpaOrder jpaOrder && jpaOrder.isUnsafe()) {
106-
return;
107-
}
108-
109-
if (PUNCTUATION_PATTERN.matcher(order.getProperty()).find()) {
110-
throw new InvalidDataAccessApiUsageException(String.format(UNSAFE_PROPERTY_REFERENCE, order));
111-
}
112-
}
113-
11495
/**
11596
* Using the {@code primaryFromAlias} and the {@link org.springframework.data.domain.Sort.Order}, construct a suitable
11697
* argument to be added to an {@literal ORDER BY} expression.

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ public static Collection<String> getProjectionInputProperties(JpaEntityInformati
6464
Collection<String> projectionProperties, Sort sort) {
6565

6666
Collection<String> properties = new LinkedHashSet<>(projectionProperties);
67-
sort.forEach(it -> properties.add(it.getProperty()));
67+
sort.forEach(it -> {
68+
QueryUtils.checkSortExpression(it);
69+
properties.add(it.getProperty());
70+
});
6871
properties.addAll(entity.getIdAttributeNames());
6972

7073
return properties;
@@ -95,6 +98,7 @@ public static Collection<String> getProjectionInputProperties(JpaEntityInformati
9598
int j = 0;
9699
for (Order inner : sort) {
97100

101+
QueryUtils.checkSortExpression(order);
98102
E propertyExpression = strategy.createExpression(inner.getProperty());
99103
Object o = keysetValues.get(inner.getProperty());
100104

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ public CriteriaBuilderStrategy(From<?, ?> from, CriteriaBuilder cb) {
101101

102102
@Override
103103
public Expression<Comparable> createExpression(String property) {
104-
105104
PropertyPath path = PropertyPath.from(property, from.getJavaType());
106105
return QueryUtils.toExpressionRecursively(from, path);
107106
}
@@ -164,6 +163,8 @@ public JpqlQueryBuilder.Predicate compare(Order order, JpqlQueryBuilder.Expressi
164163
if (value == null) {
165164
return order.isAscending() ? where.isNull() : where.isNotNull();
166165
}
166+
167+
QueryUtils.checkSortExpression(order);
167168
return order.isAscending() ? where.gt(factory.capture(order.getProperty(), value))
168169
: where.lt(factory.capture(order.getProperty(), value));
169170
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,8 @@ private static jakarta.persistence.criteria.Order toJpaOrder(Order order, From<?
751751

752752
Expression<?> expression;
753753

754+
checkSortExpression(order);
755+
754756
if (order instanceof JpaOrder jpaOrder && jpaOrder.isUnsafe()) {
755757
expression = new HqlOrderExpressionVisitor(cb, from, QueryUtils::toExpressionRecursively)
756758
.createCriteriaExpression(order);
@@ -784,7 +786,7 @@ private static Nulls toNulls(Sort.NullHandling nullHandling) {
784786
*
785787
* @param order
786788
*/
787-
static void checkSortExpression(Order order) {
789+
public static void checkSortExpression(Order order) {
788790

789791
if (order instanceof JpaOrder jpaOrder && jpaOrder.isUnsafe()) {
790792
return;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ private static <T> Window<T> createWindow(Sort sort, int limit, Direction direct
8686
IntFunction<ScrollPosition> positionFunction = value -> {
8787

8888
T object = resultsToUse.get(value);
89-
Map<String, Object> keys = entity.getKeyset(sort.stream().map(Order::getProperty).toList(), object);
89+
Map<String, Object> keys = entity
90+
.getKeyset(sort.stream().peek(QueryUtils::checkSortExpression).map(Order::getProperty).toList(), object);
9091

9192
return ScrollPosition.of(keys, direction);
9293
};

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.data.domain.Sort;
2424
import org.springframework.data.domain.Sort.Order;
2525
import org.springframework.data.jpa.provider.PersistenceProvider;
26+
import org.springframework.data.jpa.repository.query.QueryUtils;
2627
import org.springframework.data.mapping.PropertyPath;
2728
import org.springframework.data.querydsl.QSort;
2829
import org.springframework.util.Assert;
@@ -228,6 +229,7 @@ private Expression<?> buildOrderPropertyPathFrom(Order order) {
228229

229230
Assert.notNull(order, "Order must not be null");
230231

232+
QueryUtils.checkSortExpression(order);
231233
PropertyPath path = PropertyPath.from(order.getProperty(), builder.getType());
232234
Expression<?> sortPropertyExpression = builder;
233235

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.junit.jupiter.api.extension.ExtendWith;
5151
import org.mockito.Mockito;
5252

53+
import org.springframework.dao.InvalidDataAccessApiUsageException;
5354
import org.springframework.data.domain.Sort;
5455
import org.springframework.data.domain.Sort.Direction;
5556
import org.springframework.data.jpa.domain.sample.Category;
@@ -358,6 +359,19 @@ void toOrdersCanSortByJoinColumn() {
358359
assertThat(orders).hasSize(1);
359360
}
360361

362+
@Test //
363+
void shouldReportCorrectException() {
364+
365+
CriteriaBuilder builder = em.getCriteriaBuilder();
366+
CriteriaQuery<User> query = builder.createQuery(User.class);
367+
Root<User> root = query.from(User.class);
368+
369+
Sort sort = Sort.by(Direction.ASC, "LENGTH(username)");
370+
371+
assertThatExceptionOfType(InvalidDataAccessApiUsageException.class)
372+
.isThrownBy(() -> QueryUtils.toOrders(sort, root, builder));
373+
}
374+
361375
@Test // GH-3529, GH-3587
362376
void queryUtilsConsidersNullPrecedence() {
363377

0 commit comments

Comments
 (0)