Browse Source

just some stuff to see if it works the way we expect it to do

labs/generated-repositories
Christoph Strobl 11 months ago
parent
commit
40f21833e3
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 169
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/generated/MongoBlocks.java
  2. 63
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/generated/MongoRepositoryContributor.java
  3. 13
      spring-data-mongodb/src/test/java/example/aot/UserRepository.java

169
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/generated/MongoBlocks.java

@ -0,0 +1,169 @@ @@ -0,0 +1,169 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.aot.generated;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.mongodb.BindableMongoExpression;
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.repository.Hint;
import org.springframework.data.mongodb.repository.ReadPreference;
import org.springframework.data.repository.aot.generate.AotRepositoryMethodBuilder.MethodGenerationMetadata;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.CodeBlock.Builder;
import org.springframework.javapoet.TypeName;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* @author Christoph Strobl
*/
public class MongoBlocks {
public static QueryBlockBuilder queryBlockBuilder(RepositoryInformation repositoryInformation,
MethodGenerationMetadata metadata) {
return new QueryBlockBuilder(repositoryInformation, metadata);
}
public static QueryExecutionBlockBuilder queryExecutionBlockBuilder(RepositoryInformation repositoryInformation,
MethodGenerationMetadata metadata) {
return new QueryExecutionBlockBuilder(repositoryInformation, metadata);
}
static class QueryExecutionBlockBuilder {
RepositoryInformation repositoryInformation;
MethodGenerationMetadata metadata;
public QueryExecutionBlockBuilder(RepositoryInformation repositoryInformation, MethodGenerationMetadata metadata) {
this.repositoryInformation = repositoryInformation;
this.metadata = metadata;
}
CodeBlock build(String queryVariableName) {
String mongoOpsRef = metadata.fieldNameOf(MongoOperations.class);
Builder builder = CodeBlock.builder();
boolean isProjecting = metadata.getActualReturnType() != null && !ObjectUtils
.nullSafeEquals(TypeName.get(repositoryInformation.getDomainType()), metadata.getActualReturnType());
Object actualReturnType = isProjecting ? metadata.getActualReturnType() : repositoryInformation.getDomainType();
if (isProjecting) {
builder.addStatement("$T<$T> finder = $L.query($T.class).as($T.class).matching($L)", TerminatingFind.class,
actualReturnType, mongoOpsRef, repositoryInformation.getDomainType(), actualReturnType, queryVariableName);
} else {
builder.addStatement("$T<$T> finder = $L.query($T.class).matching($L)", TerminatingFind.class, actualReturnType,
mongoOpsRef, repositoryInformation.getDomainType(), queryVariableName);
}
String terminatingMethod = "all()";
if (metadata.returnsSingleValue()) {
if (metadata.returnsOptionalValue()) {
terminatingMethod = "one()";
} else {
terminatingMethod = "oneValue()";
}
}
if (!metadata.returnsPage()) {
builder.addStatement("return finder.$L", terminatingMethod);
} else {
builder.addStatement("return $T.getPage(finder.$L, $L, () -> finder.count())", PageableExecutionUtils.class, terminatingMethod,
metadata.getPageableParameterName());
}
return builder.build();
}
}
static class QueryBlockBuilder {
MethodGenerationMetadata metadata;
String queryString;
List<String> arguments;
// MongoParameters argumentSource;
public QueryBlockBuilder(RepositoryInformation repositoryInformation, MethodGenerationMetadata metadata) {
this.metadata = metadata;
this.arguments = Arrays.stream(metadata.getRepositoryMethod().getParameters()).map(Parameter::getName)
.collect(Collectors.toList());
// ParametersSource parametersSource = ParametersSource.of(repositoryInformation, metadata.getRepositoryMethod());
// this.argumentSource = new MongoParameters(parametersSource, false);
}
public QueryBlockBuilder filter(String filter) {
this.queryString = filter;
return this;
}
CodeBlock build(String queryVariableName) {
String mongoOpsRef = metadata.fieldNameOf(MongoOperations.class);
CodeBlock.Builder builder = CodeBlock.builder();
builder.addStatement("$T filter = new $T($S, $L.getConverter(), new $T[]{ $L })", BindableMongoExpression.class,
BindableMongoExpression.class, queryString, mongoOpsRef, Object.class,
StringUtils.collectionToCommaDelimitedString(arguments));
builder.addStatement("$T $L = new $T(filter.toDocument())",
org.springframework.data.mongodb.core.query.Query.class, queryVariableName, BasicQuery.class);
String sortParameter = metadata.getSortParameterName();
if (StringUtils.hasText(sortParameter)) {
builder.addStatement("$L.with($L)", queryVariableName, sortParameter);
}
String limitParameter = metadata.getLimitParameterName();
if (StringUtils.hasText(limitParameter)) {
builder.addStatement("$L.limit($L)", queryVariableName, limitParameter);
}
String pageableParameter = metadata.getPageableParameterName();
if (StringUtils.hasText(pageableParameter)) {
builder.addStatement("$L.with($L)", queryVariableName, pageableParameter);
}
String hint = metadata.annotationValue(Hint.class, "value");
if (StringUtils.hasText(hint)) {
builder.addStatement("$L.withHint($S)", queryVariableName, hint);
}
String readPreference = metadata.annotationValue(ReadPreference.class, "value");
if (StringUtils.hasText(readPreference)) {
builder.addStatement("$L.withReadPreference($T.valueOf($S))", queryVariableName,
com.mongodb.ReadPreference.class, readPreference);
}
// TODO: all the meta stuff
return builder.build();
}
}
}

