Skip to content

Commit c5e95b2

Browse files
committed
Add AOT fragment integration tests for UserRepositoryTests.
Add support for properties-based named queries. Align AOT implementation with reflective repository behavior. Closes #3950
1 parent 0b581c5 commit c5e95b2

File tree

13 files changed

+512
-252
lines changed

13 files changed

+512
-252
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotQueries.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ public Map<String, Object> serialize() {
101101
serialized.put("query", sq.getQueryString());
102102
}
103103

104+
if (result() instanceof StringAotQuery.NamedStringAotQuery nsq) {
105+
serialized.put("name", nsq.getQueryName());
106+
}
107+
104108
if (paging) {
105109

106110
if (count() instanceof NamedAotQuery nq) {
@@ -112,6 +116,10 @@ public Map<String, Object> serialize() {
112116
if (count() instanceof StringAotQuery sq) {
113117
serialized.put("count-query", sq.getQueryString());
114118
}
119+
120+
if (count() instanceof StringAotQuery.NamedStringAotQuery nsq) {
121+
serialized.put("count-name", nsq.getQueryName());
122+
}
115123
}
116124

117125
return serialized;

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/AotRepositoryFragmentSupport.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,27 @@
1717

1818
import jakarta.persistence.Tuple;
1919

20+
import java.lang.reflect.Array;
2021
import java.lang.reflect.Method;
22+
import java.util.ArrayList;
2123
import java.util.Collection;
24+
import java.util.Optional;
25+
import java.util.function.UnaryOperator;
2226
import java.util.stream.Stream;
2327

2428
import org.jspecify.annotations.Nullable;
2529

2630
import org.springframework.core.CollectionFactory;
31+
import org.springframework.core.convert.ConversionService;
32+
import org.springframework.core.convert.support.ConfigurableConversionService;
33+
import org.springframework.core.convert.support.DefaultConversionService;
2734
import org.springframework.data.domain.Slice;
2835
import org.springframework.data.domain.Sort;
2936
import org.springframework.data.expression.ValueEvaluationContextProvider;
3037
import org.springframework.data.expression.ValueExpression;
3138
import org.springframework.data.jpa.repository.query.DeclaredQuery;
3239
import org.springframework.data.jpa.repository.query.JpaParameters;
40+
import org.springframework.data.jpa.repository.query.JpaResultConverters;
3341
import org.springframework.data.jpa.repository.query.QueryEnhancer;
3442
import org.springframework.data.jpa.repository.query.QueryEnhancerSelector;
3543
import org.springframework.data.jpa.util.TupleBackedMap;
@@ -50,6 +58,19 @@
5058
*/
5159
public class AotRepositoryFragmentSupport {
5260

61+
private static final ConversionService CONVERSION_SERVICE;
62+
63+
static {
64+
65+
ConfigurableConversionService conversionService = new DefaultConversionService();
66+
67+
conversionService.addConverter(JpaResultConverters.BlobToByteArrayConverter.INSTANCE);
68+
conversionService.removeConvertible(Collection.class, Object.class);
69+
conversionService.removeConvertible(Object.class, Optional.class);
70+
71+
CONVERSION_SERVICE = conversionService;
72+
}
73+
5374
private final RepositoryMetadata repositoryMetadata;
5475

5576
private final ValueExpressionDelegate valueExpressions;
@@ -111,6 +132,41 @@ protected String rewriteQuery(DeclaredQuery query, Sort sort, Class<?> returnedT
111132
return expression.evaluate(contextProvider.getEvaluationContext(args, expression.getExpressionDependencies()));
112133
}
113134

135+
protected @Nullable Object mapIgnoreCase(@Nullable Object source, UnaryOperator<String> mapper) {
136+
137+
if (source == null) {
138+
return null;
139+
}
140+
141+
if (source.getClass().isArray()) {
142+
int length = Array.getLength(source);
143+
Collection<Object> result = new ArrayList<>(length);
144+
145+
for (int i = 0; i < length; i++) {
146+
result.add(Array.get(source, i));
147+
}
148+
source = result;
149+
}
150+
151+
if (source instanceof Collection<?> c) {
152+
153+
Collection<@Nullable Object> result = new ArrayList<>(c.size());
154+
155+
for (Object o : c) {
156+
157+
if (o instanceof String s) {
158+
result.add(mapper.apply(s));
159+
} else {
160+
result.add(o != null ? mapper.apply(o.toString()) : null);
161+
}
162+
}
163+
164+
return result;
165+
}
166+
167+
return source;
168+
}
169+
114170
protected <T> @Nullable T convertOne(@Nullable Object result, boolean nativeQuery, Class<T> projection) {
115171

116172
if (result == null) {
@@ -121,6 +177,10 @@ protected String rewriteQuery(DeclaredQuery query, Sort sort, Class<?> returnedT
121177
return projection.cast(result);
122178
}
123179

180+
if (CONVERSION_SERVICE.canConvert(result.getClass(), projection)) {
181+
return CONVERSION_SERVICE.convert(result, projection);
182+
}
183+
124184
return projectionFactory.createProjection(projection,
125185
result instanceof Tuple t ? new TupleBackedMap(nativeQuery ? TupleBackedMap.underscoreAware(t) : t) : result);
126186
}

0 commit comments

Comments
 (0)