Skip to content

Commit c12bdb7

Browse files
committed
Simplify package structure.
See #3830
1 parent f76ff3e commit c12bdb7

18 files changed

+101
-78
lines changed
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024-2025 the original author or authors.
2+
* Copyright 2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.jpa.repository.aot.generated;
16+
package org.springframework.data.jpa.repository.aot;
1717

1818
import jakarta.persistence.EntityManager;
1919
import jakarta.persistence.EntityManagerFactory;
+6-4
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.jpa.repository.aot.generated;
16+
package org.springframework.data.jpa.repository.aot;
1717

18-
import jakarta.validation.constraints.Null;
1918

2019
import java.util.function.Function;
2120

21+
import org.jspecify.annotations.Nullable;
22+
2223
import org.springframework.data.jpa.repository.query.DeclaredQuery;
2324
import org.springframework.data.jpa.repository.query.QueryEnhancer;
2425
import org.springframework.data.jpa.repository.query.QueryEnhancerSelector;
@@ -35,15 +36,16 @@ record AotQueries(AotQuery result, AotQuery count) {
3536
/**
3637
* Derive a count query from the given query.
3738
*/
38-
public static AotQueries from(StringAotQuery query, @Null String countProjection, QueryEnhancerSelector selector) {
39+
public static AotQueries from(StringAotQuery query, @Nullable String countProjection,
40+
QueryEnhancerSelector selector) {
3941
return from(query, StringAotQuery::getQuery, countProjection, selector);
4042
}
4143

4244
/**
4345
* Derive a count query from the given query.
4446
*/
4547
public static <T extends AotQuery> AotQueries from(T query, Function<T, DeclaredQuery> queryMapper,
46-
@Null String countProjection, QueryEnhancerSelector selector) {
48+
@Nullable String countProjection, QueryEnhancerSelector selector) {
4749

4850
DeclaredQuery underlyingQuery = queryMapper.apply(query);
4951
QueryEnhancer queryEnhancer = selector.select(underlyingQuery).create(underlyingQuery);
+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.jpa.repository.aot.generated;
16+
package org.springframework.data.jpa.repository.aot;
1717

1818
import java.util.List;
1919

Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.jpa.repository.aot.generated;
16+
package org.springframework.data.jpa.repository.aot;
1717

1818
import java.lang.reflect.Method;
1919

@@ -32,6 +32,7 @@
3232
import org.springframework.data.repository.query.ParametersSource;
3333
import org.springframework.data.repository.query.ReturnedType;
3434
import org.springframework.data.repository.query.ValueExpressionDelegate;
35+
import org.springframework.data.util.Lazy;
3536
import org.springframework.util.ConcurrentLruCache;
3637

3738
/**
@@ -48,11 +49,11 @@ public class AotRepositoryFragmentSupport {
4849

4950
private final ProjectionFactory projectionFactory;
5051

51-
private final ConcurrentLruCache<DeclaredQuery, QueryEnhancer> enhancers;
52+
private final Lazy<ConcurrentLruCache<DeclaredQuery, QueryEnhancer>> enhancers;
5253

53-
private final ConcurrentLruCache<String, ValueExpression> expressions;
54+
private final Lazy<ConcurrentLruCache<String, ValueExpression>> expressions;
5455

55-
private final ConcurrentLruCache<Method, ValueEvaluationContextProvider> contextProviders;
56+
private final Lazy<ConcurrentLruCache<Method, ValueEvaluationContextProvider>> contextProviders;
5657

5758
protected AotRepositoryFragmentSupport(QueryEnhancerSelector selector,
5859
RepositoryFactoryBeanSupport.FragmentCreationContext context) {
@@ -66,10 +67,10 @@ protected AotRepositoryFragmentSupport(QueryEnhancerSelector selector, Repositor
6667
this.repositoryMetadata = repositoryMetadata;
6768
this.valueExpressions = valueExpressions;
6869
this.projectionFactory = projectionFactory;
69-
this.enhancers = new ConcurrentLruCache<>(32, query -> selector.select(query).create(query));
70-
this.expressions = new ConcurrentLruCache<>(32, valueExpressions::parse);
71-
this.contextProviders = new ConcurrentLruCache<>(32, it -> valueExpressions
72-
.createValueContextProvider(new JpaParameters(ParametersSource.of(repositoryMetadata, it))));
70+
this.enhancers = Lazy.of(() -> new ConcurrentLruCache<>(32, query -> selector.select(query).create(query)));
71+
this.expressions = Lazy.of(() -> new ConcurrentLruCache<>(32, valueExpressions::parse));
72+
this.contextProviders = Lazy.of(() -> new ConcurrentLruCache<>(32, it -> valueExpressions
73+
.createValueContextProvider(new JpaParameters(ParametersSource.of(repositoryMetadata, it)))));
7374
}
7475

7576
/**
@@ -82,7 +83,7 @@ protected AotRepositoryFragmentSupport(QueryEnhancerSelector selector, Repositor
8283
*/
8384
protected String rewriteQuery(DeclaredQuery query, Sort sort, Class<?> returnedType) {
8485

85-
QueryEnhancer queryStringEnhancer = this.enhancers.get(query);
86+
QueryEnhancer queryStringEnhancer = this.enhancers.get().get(query);
8687
return queryStringEnhancer.rewrite(new DefaultQueryRewriteInformation(sort,
8788
ReturnedType.of(returnedType, repositoryMetadata.getDomainType(), projectionFactory)));
8889
}
@@ -97,8 +98,8 @@ protected String rewriteQuery(DeclaredQuery query, Sort sort, Class<?> returnedT
9798
*/
9899
protected @Nullable Object evaluateExpression(Method method, String expressionString, Object... args) {
99100

100-
ValueExpression expression = this.expressions.get(expressionString);
101-
ValueEvaluationContextProvider contextProvider = this.contextProviders.get(method);
101+
ValueExpression expression = this.expressions.get().get(expressionString);
102+
ValueEvaluationContextProvider contextProvider = this.contextProviders.get().get(method);
102103

103104
return expression.evaluate(contextProvider.getEvaluationContext(args, expression.getExpressionDependencies()));
104105
}
+12-24
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.jpa.repository.aot.generated;
16+
package org.springframework.data.jpa.repository.aot;
1717

1818
import jakarta.persistence.EntityManager;
1919
import jakarta.persistence.Query;
@@ -55,15 +55,13 @@
5555
class JpaCodeBlocks {
5656

5757
/**
58-
* @param context
5958
* @return new {@link QueryBlockBuilder}.
6059
*/
6160
public static QueryBlockBuilder queryBuilder(AotQueryMethodGenerationContext context, JpaQueryMethod queryMethod) {
6261
return new QueryBlockBuilder(context, queryMethod);
6362
}
6463

6564
/**
66-
* @param context
6765
* @return new {@link QueryExecutionBlockBuilder}.
6866
*/
6967
static QueryExecutionBlockBuilder executionBuilder(AotQueryMethodGenerationContext context,
@@ -79,9 +77,8 @@ static class QueryBlockBuilder {
7977
private final AotQueryMethodGenerationContext context;
8078
private final JpaQueryMethod queryMethod;
8179
private String queryVariableName = "query";
82-
private AotQueries queries;
80+
private @Nullable AotQueries queries;
8381
private MergedAnnotation<QueryHints> queryHints = MergedAnnotation.missing();
84-
private MergedAnnotation<org.springframework.data.jpa.repository.Query> query = MergedAnnotation.missing();
8582
private @Nullable String sqlResultSetMapping;
8683
private @Nullable Class<?> queryReturnType;
8784

@@ -101,18 +98,6 @@ public QueryBlockBuilder filter(AotQueries query) {
10198
return this;
10299
}
103100

104-
public QueryBlockBuilder queryHints(MergedAnnotation<QueryHints> queryHints) {
105-
106-
this.queryHints = queryHints;
107-
return this;
108-
}
109-
110-
public QueryBlockBuilder query(MergedAnnotation<org.springframework.data.jpa.repository.Query> query) {
111-
112-
this.query = query;
113-
return this;
114-
}
115-
116101
public QueryBlockBuilder nativeQuery(MergedAnnotation<NativeQuery> nativeQuery) {
117102

118103
if (nativeQuery.isPresent()) {
@@ -121,6 +106,12 @@ public QueryBlockBuilder nativeQuery(MergedAnnotation<NativeQuery> nativeQuery)
121106
return this;
122107
}
123108

109+
public QueryBlockBuilder queryHints(MergedAnnotation<QueryHints> queryHints) {
110+
111+
this.queryHints = queryHints;
112+
return this;
113+
}
114+
124115
public QueryBlockBuilder queryReturnType(@Nullable Class<?> queryReturnType) {
125116
this.queryReturnType = queryReturnType;
126117
return this;
@@ -142,7 +133,7 @@ public CodeBlock build() {
142133

143134
String queryStringNameVariableName = null;
144135

145-
if (queries.result() instanceof StringAotQuery sq) {
136+
if (queries != null && queries.result() instanceof StringAotQuery sq) {
146137

147138
queryStringNameVariableName = "%sString".formatted(queryVariableName);
148139
builder.addStatement("$T $L = $S", String.class, queryStringNameVariableName, sq.getQueryString());
@@ -157,9 +148,6 @@ public CodeBlock build() {
157148
builder.addStatement("$T $L = $S", String.class, countQueryStringNameVariableName, sq.getQueryString());
158149
}
159150

160-
// sorting
161-
// TODO: refactor into sort builder
162-
163151
String sortParameterName = context.getSortParameterName();
164152
if (sortParameterName == null && context.getPageableParameterName() != null) {
165153
sortParameterName = "%s.getSort()".formatted(context.getPageableParameterName());
@@ -202,7 +190,7 @@ private CodeBlock applySorting(String sort, String queryString, Class<?> actualR
202190
builder.beginControlFlow("if ($L.isSorted())", sort);
203191

204192
builder.addStatement("$T declaredQuery = $T.$L($L)", DeclaredQuery.class, DeclaredQuery.class,
205-
queries.isNative() ? "nativeQuery" : "jpqlQuery",
193+
queries != null && queries.isNative() ? "nativeQuery" : "jpqlQuery",
206194
queryString);
207195

208196
builder.addStatement("$L = rewriteQuery(declaredQuery, $L, $T.class)", queryString, sort, actualReturnType);
@@ -227,7 +215,7 @@ private CodeBlock applyLimits(boolean exists) {
227215
builder.beginControlFlow("if ($L.isLimited())", limit);
228216
builder.addStatement("$L.setMaxResults($L.max())", queryVariableName, limit);
229217
builder.endControlFlow();
230-
} else if (queries.result().isLimited()) {
218+
} else if (queries != null && queries.result().isLimited()) {
231219
builder.addStatement("$L.setMaxResults($L)", queryVariableName, queries.result().getLimit().max());
232220
}
233221

@@ -358,7 +346,7 @@ private Object getParameter(ParameterBinding.ParameterOrigin origin) {
358346

359347
Builder builder = CodeBlock.builder();
360348
ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
361-
String[] parameterNames = discoverer.getParameterNames(context.getMethod());
349+
var parameterNames = discoverer.getParameterNames(context.getMethod());
362350

363351
String expressionString = expr.expression().getExpressionString();
364352
// re-wrap expression
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024-2025 the original author or authors.
2+
* Copyright 2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,12 +13,13 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.jpa.repository.aot.generated;
16+
package org.springframework.data.jpa.repository.aot;
1717

1818
import jakarta.persistence.EntityManager;
1919
import jakarta.persistence.EntityManagerFactory;
2020
import jakarta.persistence.Tuple;
2121
import jakarta.persistence.TypedQueryReference;
22+
import jakarta.persistence.metamodel.Metamodel;
2223

2324
import java.lang.reflect.Method;
2425
import java.util.Arrays;
@@ -57,6 +58,7 @@
5758
import org.springframework.data.repository.query.QueryMethod;
5859
import org.springframework.data.repository.query.ReturnedType;
5960
import org.springframework.data.repository.query.parser.PartTree;
61+
import org.springframework.data.util.TypeInformation;
6062
import org.springframework.javapoet.CodeBlock;
6163
import org.springframework.javapoet.TypeName;
6264
import org.springframework.javapoet.TypeSpec;
@@ -76,13 +78,23 @@
7678
*/
7779
public class JpaRepositoryContributor extends RepositoryContributor {
7880

79-
private final AotMetamodel metaModel;
81+
private final EntityManagerFactory emf;
82+
private final Metamodel metaModel;
8083
private final PersistenceProvider persistenceProvider;
8184

8285
public JpaRepositoryContributor(AotRepositoryContext repositoryContext) {
8386
super(repositoryContext);
84-
this.metaModel = new AotMetamodel(repositoryContext.getResolvedTypes());
85-
this.persistenceProvider = PersistenceProvider.fromEntityManagerFactory(metaModel.getEntityManagerFactory());
87+
AotMetamodel amm = new AotMetamodel(repositoryContext.getResolvedTypes());
88+
this.metaModel = amm;
89+
this.emf = amm.getEntityManagerFactory();
90+
this.persistenceProvider = PersistenceProvider.fromEntityManagerFactory(amm.getEntityManagerFactory());
91+
}
92+
93+
public JpaRepositoryContributor(AotRepositoryContext repositoryContext, EntityManagerFactory entityManagerFactory) {
94+
super(repositoryContext);
95+
this.emf = entityManagerFactory;
96+
this.metaModel = entityManagerFactory.getMetamodel();
97+
this.persistenceProvider = PersistenceProvider.fromEntityManagerFactory(entityManagerFactory);
8698
}
8799

88100
@Override
@@ -118,16 +130,31 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
118130
return null;
119131
}
120132

133+
ReturnedType returnedType = queryMethod.getResultProcessor().getReturnedType();
134+
135+
// no interface/dynamic projections for now.
136+
if (returnedType.isProjecting() && returnedType.getReturnedType().isInterface()) {
137+
return null;
138+
}
139+
140+
if (queryMethod.getParameters().hasDynamicProjection()) {
141+
return null;
142+
}
143+
121144
// no KeysetScrolling for now.
122145
if (queryMethod.getParameters().hasScrollPositionParameter()) {
123146
return null;
124147
}
125148

126149
if (queryMethod.isModifyingQuery()) {
127150

128-
Class<?> returnType = repositoryInformation.getReturnType(method).getType();
129-
if (!ClassUtils.isVoidType(returnType)
130-
&& !JpaCodeBlocks.QueryExecutionBlockBuilder.returnsModifying(returnType)) {
151+
TypeInformation<?> returnType = repositoryInformation.getReturnType(method);
152+
153+
boolean returnsCount = JpaCodeBlocks.QueryExecutionBlockBuilder.returnsModifying(returnType.getType());
154+
155+
boolean isVoid = ClassUtils.isVoidType(returnType.getType());
156+
157+
if (!returnsCount && !isVoid) {
131158
return null;
132159
}
133160
}
@@ -140,15 +167,14 @@ protected void customizeConstructor(AotRepositoryConstructorBuilder constructorB
140167
MergedAnnotation<NativeQuery> nativeQuery = context.getAnnotation(NativeQuery.class);
141168
MergedAnnotation<QueryHints> queryHints = context.getAnnotation(QueryHints.class);
142169
MergedAnnotation<Modifying> modifying = context.getAnnotation(Modifying.class);
143-
ReturnedType returnedType = context.getReturnedType();
144170

145171
body.add(context.codeBlocks().logDebug("invoking [%s]".formatted(context.getMethod().getName())));
146172

147173
AotQueries aotQueries = getQueries(context, query, selector, queryMethod, returnedType);
148174

149175
body.add(JpaCodeBlocks.queryBuilder(context, queryMethod).filter(aotQueries)
150-
.queryReturnType(getQueryReturnType(aotQueries.result(), returnedType, context)).query(query)
151-
.nativeQuery(nativeQuery).queryHints(queryHints).build());
176+
.queryReturnType(getQueryReturnType(aotQueries.result(), returnedType, context)).nativeQuery(nativeQuery)
177+
.queryHints(queryHints).build());
152178

153179
body.add(
154180
JpaCodeBlocks.executionBuilder(context, queryMethod).modifying(modifying).query(aotQueries.result()).build());
@@ -178,8 +204,7 @@ private AotQueries buildStringQuery(Class<?> domainType, ReturnedType returnedTy
178204

179205
UnaryOperator<String> operator = s -> s.replaceAll("#\\{#entityName}", domainType.getName());
180206
boolean isNative = query.getBoolean("nativeQuery");
181-
Function<String, StringAotQuery> queryFunction = isNative ? StringAotQuery::nativeQuery
182-
: StringAotQuery::jpqlQuery;
207+
Function<String, StringAotQuery> queryFunction = isNative ? StringAotQuery::nativeQuery : StringAotQuery::jpqlQuery;
183208
queryFunction = operator.andThen(queryFunction);
184209

185210
String queryString = query.getString("value");
@@ -252,8 +277,6 @@ private NamedAotQuery buildNamedAotQuery(TypedQueryReference<?> namedQuery, JpaQ
252277
returnedType.getReturnedType(), returnedType.getTypeToRead(), void.class, null, Long.class, Integer.class,
253278
Long.TYPE, Integer.TYPE, Number.class);
254279

255-
EntityManagerFactory emf = metaModel.getEntityManagerFactory();
256-
257280
for (Class<?> candidate : candidates) {
258281

259282
Map<String, ? extends TypedQueryReference<?>> namedQueries = emf.getNamedQueries(candidate);
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.jpa.repository.aot.generated;
16+
package org.springframework.data.jpa.repository.aot;
1717

1818
import java.util.List;
1919

Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package org.springframework.data.jpa.repository.aot.generated;
16+
package org.springframework.data.jpa.repository.aot;
1717

1818
import java.util.List;
1919

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* Ahead-of-Time (AOT) generation for Spring Data JPA repositories.
3+
*/
4+
@org.jspecify.annotations.NullMarked
5+
package org.springframework.data.jpa.repository.aot;

0 commit comments

Comments
 (0)