63
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/aot/generated/MongoRepositoryContributor.java

@ -15,13 +15,10 @@ @@ -15,13 +15,10 @@
*/
package org.springframework.data.mongodb.aot.generated;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.commons.logging.Log;
import org.bson.conversions.Bson;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.data.domain.Pageable;
@ -30,13 +27,11 @@ import org.springframework.data.domain.ScrollPosition; @@ -30,13 +27,11 @@ import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.BindableMongoExpression;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.MongoWriter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.CriteriaDefinition.Placeholder;
import org.springframework.data.mongodb.core.query.TextCriteria;
@ -49,6 +44,8 @@ import org.springframework.data.mongodb.util.BsonUtils; @@ -49,6 +44,8 @@ import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.data.repository.aot.generate.AotRepositoryConstructorBuilder;
import org.springframework.data.repository.aot.generate.AotRepositoryMethodBuilder;
import org.springframework.data.repository.aot.generate.AotRepositoryMethodBuilder.MethodGenerationMetadata;
import org.springframework.data.repository.aot.generate.CodeBlocks;
import org.springframework.data.repository.aot.generate.Contribution;
import org.springframework.data.repository.aot.generate.RepositoryContributor;
import org.springframework.data.repository.config.AotRepositoryContext;
import org.springframework.data.repository.core.RepositoryInformation;
@ -57,8 +54,6 @@ import org.springframework.data.util.TypeInformation; @@ -57,8 +54,6 @@ import org.springframework.data.util.TypeInformation;
import org.springframework.javapoet.MethodSpec.Builder;
import org.springframework.javapoet.TypeName;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.DBRef;
@ -78,16 +73,14 @@ public class MongoRepositoryContributor extends RepositoryContributor { @@ -78,16 +73,14 @@ public class MongoRepositoryContributor extends RepositoryContributor {
}
@Override
protected void customizeDerivedMethod(AotRepositoryMethodBuilder methodBuilder) {
protected Contribution customizeDerivedMethod(AotRepositoryMethodBuilder methodBuilder) {
methodBuilder.customize((repositoryInformation, metadata, body) -> {
Query query = AnnotatedElementUtils.findMergedAnnotation(metadata.getRepositoryMethod(), Query.class);
if (query != null) {
userAnnotatedQuery(repositoryInformation, metadata, body, query);
userAnnotatedQuery(repositoryInformation, metadata, methodBuilder.codeBlocks(), body, query);
} else {
;
MongoMappingContext mongoMappingContext = new MongoMappingContext();
mongoMappingContext.setSimpleTypeHolder(
MongoCustomConversions.create((cfg) -> cfg.useNativeDriverJavaTimeCodecs()).getSimpleTypeHolder());
@ -195,52 +188,22 @@ public class MongoRepositoryContributor extends RepositoryContributor { @@ -195,52 +188,22 @@ public class MongoRepositoryContributor extends RepositoryContributor {
org.springframework.data.mongodb.core.query.Query partTreeQuery = queryCreator.createQuery();
StringBuffer buffer = new StringBuffer();
BsonUtils.writeJson(partTreeQuery.getQueryObject()).to(buffer);
writeStringQuery(repositoryInformation, metadata, body, buffer.toString());
writeStringQuery(repositoryInformation, metadata, methodBuilder.codeBlocks(), body, buffer.toString());
}
});
return Contribution.CODE;
}
private static void writeStringQuery(RepositoryInformation repositoryInformation, MethodGenerationMetadata metadata,
Builder body, String query) {
String mongoOpsRef = metadata.fieldNameOf(MongoOperations.class);
String arguments = StringUtils.collectionToCommaDelimitedString(Arrays
.stream(metadata.getRepositoryMethod().getParameters()).map(Parameter::getName).collect(Collectors.toList()));
body.beginControlFlow("if($L.isDebugEnabled())", metadata.fieldNameOf(Log.class));
body.addStatement("$L.debug(\"invoking generated [$L] method\")", metadata.fieldNameOf(Log.class),
metadata.getRepositoryMethod().getName());
body.endControlFlow();
body.addStatement("$T filter = new $T($S, $L.getConverter(), new $T[]{ $L })", BindableMongoExpression.class,
BindableMongoExpression.class, query, mongoOpsRef, Object.class, arguments);
body.addStatement("$T query = new $T(filter.toDocument())", org.springframework.data.mongodb.core.query.Query.class,
BasicQuery.class);
boolean isCollectionType = TypeInformation.fromReturnTypeOf(metadata.getRepositoryMethod()).isCollectionLike();
String terminatingMethod = isCollectionType ? "all()" : "oneValue()";
if (metadata.getActualReturnType() != null && ObjectUtils
.nullSafeEquals(TypeName.get(repositoryInformation.getDomainType()), metadata.getActualReturnType())) {
body.addStatement("""
return $L.query($T.class)
.matching(query)
.$L""", mongoOpsRef, repositoryInformation.getDomainType(), terminatingMethod);
} else {
body.addStatement("""
return $L.query($T.class)
.as($T.class)
.matching(query)
.$L""", mongoOpsRef, repositoryInformation.getDomainType(),
metadata.getActualReturnType() != null ? metadata.getActualReturnType()
: repositoryInformation.getDomainType(),
terminatingMethod);
}
CodeBlocks codeBlocks, Builder body, String query) {
body.addCode(codeBlocks.logDebug("invoking [%s]".formatted(metadata.getRepositoryMethod().getName())));
body.addCode(MongoBlocks.queryBlockBuilder(repositoryInformation, metadata).filter(query).build("query"));
body.addCode(MongoBlocks.queryExecutionBlockBuilder(repositoryInformation, metadata).build("query"));
}
private static void userAnnotatedQuery(RepositoryInformation repositoryInformation, MethodGenerationMetadata metadata,
Builder body, Query query) {
writeStringQuery(repositoryInformation, metadata, body, query.value());
CodeBlocks codeBlocks, Builder body, Query query) {
writeStringQuery(repositoryInformation, metadata, codeBlocks, body, query.value());
}
}

13
spring-data-mongodb/src/test/java/example/aot/UserRepository.java

@ -17,7 +17,12 @@ package example.aot; @@ -17,7 +17,12 @@ package example.aot;
import java.util.List;
import org.springframework.data.domain.Limit;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.ReadPreference;
import org.springframework.data.repository.CrudRepository;
/**
@ -30,7 +35,15 @@ public interface UserRepository extends CrudRepository<User, String> { @@ -30,7 +35,15 @@ public interface UserRepository extends CrudRepository<User, String> {
@Query("{ 'username' : '?0' }")
List<User> findAllByAnnotatedQueryWithParameter(String username);
@ReadPreference("secondary")
User findByUsername(String username);
List<User> findUserByLastnameLike(String lastname);
List<User> findUserByLastnameStartingWith(String lastname, Pageable page);
List<User> findUserByLastnameStartingWith(String lastname, Sort sort);
List<User> findUserByLastnameStartingWith(String lastname, Limit limit);
List<User> findUserByLastnameStartingWith(String lastname, Sort sort, Limit limit);
Page<User> findUserByFirstnameStartingWith(String lastname, Pageable page);
}

Loading…
Cancel
Save