@@ -18,10 +18,13 @@ package org.springframework.data.mongodb.aot.generated;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.data.mongodb.BindableMongoExpression;
|
||||
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
|
||||
import org.springframework.data.mongodb.core.ExecutableRemoveOperation.ExecutableRemove;
|
||||
@@ -33,7 +36,8 @@ import org.springframework.data.mongodb.repository.query.MongoQueryExecution.Del
|
||||
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.DeleteExecutionX.Type;
|
||||
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagedExecution;
|
||||
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.SlicedExecution;
|
||||
import org.springframework.data.repository.aot.generate.AotRepositoryMethodGenerationContext;
|
||||
import org.springframework.data.mongodb.repository.query.MongoQueryMethod;
|
||||
import org.springframework.data.repository.aot.generate.AotQueryMethodGenerationContext;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.CodeBlock.Builder;
|
||||
@@ -50,25 +54,29 @@ public class MongoBlocks {
|
||||
|
||||
private static final Pattern PARAMETER_BINDING_PATTERN = Pattern.compile("\\?(\\d+)");
|
||||
|
||||
static QueryBlockBuilder queryBlockBuilder(AotRepositoryMethodGenerationContext context) {
|
||||
return new QueryBlockBuilder(context);
|
||||
static QueryBlockBuilder queryBlockBuilder(AotQueryMethodGenerationContext context, MongoQueryMethod queryMethod) {
|
||||
return new QueryBlockBuilder(context, queryMethod);
|
||||
}
|
||||
|
||||
static QueryExecutionBlockBuilder queryExecutionBlockBuilder(AotRepositoryMethodGenerationContext context) {
|
||||
return new QueryExecutionBlockBuilder(context);
|
||||
static QueryExecutionBlockBuilder queryExecutionBlockBuilder(AotQueryMethodGenerationContext context,
|
||||
MongoQueryMethod queryMethod) {
|
||||
return new QueryExecutionBlockBuilder(context, queryMethod);
|
||||
}
|
||||
|
||||
static DeleteExecutionBuilder deleteExecutionBlockBuilder(AotRepositoryMethodGenerationContext context) {
|
||||
return new DeleteExecutionBuilder(context);
|
||||
static DeleteExecutionBuilder deleteExecutionBlockBuilder(AotQueryMethodGenerationContext context,
|
||||
MongoQueryMethod queryMethod) {
|
||||
return new DeleteExecutionBuilder(context, queryMethod);
|
||||
}
|
||||
|
||||
static class DeleteExecutionBuilder {
|
||||
|
||||
AotRepositoryMethodGenerationContext context;
|
||||
private final AotQueryMethodGenerationContext context;
|
||||
private final MongoQueryMethod queryMethod;
|
||||
String queryVariableName;
|
||||
|
||||
public DeleteExecutionBuilder(AotRepositoryMethodGenerationContext context) {
|
||||
public DeleteExecutionBuilder(AotQueryMethodGenerationContext context, MongoQueryMethod queryMethod) {
|
||||
this.context = context;
|
||||
this.queryMethod = queryMethod;
|
||||
}
|
||||
|
||||
public DeleteExecutionBuilder referencing(String queryVariableName) {
|
||||
@@ -85,15 +93,16 @@ public class MongoBlocks {
|
||||
&& !ObjectUtils.nullSafeEquals(TypeName.get(context.getRepositoryInformation().getDomainType()),
|
||||
context.getActualReturnType());
|
||||
|
||||
Object actualReturnType = isProjecting ? context.getActualReturnType()
|
||||
Object actualReturnType = isProjecting ? context.getActualReturnType().getType()
|
||||
: context.getRepositoryInformation().getDomainType();
|
||||
|
||||
builder.add("\n");
|
||||
builder.addStatement("$T<$T> remover = $L.remove($T.class)", ExecutableRemove.class, actualReturnType,
|
||||
builder.addStatement("$T<$T> remover = $L.remove($T.class)", ExecutableRemove.class,
|
||||
context.getRepositoryInformation().getDomainType(),
|
||||
mongoOpsRef, context.getRepositoryInformation().getDomainType());
|
||||
|
||||
Type type = Type.FIND_AND_REMOVE_ALL;
|
||||
if (context.returnsSingleValue()) {
|
||||
if (!queryMethod.isCollectionQuery()) {
|
||||
if (!ClassUtils.isPrimitiveOrWrapper(context.getMethod().getReturnType())) {
|
||||
type = Type.FIND_AND_REMOVE_ONE;
|
||||
} else {
|
||||
@@ -103,7 +112,7 @@ public class MongoBlocks {
|
||||
|
||||
actualReturnType = ClassUtils.isPrimitiveOrWrapper(context.getMethod().getReturnType())
|
||||
? ClassName.get(context.getMethod().getReturnType())
|
||||
: context.returnsSingleValue() ? actualReturnType : context.getReturnType();
|
||||
: queryMethod.isCollectionQuery() ? context.getReturnTypeName() : actualReturnType;
|
||||
|
||||
builder.addStatement("return ($T) new $T(remover, $T.$L).execute($L)", actualReturnType, DeleteExecutionX.class,
|
||||
DeleteExecutionX.Type.class, type.name(), queryVariableName);
|
||||
@@ -114,11 +123,14 @@ public class MongoBlocks {
|
||||
|
||||
static class QueryExecutionBlockBuilder {
|
||||
|
||||
AotRepositoryMethodGenerationContext context;
|
||||
private final AotQueryMethodGenerationContext context;
|
||||
private final MongoQueryMethod queryMethod;
|
||||
private String queryVariableName;
|
||||
private boolean count, exists;
|
||||
|
||||
public QueryExecutionBlockBuilder(AotRepositoryMethodGenerationContext context) {
|
||||
public QueryExecutionBlockBuilder(AotQueryMethodGenerationContext context, MongoQueryMethod queryMethod) {
|
||||
this.context = context;
|
||||
this.queryMethod = queryMethod;
|
||||
}
|
||||
|
||||
QueryExecutionBlockBuilder referencing(String queryVariableName) {
|
||||
@@ -127,16 +139,24 @@ public class MongoBlocks {
|
||||
return this;
|
||||
}
|
||||
|
||||
QueryExecutionBlockBuilder count(boolean count) {
|
||||
this.count = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
QueryExecutionBlockBuilder exists(boolean exists) {
|
||||
this.exists = exists;
|
||||
return this;
|
||||
}
|
||||
|
||||
CodeBlock build() {
|
||||
|
||||
String mongoOpsRef = context.fieldNameOf(MongoOperations.class);
|
||||
|
||||
Builder builder = CodeBlock.builder();
|
||||
|
||||
boolean isProjecting = context.getActualReturnType() != null
|
||||
&& !ObjectUtils.nullSafeEquals(TypeName.get(context.getRepositoryInformation().getDomainType()),
|
||||
context.getActualReturnType());
|
||||
Object actualReturnType = isProjecting ? context.getActualReturnType()
|
||||
boolean isProjecting = context.getReturnedType().isProjecting();
|
||||
Object actualReturnType = isProjecting ? context.getActualReturnType().getType()
|
||||
: context.getRepositoryInformation().getDomainType();
|
||||
|
||||
builder.add("\n");
|
||||
@@ -150,24 +170,23 @@ public class MongoBlocks {
|
||||
context.getRepositoryInformation().getDomainType());
|
||||
}
|
||||
|
||||
String terminatingMethod = "all()";
|
||||
if (context.returnsSingleValue()) {
|
||||
String terminatingMethod;
|
||||
|
||||
if (context.returnsOptionalValue()) {
|
||||
terminatingMethod = "one()";
|
||||
} else if (context.isCountMethod()) {
|
||||
terminatingMethod = "count()";
|
||||
} else if (context.isExistsMethod()) {
|
||||
terminatingMethod = "exists()";
|
||||
} else {
|
||||
terminatingMethod = "oneValue()";
|
||||
}
|
||||
if (queryMethod.isCollectionQuery() || queryMethod.isPageQuery() || queryMethod.isSliceQuery()) {
|
||||
terminatingMethod = "all()";
|
||||
} else if (count) {
|
||||
terminatingMethod = "count()";
|
||||
|
||||
} else if (exists) {
|
||||
terminatingMethod = "exists()";
|
||||
} else {
|
||||
terminatingMethod = Optional.class.isAssignableFrom(context.getReturnType().toClass()) ? "one()" : "oneValue()";
|
||||
}
|
||||
|
||||
if (context.returnsPage()) {
|
||||
if (queryMethod.isPageQuery()) {
|
||||
builder.addStatement("return new $T(finder, $L).execute($L)", PagedExecution.class,
|
||||
context.getPageableParameterName(), queryVariableName);
|
||||
} else if (context.returnsSlice()) {
|
||||
} else if (queryMethod.isSliceQuery()) {
|
||||
builder.addStatement("return new $T(finder, $L).execute($L)", SlicedExecution.class,
|
||||
context.getPageableParameterName(), queryVariableName);
|
||||
} else {
|
||||
@@ -181,12 +200,14 @@ public class MongoBlocks {
|
||||
|
||||
static class QueryBlockBuilder {
|
||||
|
||||
AotRepositoryMethodGenerationContext context;
|
||||
private final AotQueryMethodGenerationContext context;
|
||||
private final MongoQueryMethod queryMethod;
|
||||
|
||||
StringQuery source;
|
||||
List<String> arguments;
|
||||
private String queryVariableName;
|
||||
|
||||
public QueryBlockBuilder(AotRepositoryMethodGenerationContext context) {
|
||||
public QueryBlockBuilder(AotQueryMethodGenerationContext context, MongoQueryMethod queryMethod) {
|
||||
this.context = context;
|
||||
this.arguments = Arrays.stream(context.getMethod().getParameters()).map(Parameter::getName)
|
||||
.collect(Collectors.toList());
|
||||
@@ -194,6 +215,7 @@ public class MongoBlocks {
|
||||
// ParametersSource parametersSource = ParametersSource.of(repositoryInformation, metadata.getRepositoryMethod());
|
||||
// this.argumentSource = new MongoParameters(parametersSource, false);
|
||||
|
||||
this.queryMethod = queryMethod;
|
||||
}
|
||||
|
||||
public QueryBlockBuilder filter(StringQuery query) {
|
||||
@@ -239,17 +261,20 @@ public class MongoBlocks {
|
||||
}
|
||||
|
||||
String pageableParameter = context.getPageableParameterName();
|
||||
if (StringUtils.hasText(pageableParameter) && !context.returnsPage() && !context.returnsSlice()) {
|
||||
if (StringUtils.hasText(pageableParameter) && !queryMethod.isPageQuery() && !queryMethod.isSliceQuery()) {
|
||||
builder.addStatement("$L.with($L)", queryVariableName, pageableParameter);
|
||||
}
|
||||
|
||||
String hint = context.annotationValue(Hint.class, "value");
|
||||
MergedAnnotation<Hint> hintAnnotation = context.getAnnotation(Hint.class);
|
||||
String hint = hintAnnotation.isPresent() ? hintAnnotation.getString("value") : null;
|
||||
|
||||
if (StringUtils.hasText(hint)) {
|
||||
builder.addStatement("$L.withHint($S)", queryVariableName, hint);
|
||||
}
|
||||
|
||||
String readPreference = context.annotationValue(ReadPreference.class, "value");
|
||||
MergedAnnotation<ReadPreference> readPreferenceAnnotation = context.getAnnotation(ReadPreference.class);
|
||||
String readPreference = readPreferenceAnnotation.isPresent() ? readPreferenceAnnotation.getString("value") : null;
|
||||
|
||||
if (StringUtils.hasText(readPreference)) {
|
||||
builder.addStatement("$L.withReadPreference($T.valueOf($S))", queryVariableName,
|
||||
com.mongodb.ReadPreference.class, readPreference);
|
||||
|
||||
@@ -15,20 +15,27 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.aot.generated;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.data.mongodb.aot.generated.MongoBlocks.QueryBlockBuilder;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
import org.springframework.data.mongodb.repository.Aggregation;
|
||||
import org.springframework.data.mongodb.repository.Query;
|
||||
import org.springframework.data.mongodb.repository.query.MongoQueryMethod;
|
||||
import org.springframework.data.repository.aot.generate.AotQueryMethodGenerationContext;
|
||||
import org.springframework.data.repository.aot.generate.AotRepositoryConstructorBuilder;
|
||||
import org.springframework.data.repository.aot.generate.AotRepositoryMethodBuilder;
|
||||
import org.springframework.data.repository.aot.generate.AotRepositoryMethodGenerationContext;
|
||||
import org.springframework.data.repository.aot.generate.MethodContributor;
|
||||
import org.springframework.data.repository.aot.generate.RepositoryContributor;
|
||||
import org.springframework.data.repository.config.AotRepositoryContext;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.javapoet.MethodSpec.Builder;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.TypeName;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -38,11 +45,13 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class MongoRepositoryContributor extends RepositoryContributor {
|
||||
|
||||
private AotQueryCreator queryCreator;
|
||||
private final AotQueryCreator queryCreator;
|
||||
private final MongoMappingContext mappingContext;
|
||||
|
||||
public MongoRepositoryContributor(AotRepositoryContext repositoryContext) {
|
||||
super(repositoryContext);
|
||||
this.queryCreator = new AotQueryCreator();
|
||||
this.mappingContext = new MongoMappingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,36 +60,43 @@ public class MongoRepositoryContributor extends RepositoryContributor {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AotRepositoryMethodBuilder contributeRepositoryMethod(
|
||||
AotRepositoryMethodGenerationContext generationContext) {
|
||||
protected @Nullable MethodContributor<? extends QueryMethod> contributeQueryMethod(Method method,
|
||||
RepositoryInformation repositoryInformation) {
|
||||
|
||||
// TODO: do not generate stuff for spel expressions
|
||||
|
||||
if (AnnotatedElementUtils.hasAnnotation(generationContext.getMethod(), Aggregation.class)) {
|
||||
if (AnnotatedElementUtils.hasAnnotation(method, Aggregation.class)) {
|
||||
return null;
|
||||
}
|
||||
{
|
||||
Query queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(generationContext.getMethod(), Query.class);
|
||||
if (queryAnnotation != null) {
|
||||
if (StringUtils.hasText(queryAnnotation.value())
|
||||
&& Pattern.compile("[\\?:][#$]\\{.*\\}").matcher(queryAnnotation.value()).find()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Query queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
|
||||
if (queryAnnotation != null) {
|
||||
if (StringUtils.hasText(queryAnnotation.value())
|
||||
&& Pattern.compile("[\\?:][#$]\\{.*\\}").matcher(queryAnnotation.value()).find()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// so the rest should work
|
||||
return new AotRepositoryMethodBuilder(generationContext).customize((context, body) -> {
|
||||
MongoQueryMethod queryMethod = new MongoQueryMethod(method, repositoryInformation, getProjectionFactory(),
|
||||
mappingContext);
|
||||
|
||||
Query queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(context.getMethod(), Query.class);
|
||||
return MethodContributor.forQueryMethod(queryMethod).contribute(context -> {
|
||||
CodeBlock.Builder builder = CodeBlock.builder();
|
||||
|
||||
boolean count, delete, exists;
|
||||
StringQuery query;
|
||||
if (queryAnnotation != null && StringUtils.hasText(queryAnnotation.value())) {
|
||||
query = new StringQuery(queryAnnotation.value());
|
||||
count = queryAnnotation.count();
|
||||
delete = queryAnnotation.delete();
|
||||
exists = queryAnnotation.exists();
|
||||
|
||||
} else {
|
||||
PartTree partTree = new PartTree(context.getMethod().getName(),
|
||||
context.getRepositoryInformation().getDomainType());
|
||||
query = queryCreator.createQuery(partTree, context.getMethod().getParameterCount());
|
||||
count = partTree.isCountProjection();
|
||||
delete = partTree.isDelete();
|
||||
exists = partTree.isExistsProjection();
|
||||
|
||||
}
|
||||
|
||||
if (queryAnnotation != null && StringUtils.hasText(queryAnnotation.sort())) {
|
||||
@@ -90,29 +106,31 @@ public class MongoRepositoryContributor extends RepositoryContributor {
|
||||
query.fields(queryAnnotation.fields());
|
||||
}
|
||||
|
||||
writeStringQuery(context, body, query);
|
||||
writeStringQuery(context, builder, count, delete, exists, query, queryMethod);
|
||||
|
||||
return builder.build();
|
||||
});
|
||||
}
|
||||
|
||||
private static void writeStringQuery(AotRepositoryMethodGenerationContext context, Builder body, StringQuery query) {
|
||||
private static void writeStringQuery(AotQueryMethodGenerationContext context, CodeBlock.Builder body, boolean count,
|
||||
boolean delete, boolean exists, StringQuery query, MongoQueryMethod queryMethod) {
|
||||
|
||||
body.addCode(context.codeBlocks().logDebug("invoking [%s]".formatted(context.getMethod().getName())));
|
||||
QueryBlockBuilder queryBlockBuilder = MongoBlocks.queryBlockBuilder(context).filter(query);
|
||||
body.add(context.codeBlocks().logDebug("invoking [%s]".formatted(context.getMethod().getName())));
|
||||
QueryBlockBuilder queryBlockBuilder = MongoBlocks.queryBlockBuilder(context, queryMethod).filter(query);
|
||||
|
||||
if (context.isDeleteMethod()) {
|
||||
if (delete) {
|
||||
|
||||
String deleteQueryVariableName = "deleteQuery";
|
||||
body.addCode(queryBlockBuilder.usingQueryVariableName(deleteQueryVariableName).build());
|
||||
body.addCode(MongoBlocks.deleteExecutionBlockBuilder(context).referencing(deleteQueryVariableName).build());
|
||||
body.add(queryBlockBuilder.usingQueryVariableName(deleteQueryVariableName).build());
|
||||
body.add(
|
||||
MongoBlocks.deleteExecutionBlockBuilder(context, queryMethod).referencing(deleteQueryVariableName).build());
|
||||
} else {
|
||||
|
||||
String filterQueryVariableName = "filterQuery";
|
||||
body.addCode(queryBlockBuilder.usingQueryVariableName(filterQueryVariableName).build());
|
||||
body.addCode(MongoBlocks.queryExecutionBlockBuilder(context).referencing(filterQueryVariableName).build());
|
||||
body.add(queryBlockBuilder.usingQueryVariableName(filterQueryVariableName).build());
|
||||
body.add(MongoBlocks.queryExecutionBlockBuilder(context, queryMethod).exists(exists).count(count)
|
||||
.referencing(filterQueryVariableName).build());
|
||||
}
|
||||
}
|
||||
|
||||
private static void userAnnotatedQuery(AotRepositoryMethodGenerationContext context, Builder body, Query query) {
|
||||
writeStringQuery(context, body, new StringQuery(query.value()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
@@ -2223,6 +2224,11 @@ public class MappingMongoConverter extends AbstractMongoConverter
|
||||
public TypeDescriptor toTypeDescriptor() {
|
||||
return delegate.toTypeDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolvableType toResolvableType() {
|
||||
return delegate.toResolvableType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user