Polishing.
Refactor ContextualValueExpressionEvaluator into Function, refactor BindingContext into nested class. Simplify tests. Rename tests to reflect what they are actually doing. Reformat code. See #453 Original pull request: #505
This commit is contained in:
@@ -13,10 +13,12 @@
|
||||
** xref:repositories/namespace-reference.adoc[]
|
||||
** xref:repositories/query-keywords-reference.adoc[]
|
||||
** xref:repositories/query-return-types-reference.adoc[]
|
||||
|
||||
* xref:ldap.adoc[]
|
||||
** xref:ldap/configuration.adoc[]
|
||||
** xref:ldap/usage.adoc[]
|
||||
** xref:ldap/query-methods.adoc[]
|
||||
** xref:ldap/value-expressions.adoc[]
|
||||
** xref:ldap/querydsl.adoc[]
|
||||
** xref:ldap/cdi-integration.adoc[]
|
||||
|
||||
|
||||
@@ -76,3 +76,91 @@ The following table provides samples of the keywords that you can use with query
|
||||
| `(!(Firstname=name))`
|
||||
|
||||
|===
|
||||
|
||||
[[ldap.query-methods.at-query]]
|
||||
== Using `@Query`
|
||||
|
||||
If you need to use a custom query that can't be derived from the method name, you can use the `@Query` annotation to define the query.
|
||||
As queries are tied to the Java method that runs them, you can actually bind parameters to be passed to the query.
|
||||
|
||||
The following example shows a query created with the `@Query` annotation:
|
||||
|
||||
.Declare query at the query method using `@Query`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
interface PersonRepository extends LdapRepository<Person, Long> {
|
||||
|
||||
@Query("(&(employmentType=*)(!(employmentType=Hired))(mail=:emailAddress))")
|
||||
Person findEmployeeByEmailAddress(String emailAddress);
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Spring Data supports named (parameter names prefixed with `:`) and positional parameter binding (in the form of zero-based `?0`).
|
||||
We recommend using named parameters for easier readability.
|
||||
Also, using positional parameters makes query methods a little error-prone when refactoring regarding the parameter position.
|
||||
|
||||
[[ldap.encoding]]
|
||||
== Parameter Encoding
|
||||
|
||||
Query parameters of String-based queries are encoded according to https://datatracker.ietf.org/doc/html/rfc2254[RFC2254].
|
||||
This can lead to undesired escaping of certain characters.
|
||||
You can specify your own encoder through the `@LdapEncode` annotation that defines which javadoc:org.springframework.data.ldap.repository.LdapEncoder[] to use.
|
||||
|
||||
`@LdapEncode` applies to individual parameters of a query method.
|
||||
It is not applies for derived queries or Value Expressions (SpEL, Property Placeholders).
|
||||
|
||||
.Declare a custom `LdapEncoder` for a query method
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
interface PersonRepository extends LdapRepository<Person, Long> {
|
||||
|
||||
@Query("(&(employmentType=*)(!(employmentType=Hired))(firstName=:firstName))")
|
||||
Person findEmployeeByFirstNameLike(@LdapEncode(MyLikeEncoder.class) String firstName);
|
||||
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[ldap.query.spel-expressions]]
|
||||
== Using SpEL Expressions
|
||||
|
||||
Spring Data allows you to use SpEL expressions in your query methods.
|
||||
SpEL expressions are part of Spring Data's xref:ldap/value-expressions.adoc[Value Expressions] support.
|
||||
SpEL expressions can be used to manipulate query method arguments as well as to invoke bean methods.
|
||||
Method arguments can be accessed by name or index as demonstrated in the following example.
|
||||
|
||||
.Using SpEL expressions in Repository Query Methods
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Query("(&(firstName=?#{[0]})(mail=:?#{principal.emailAddress}))")
|
||||
List<Person> findByFirstnameAndCurrentUserWithCustomQuery(String firstname);
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Values provided by SpEL expressions are not escaped according to RFC2254.
|
||||
You have to ensure that the values are properly escaped if needed.
|
||||
Consider using Spring Ldap's `org.springframework.ldap.support.LdapEncoder` helper class.
|
||||
|
||||
[[ldap.query.property-placeholders]]
|
||||
== Using Property Placeholders
|
||||
|
||||
Property Placeholders (see xref:ldap/value-expressions.adoc[Value Expressions]) can help to easily customize your queries based on configuration properties from Spring's `Environment`.
|
||||
These are useful for queries that need to be customized based on the environment or configuration.
|
||||
|
||||
.Using Property Placeholders in Repository Query Methods
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Query("(&(firstName=?0)(stage=:?${myapp.stage:dev}))")
|
||||
List<Person> findByFirstnameAndStageWithCustomQuery(String firstname);
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Values provided by Property Placeholders are not escaped according to RFC2254.
|
||||
You have to ensure that the values are properly escaped if needed.
|
||||
Consider using Spring Ldap's `org.springframework.ldap.support.LdapEncoder` helper class.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
include::{commons}@data-commons::page$value-expressions.adoc[]
|
||||
@@ -26,7 +26,6 @@ import org.springframework.data.mapping.model.EntityInstantiators;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.ldap.core.LdapOperations;
|
||||
import org.springframework.ldap.query.LdapQuery;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -39,9 +39,9 @@ import org.springframework.util.Assert;
|
||||
public class AnnotatedLdapRepositoryQuery extends AbstractLdapRepositoryQuery {
|
||||
|
||||
private final Query queryAnnotation;
|
||||
private final ValueExpressionDelegate valueExpressionDelegate;
|
||||
private final StringBasedQuery stringBasedQuery;
|
||||
private final StringBasedQuery stringBasedBase;
|
||||
private final StringBasedQuery query;
|
||||
private final StringBasedQuery base;
|
||||
private final ValueEvaluationContextProvider valueContextProvider;
|
||||
|
||||
/**
|
||||
* Construct a new instance.
|
||||
@@ -70,7 +70,7 @@ public class AnnotatedLdapRepositoryQuery extends AbstractLdapRepositoryQuery {
|
||||
* @param mappingContext must not be {@literal null}.
|
||||
* @param instantiators must not be {@literal null}.
|
||||
* @param valueExpressionDelegate must not be {@literal null}
|
||||
* @since 3.4
|
||||
* @since 3.5
|
||||
*/
|
||||
public AnnotatedLdapRepositoryQuery(LdapQueryMethod queryMethod, Class<?> entityType, LdapOperations ldapOperations,
|
||||
MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> mappingContext,
|
||||
@@ -81,34 +81,32 @@ public class AnnotatedLdapRepositoryQuery extends AbstractLdapRepositoryQuery {
|
||||
Assert.notNull(queryMethod.getQueryAnnotation(), "Annotation must be present");
|
||||
Assert.hasLength(queryMethod.getQueryAnnotation().value(), "Query filter must be specified");
|
||||
|
||||
queryAnnotation = queryMethod.getRequiredQueryAnnotation();
|
||||
this.valueExpressionDelegate = valueExpressionDelegate;
|
||||
stringBasedQuery = new StringBasedQuery(queryAnnotation.value(), queryMethod.getParameters(), valueExpressionDelegate);
|
||||
stringBasedBase = new StringBasedQuery(queryAnnotation.base(), queryMethod.getParameters(), valueExpressionDelegate);
|
||||
this.queryAnnotation = queryMethod.getRequiredQueryAnnotation();
|
||||
this.query = new StringBasedQuery(queryAnnotation.value(), queryMethod.getParameters(), valueExpressionDelegate);
|
||||
this.base = new StringBasedQuery(queryAnnotation.base(), queryMethod.getParameters(), valueExpressionDelegate);
|
||||
this.valueContextProvider = valueExpressionDelegate.createValueContextProvider(getQueryMethod().getParameters());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LdapQuery createQuery(LdapParameterAccessor parameters) {
|
||||
|
||||
ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate
|
||||
.createValueContextProvider(getQueryMethod().getParameters());
|
||||
String query = bind(parameters, valueContextProvider, this.query);
|
||||
String base = bind(parameters, valueContextProvider, this.base);
|
||||
|
||||
String boundQuery = bind(parameters, valueContextProvider, stringBasedQuery);
|
||||
|
||||
String boundBase = bind(parameters, valueContextProvider, stringBasedBase);
|
||||
|
||||
return query().base(boundBase) //
|
||||
return query().base(base) //
|
||||
.searchScope(queryAnnotation.searchScope()) //
|
||||
.countLimit(queryAnnotation.countLimit()) //
|
||||
.timeLimit(queryAnnotation.timeLimit()) //
|
||||
.filter(boundQuery);
|
||||
.filter(query, parameters.getBindableParameterValues());
|
||||
}
|
||||
|
||||
private String bind(LdapParameterAccessor parameters, ValueEvaluationContextProvider valueContextProvider, StringBasedQuery query) {
|
||||
|
||||
ValueEvaluationContext evaluationContext = valueContextProvider
|
||||
.getEvaluationContext(parameters.getBindableParameterValues(), query.getExpressionDependencies());
|
||||
|
||||
return query.bindQuery(parameters,
|
||||
new ContextualValueExpressionEvaluator(valueExpressionDelegate, evaluationContext));
|
||||
expression -> expression.evaluate(evaluationContext));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2024 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.ldap.repository.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ldap.support.LdapEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Value object capturing the binding context to provide {@link #getBindingValues() binding values} for queries.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.4
|
||||
*/
|
||||
class BindingContext {
|
||||
|
||||
private final Parameters<?, ?> parameters;
|
||||
|
||||
private final ParameterAccessor parameterAccessor;
|
||||
|
||||
private final List<ParameterBinding> bindings;
|
||||
|
||||
private final ValueExpressionEvaluator evaluator;
|
||||
|
||||
/**
|
||||
* Create new {@link BindingContext}.
|
||||
*/
|
||||
BindingContext(Parameters<?, ?> parameters, ParameterAccessor parameterAccessor,
|
||||
List<ParameterBinding> bindings, ValueExpressionEvaluator evaluator) {
|
||||
|
||||
this.parameters = parameters;
|
||||
this.parameterAccessor = parameterAccessor;
|
||||
this.bindings = bindings;
|
||||
this.evaluator = evaluator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} when list of bindings is not empty.
|
||||
*/
|
||||
private boolean hasBindings() {
|
||||
return !bindings.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind values provided by {@link LdapParameterAccessor} to placeholders in {@link BindingContext} while
|
||||
* considering potential conversions and parameter types.
|
||||
*
|
||||
* @return {@literal null} if given {@code raw} value is empty.
|
||||
*/
|
||||
public List<Object> getBindingValues() {
|
||||
|
||||
if (!hasBindings()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> parameters = new ArrayList<>(bindings.size());
|
||||
|
||||
for (ParameterBinding binding : bindings) {
|
||||
Object parameterValueForBinding = getParameterValueForBinding(binding);
|
||||
parameters.add(parameterValueForBinding);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value to be used for the given {@link ParameterBinding}.
|
||||
*
|
||||
* @param binding must not be {@literal null}.
|
||||
* @return the value used for the given {@link ParameterBinding}.
|
||||
*/
|
||||
@Nullable
|
||||
private Object getParameterValueForBinding(ParameterBinding binding) {
|
||||
|
||||
if (binding.isExpression()) {
|
||||
return evaluator.evaluate(binding.getRequiredExpression());
|
||||
}
|
||||
|
||||
Object value = binding.isNamed() ?
|
||||
parameterAccessor.getBindableValue(getParameterIndex(parameters, binding.getRequiredParameterName())) :
|
||||
parameterAccessor.getBindableValue(binding.getParameterIndex());
|
||||
return value == null ? null : LdapEncoder.filterEncode(value.toString());
|
||||
}
|
||||
|
||||
private int getParameterIndex(Parameters<?, ?> parameters, String parameterName) {
|
||||
|
||||
return parameters.stream() //
|
||||
.filter(parameter -> parameter //
|
||||
.getName().filter(s -> s.equals(parameterName)) //
|
||||
.isPresent()) //
|
||||
.mapToInt(Parameter::getIndex) //
|
||||
.findFirst() //
|
||||
.orElseThrow(() -> new IllegalArgumentException(
|
||||
String.format("Invalid parameter name; Cannot resolve parameter [%s]", parameterName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic parameter binding with name or position information.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class ParameterBinding {
|
||||
|
||||
private final int parameterIndex;
|
||||
private final @Nullable String expression;
|
||||
private final @Nullable String parameterName;
|
||||
|
||||
private ParameterBinding(int parameterIndex, @Nullable String expression, @Nullable String parameterName) {
|
||||
|
||||
this.parameterIndex = parameterIndex;
|
||||
this.expression = expression;
|
||||
this.parameterName = parameterName;
|
||||
}
|
||||
|
||||
static ParameterBinding expression(String expression, boolean quoted) {
|
||||
return new ParameterBinding(-1, expression, null);
|
||||
}
|
||||
|
||||
static ParameterBinding indexed(int parameterIndex) {
|
||||
return new ParameterBinding(parameterIndex, null, null);
|
||||
}
|
||||
|
||||
static ParameterBinding named(String name) {
|
||||
return new ParameterBinding(-1, null, name);
|
||||
}
|
||||
|
||||
boolean isNamed() {
|
||||
return (parameterName != null);
|
||||
}
|
||||
|
||||
int getParameterIndex() {
|
||||
return parameterIndex;
|
||||
}
|
||||
|
||||
String getParameter() {
|
||||
return ("?" + (isExpression() ? "expr" : "") + parameterIndex);
|
||||
}
|
||||
|
||||
String getRequiredExpression() {
|
||||
|
||||
Assert.state(expression != null, "ParameterBinding is not an expression");
|
||||
return expression;
|
||||
}
|
||||
|
||||
boolean isExpression() {
|
||||
return (this.expression != null);
|
||||
}
|
||||
|
||||
String getRequiredParameterName() {
|
||||
|
||||
Assert.state(parameterName != null, "ParameterBinding is not named");
|
||||
|
||||
return parameterName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 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.ldap.repository.query;
|
||||
|
||||
import org.springframework.data.expression.ValueEvaluationContext;
|
||||
import org.springframework.data.expression.ValueExpression;
|
||||
import org.springframework.data.expression.ValueExpressionParser;
|
||||
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class ContextualValueExpressionEvaluator implements ValueExpressionEvaluator {
|
||||
|
||||
private final ValueExpressionParser parser;
|
||||
|
||||
public ContextualValueExpressionEvaluator(ValueExpressionParser parser, ValueEvaluationContext evaluationContext) {
|
||||
this.parser = parser;
|
||||
this.evaluationContext = evaluationContext;
|
||||
}
|
||||
|
||||
private final ValueEvaluationContext evaluationContext;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T evaluate(String expressionString) {
|
||||
ValueExpression expression = parser.parse(expressionString);
|
||||
return (T) expression.evaluate(evaluationContext);
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import org.springframework.data.repository.query.ParameterAccessor;
|
||||
* @author Mark Paluch
|
||||
* @since 2.6
|
||||
*/
|
||||
interface LdapParameterAccessor extends ParameterAccessor {
|
||||
public interface LdapParameterAccessor extends ParameterAccessor {
|
||||
|
||||
/**
|
||||
* Returns the bindable parameter values of the underlying query method.
|
||||
|
||||
@@ -15,20 +15,27 @@
|
||||
*/
|
||||
package org.springframework.data.ldap.repository.query;
|
||||
|
||||
import static org.springframework.data.ldap.repository.query.StringBasedQuery.BindingContext.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.data.ldap.repository.query.BindingContext.ParameterBinding;
|
||||
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
|
||||
import org.springframework.data.expression.ValueExpression;
|
||||
import org.springframework.data.expression.ValueExpressionParser;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.ldap.support.LdapEncoder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -36,18 +43,13 @@ import org.springframework.util.StringUtils;
|
||||
* String-based Query abstracting a query with parameter bindings.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.4
|
||||
* @since 3.5
|
||||
*/
|
||||
class StringBasedQuery {
|
||||
|
||||
private final String query;
|
||||
|
||||
private final Parameters<?, ?> parameters;
|
||||
|
||||
private final ValueExpressionDelegate expressionParser;
|
||||
|
||||
private final List<ParameterBinding> queryParameterBindings = new ArrayList<>();
|
||||
|
||||
private final ExpressionDependencies expressionDependencies;
|
||||
|
||||
/**
|
||||
@@ -57,12 +59,11 @@ class StringBasedQuery {
|
||||
* @param parameters must not be {@literal null}.
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
*/
|
||||
StringBasedQuery(String query, Parameters<?, ?> parameters, ValueExpressionDelegate expressionParser) {
|
||||
public StringBasedQuery(String query, Parameters<?, ?> parameters, ValueExpressionDelegate expressionParser) {
|
||||
|
||||
this.query = ParameterBindingParser.INSTANCE.parseAndCollectParameterBindingsFromQueryIntoBindings(query,
|
||||
this.queryParameterBindings);
|
||||
this.query = ParameterBindingParser.parseAndCollectParameterBindingsFromQueryIntoBindings(query,
|
||||
this.queryParameterBindings, expressionParser);
|
||||
this.parameters = parameters;
|
||||
this.expressionParser = expressionParser;
|
||||
this.expressionDependencies = createExpressionDependencies();
|
||||
}
|
||||
|
||||
@@ -77,7 +78,7 @@ class StringBasedQuery {
|
||||
for (ParameterBinding binding : queryParameterBindings) {
|
||||
if (binding.isExpression()) {
|
||||
dependencies
|
||||
.add(expressionParser.parse(binding.getRequiredExpression()).getExpressionDependencies());
|
||||
.add(binding.getRequiredExpression().getExpressionDependencies());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,17 +101,17 @@ class StringBasedQuery {
|
||||
* @param evaluator must not be {@literal null}.
|
||||
* @return the bound String query containing formatted parameters.
|
||||
*/
|
||||
String bindQuery(LdapParameterAccessor parameterAccessor, ValueExpressionEvaluator evaluator) {
|
||||
public String bindQuery(LdapParameterAccessor parameterAccessor, Function<ValueExpression, Object> evaluator) {
|
||||
|
||||
Assert.notNull(parameterAccessor, "LdapParameterAccessor must not be null");
|
||||
Assert.notNull(evaluator, "SpELExpressionEvaluator must not be null");
|
||||
Assert.notNull(evaluator, "ExpressionEvaluator must not be null");
|
||||
|
||||
BindingContext bindingContext = new BindingContext(this.parameters, parameterAccessor, this.queryParameterBindings,
|
||||
evaluator);
|
||||
|
||||
List<Object> arguments = bindingContext.getBindingValues();
|
||||
|
||||
return ParameterBinder.INSTANCE.bind(this.query, arguments);
|
||||
return ParameterBinder.bind(this.query, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,54 +119,7 @@ class StringBasedQuery {
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
enum ParameterBinder {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
private static final String ARGUMENT_PLACEHOLDER = "?_param_?";
|
||||
private static final Pattern ARGUMENT_PLACEHOLDER_PATTERN = Pattern.compile(Pattern.quote(ARGUMENT_PLACEHOLDER));
|
||||
|
||||
public String bind(String input, List<Object> parameters) {
|
||||
|
||||
if (parameters.isEmpty()) {
|
||||
return input;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
int startIndex = 0;
|
||||
int currentPosition = 0;
|
||||
int parameterIndex = 0;
|
||||
|
||||
Matcher matcher = ARGUMENT_PLACEHOLDER_PATTERN.matcher(input);
|
||||
|
||||
while (currentPosition < input.length()) {
|
||||
|
||||
if (!matcher.find()) {
|
||||
break;
|
||||
}
|
||||
|
||||
int exprStart = matcher.start();
|
||||
|
||||
result.append(input.subSequence(startIndex, exprStart)).append(parameters.get(parameterIndex));
|
||||
|
||||
parameterIndex++;
|
||||
currentPosition = matcher.end();
|
||||
startIndex = currentPosition;
|
||||
}
|
||||
|
||||
return result.append(input.subSequence(currentPosition, input.length())).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A parser that extracts the parameter bindings from a given query string.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
enum ParameterBindingParser {
|
||||
|
||||
INSTANCE;
|
||||
static class ParameterBindingParser {
|
||||
|
||||
private static final char CURRLY_BRACE_OPEN = '{';
|
||||
private static final char CURRLY_BRACE_CLOSE = '}';
|
||||
@@ -177,7 +131,9 @@ class StringBasedQuery {
|
||||
private static final Pattern INDEX_BASED_PROPERTY_PLACEHOLDER_PATTERN = Pattern.compile("\\?\\$\\{");
|
||||
private static final Pattern NAME_BASED_PROPERTY_PLACEHOLDER_PATTERN = Pattern.compile("\\:\\$\\{");
|
||||
|
||||
private static final Set<Pattern> VALUE_EXPRESSION_PATTERNS = Set.of(INDEX_BASED_EXPRESSION_PATTERN, NAME_BASED_EXPRESSION_PATTERN, INDEX_BASED_PROPERTY_PLACEHOLDER_PATTERN, NAME_BASED_PROPERTY_PLACEHOLDER_PATTERN);
|
||||
private static final Set<Pattern> VALUE_EXPRESSION_PATTERNS = Set.of(INDEX_BASED_EXPRESSION_PATTERN,
|
||||
NAME_BASED_EXPRESSION_PATTERN, INDEX_BASED_PROPERTY_PLACEHOLDER_PATTERN,
|
||||
NAME_BASED_PROPERTY_PLACEHOLDER_PATTERN);
|
||||
|
||||
private static final String ARGUMENT_PLACEHOLDER = "?_param_?";
|
||||
|
||||
@@ -186,9 +142,11 @@ class StringBasedQuery {
|
||||
*
|
||||
* @param input can be {@literal null} or empty.
|
||||
* @param bindings must not be {@literal null}.
|
||||
* @param expressionParser must not be {@literal null}.
|
||||
* @return a list of {@link ParameterBinding}s found in the given {@code input}.
|
||||
*/
|
||||
public String parseAndCollectParameterBindingsFromQueryIntoBindings(String input, List<ParameterBinding> bindings) {
|
||||
public static String parseAndCollectParameterBindingsFromQueryIntoBindings(String input,
|
||||
List<ParameterBinding> bindings, ValueExpressionParser expressionParser) {
|
||||
|
||||
if (!StringUtils.hasText(input)) {
|
||||
return input;
|
||||
@@ -196,11 +154,11 @@ class StringBasedQuery {
|
||||
|
||||
Assert.notNull(bindings, "Parameter bindings must not be null");
|
||||
|
||||
return transformQueryAndCollectExpressionParametersIntoBindings(input, bindings);
|
||||
return transformQueryAndCollectExpressionParametersIntoBindings(input, bindings, expressionParser);
|
||||
}
|
||||
|
||||
private static String transformQueryAndCollectExpressionParametersIntoBindings(String input,
|
||||
List<ParameterBinding> bindings) {
|
||||
List<ParameterBinding> bindings, ValueExpressionParser expressionParser) {
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
@@ -243,13 +201,11 @@ class StringBasedQuery {
|
||||
result.append(ARGUMENT_PLACEHOLDER);
|
||||
|
||||
if (isValueExpression(matcher)) {
|
||||
bindings.add(
|
||||
ParameterBinding
|
||||
.expression(input.substring(exprStart + 1, currentPosition), true));
|
||||
bindings.add(ParameterBinding
|
||||
.expression(expressionParser.parse(input.substring(exprStart + 1, currentPosition)), true));
|
||||
} else {
|
||||
if (matcher.pattern() == INDEX_PARAMETER_BINDING_PATTERN) {
|
||||
bindings
|
||||
.add(ParameterBinding.indexed(Integer.parseInt(matcher.group(1))));
|
||||
bindings.add(ParameterBinding.indexed(Integer.parseInt(matcher.group(1))));
|
||||
} else {
|
||||
bindings.add(ParameterBinding.named(matcher.group(1)));
|
||||
}
|
||||
@@ -290,4 +246,191 @@ class StringBasedQuery {
|
||||
return (matcherMap.isEmpty() ? null : matcherMap.values().iterator().next());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A parser that extracts the parameter bindings from a given query string.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class ParameterBinder {
|
||||
|
||||
|
||||
private static final String ARGUMENT_PLACEHOLDER = "?_param_?";
|
||||
private static final Pattern ARGUMENT_PLACEHOLDER_PATTERN = Pattern.compile(Pattern.quote(ARGUMENT_PLACEHOLDER));
|
||||
|
||||
public static String bind(String input, List<Object> parameters) {
|
||||
|
||||
if (parameters.isEmpty()) {
|
||||
return input;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
int startIndex = 0;
|
||||
int currentPosition = 0;
|
||||
int parameterIndex = 0;
|
||||
|
||||
Matcher matcher = ARGUMENT_PLACEHOLDER_PATTERN.matcher(input);
|
||||
|
||||
while (currentPosition < input.length()) {
|
||||
|
||||
if (!matcher.find()) {
|
||||
break;
|
||||
}
|
||||
|
||||
int exprStart = matcher.start();
|
||||
|
||||
result.append(input.subSequence(startIndex, exprStart)).append(parameters.get(parameterIndex));
|
||||
|
||||
parameterIndex++;
|
||||
currentPosition = matcher.end();
|
||||
startIndex = currentPosition;
|
||||
}
|
||||
|
||||
return result.append(input.subSequence(currentPosition, input.length())).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object capturing the binding context to provide {@link #getBindingValues() binding values} for queries.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class BindingContext {
|
||||
|
||||
private final Parameters<?, ?> parameters;
|
||||
private final ParameterAccessor parameterAccessor;
|
||||
private final List<ParameterBinding> bindings;
|
||||
private final Function<ValueExpression, Object> evaluator;
|
||||
|
||||
/**
|
||||
* Create new {@link BindingContext}.
|
||||
*/
|
||||
BindingContext(Parameters<?, ?> parameters, ParameterAccessor parameterAccessor, List<ParameterBinding> bindings,
|
||||
Function<ValueExpression, Object> evaluator) {
|
||||
|
||||
this.parameters = parameters;
|
||||
this.parameterAccessor = parameterAccessor;
|
||||
this.bindings = bindings;
|
||||
this.evaluator = evaluator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} when list of bindings is not empty.
|
||||
*/
|
||||
private boolean hasBindings() {
|
||||
return !bindings.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind values provided by {@link LdapParameterAccessor} to placeholders in {@link BindingContext} while considering
|
||||
* potential conversions and parameter types.
|
||||
*
|
||||
* @return {@literal null} if given {@code raw} value is empty.
|
||||
*/
|
||||
public List<Object> getBindingValues() {
|
||||
|
||||
if (!hasBindings()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> parameters = new ArrayList<>(bindings.size());
|
||||
|
||||
for (ParameterBinding binding : bindings) {
|
||||
Object parameterValueForBinding = getParameterValueForBinding(binding);
|
||||
parameters.add(parameterValueForBinding);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value to be used for the given {@link ParameterBinding}.
|
||||
*
|
||||
* @param binding must not be {@literal null}.
|
||||
* @return the value used for the given {@link ParameterBinding}.
|
||||
*/
|
||||
@Nullable
|
||||
private Object getParameterValueForBinding(ParameterBinding binding) {
|
||||
|
||||
if (binding.isExpression()) {
|
||||
return evaluator.apply(binding.getRequiredExpression());
|
||||
}
|
||||
|
||||
Object value = binding.isNamed()
|
||||
? parameterAccessor.getBindableValue(getParameterIndex(parameters, binding.getRequiredParameterName()))
|
||||
: parameterAccessor.getBindableValue(binding.getParameterIndex());
|
||||
return value == null ? null : LdapEncoder.filterEncode(value.toString());
|
||||
}
|
||||
|
||||
private int getParameterIndex(Parameters<?, ?> parameters, String parameterName) {
|
||||
|
||||
for (Parameter parameter : parameters) {
|
||||
|
||||
if (parameter.getName().filter(it -> it.equals(parameterName)).isPresent()) {
|
||||
return parameter.getIndex();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid parameter name; Cannot resolve parameter [%s]", parameterName));
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic parameter binding with name or position information.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class ParameterBinding {
|
||||
|
||||
private final int parameterIndex;
|
||||
private final @Nullable ValueExpression expression;
|
||||
private final @Nullable String parameterName;
|
||||
|
||||
private ParameterBinding(int parameterIndex, @Nullable ValueExpression expression,
|
||||
@Nullable String parameterName) {
|
||||
|
||||
this.parameterIndex = parameterIndex;
|
||||
this.expression = expression;
|
||||
this.parameterName = parameterName;
|
||||
}
|
||||
|
||||
static ParameterBinding expression(ValueExpression expression, boolean quoted) {
|
||||
return new ParameterBinding(-1, expression, null);
|
||||
}
|
||||
|
||||
static ParameterBinding indexed(int parameterIndex) {
|
||||
return new ParameterBinding(parameterIndex, null, null);
|
||||
}
|
||||
|
||||
static ParameterBinding named(String name) {
|
||||
return new ParameterBinding(-1, null, name);
|
||||
}
|
||||
|
||||
boolean isNamed() {
|
||||
return (parameterName != null);
|
||||
}
|
||||
|
||||
int getParameterIndex() {
|
||||
return parameterIndex;
|
||||
}
|
||||
|
||||
ValueExpression getRequiredExpression() {
|
||||
|
||||
Assert.state(expression != null, "ParameterBinding is not an expression");
|
||||
return expression;
|
||||
}
|
||||
|
||||
boolean isExpression() {
|
||||
return (this.expression != null);
|
||||
}
|
||||
|
||||
String getRequiredParameterName() {
|
||||
|
||||
Assert.state(parameterName != null, "ParameterBinding is not named");
|
||||
|
||||
return parameterName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.ldap.config;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2024 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.ldap.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -74,11 +74,9 @@ public class InMemoryLdapConfiguration {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@DependsOn("directoryServer")
|
||||
LdapContextSource ldapContextSource(Environment environment, EmbeddedLdapProperties properties,
|
||||
EmbeddedLdapProperties embeddedProperties) {
|
||||
LdapContextSource ldapContextSource(Environment environment, EmbeddedLdapProperties properties) {
|
||||
LdapContextSource source = new LdapContextSource();
|
||||
Assert.notEmpty(properties.getBaseDn(), "Base DN must be set with at least one value");
|
||||
source.setBase(properties.getBaseDn().get(0));
|
||||
|
||||
@@ -31,7 +31,6 @@ import org.springframework.data.ldap.config.InMemoryLdapConfiguration;
|
||||
import org.springframework.data.ldap.repository.LdapRepository;
|
||||
import org.springframework.data.ldap.repository.Query;
|
||||
import org.springframework.data.ldap.repository.config.EnableLdapRepositories;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
@@ -41,79 +40,47 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@TestPropertySource(properties = { "full.name=John Doe", "dc.name=memorynotfound" })
|
||||
class ValueExpressionLdapRepositoryQueryTests {
|
||||
@TestPropertySource(properties = { "full.name=John Doe" })
|
||||
class AnnotatedLdapRepositoryQueryIntegrationTests {
|
||||
|
||||
@Autowired private QueryRepository queryRepository;
|
||||
@Autowired QueryRepository queryRepository;
|
||||
|
||||
@Test
|
||||
void shouldWorkWithNamedParameters() {
|
||||
@Test // GH-453
|
||||
void filterWithNamedParameters() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.namedParameters("John Doe", "Bar");
|
||||
List<SchemaEntry> objects = queryRepository.namedParameters("John Doe");
|
||||
|
||||
assertThatReturnedObjectIsJohnDoe(objects);
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingQueryLanguageCharsShouldNotFail() {
|
||||
@Test // GH-453
|
||||
void filterWithPositionalParameters() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.namedParameters("John)(cn=Doe)", "Bar");
|
||||
|
||||
assertThat(objects).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithIndexParameters() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.indexedParameters("John Doe", "Bar");
|
||||
List<SchemaEntry> objects = queryRepository.indexedParameters("John Doe");
|
||||
|
||||
assertThatReturnedObjectIsJohnDoe(objects);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithSpelExpressions() {
|
||||
@Test // GH-453
|
||||
void filterWithSpelExpression() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.spelParameters();
|
||||
|
||||
assertThatReturnedObjectIsJohnDoe(objects);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithPropertyPlaceholders() {
|
||||
@Test // GH-453
|
||||
void filterWithPropertyPlaceholder() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.propertyPlaceholderParameters();
|
||||
|
||||
assertThatReturnedObjectIsJohnDoe(objects);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithNamedParametersForBase() {
|
||||
@Test // GH-453
|
||||
void baseWithNamedParameters() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.baseNamedParameters("John Doe", "dc=memorynotfound");
|
||||
|
||||
assertThatReturnedObjectIsJohnDoe(objects);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithIndexParametersForBase() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.baseIndexedParameters("John Doe", "memorynotfound");
|
||||
|
||||
assertThatReturnedObjectIsJohnDoe(objects);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithSpelExpressionsForBase() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.baseSpelParameters();
|
||||
|
||||
assertThatReturnedObjectIsJohnDoe(objects);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithPropertyPlaceholdersForBase() {
|
||||
|
||||
List<SchemaEntry> objects = queryRepository.basePropertyPlaceholderParameters();
|
||||
List<SchemaEntry> objects = queryRepository.baseNamedParameters("John Doe", "memorynotfound");
|
||||
|
||||
assertThatReturnedObjectIsJohnDoe(objects);
|
||||
}
|
||||
@@ -141,10 +108,10 @@ class ValueExpressionLdapRepositoryQueryTests {
|
||||
interface QueryRepository extends LdapRepository<SchemaEntry> {
|
||||
|
||||
@Query(value = "(cn=:fullName)")
|
||||
List<SchemaEntry> namedParameters(@Param("fullName") String fullName, @Param("lastName") String lastName);
|
||||
List<SchemaEntry> namedParameters(String fullName);
|
||||
|
||||
@Query(value = "(cn=?0)")
|
||||
List<SchemaEntry> indexedParameters(String fullName, String lastName);
|
||||
List<SchemaEntry> indexedParameters(String fullName);
|
||||
|
||||
@Query(value = "(cn=:#{'John ' + 'Doe'})")
|
||||
List<SchemaEntry> spelParameters();
|
||||
@@ -152,16 +119,7 @@ class ValueExpressionLdapRepositoryQueryTests {
|
||||
@Query(value = "(cn=?${full.name})")
|
||||
List<SchemaEntry> propertyPlaceholderParameters();
|
||||
|
||||
@Query(base = ":dc", value = "(cn=:fullName)")
|
||||
List<SchemaEntry> baseNamedParameters(@Param("fullName") String fullName, @Param("dc") String dc);
|
||||
|
||||
@Query(base = "dc=?1", value = "(cn=?0)")
|
||||
List<SchemaEntry> baseIndexedParameters(String fullName, String dc);
|
||||
|
||||
@Query(base = "dc=:#{'memory' + 'notfound'}", value = "(cn=:#{'John ' + 'Doe'})")
|
||||
List<SchemaEntry> baseSpelParameters();
|
||||
|
||||
@Query(base = "dc=?${dc.name}", value = "(cn=?${full.name})")
|
||||
List<SchemaEntry> basePropertyPlaceholderParameters();
|
||||
@Query(base = "dc=:dc", value = "(cn=:fullName)")
|
||||
List<SchemaEntry> baseNamedParameters(String fullName, String dc);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2024 the original author or authors.
|
||||
* Copyright 2024 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.
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.ldap.repository.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -26,14 +28,17 @@ import org.springframework.data.ldap.repository.Query;
|
||||
import org.springframework.data.mapping.model.EntityInstantiators;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.ldap.core.LdapOperations;
|
||||
import org.springframework.ldap.query.LdapQuery;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AnnotatedLdapRepositoryQueryTests {
|
||||
/**
|
||||
* Unit tests for {@link AnnotatedLdapRepositoryQuery}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class AnnotatedLdapRepositoryQueryUnitTests {
|
||||
|
||||
LdapOperations ldapOperations = Mockito.mock();
|
||||
|
||||
@@ -41,18 +46,31 @@ class AnnotatedLdapRepositoryQueryTests {
|
||||
|
||||
@Test
|
||||
void shouldEncodeQuery() throws NoSuchMethodException {
|
||||
LdapQueryMethod method = queryMethod("namedParameters");
|
||||
|
||||
LdapQueryMethod method = queryMethod("namedParameters", String.class);
|
||||
AnnotatedLdapRepositoryQuery query = repositoryQuery(method);
|
||||
|
||||
LdapQuery ldapQuery = query.createQuery(
|
||||
new LdapParametersParameterAccessor(method, new Object[] { "John)(cn=Doe)", "foo" }));
|
||||
new LdapParametersParameterAccessor(method, new Object[] { "John)(cn=Doe)" }));
|
||||
|
||||
assertThat(ldapQuery.filter().encode()).isEqualTo("(cn=John\\29\\28cn=Doe\\29)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void messageFormatParametersShouldWork() throws NoSuchMethodException {
|
||||
|
||||
LdapQueryMethod method = queryMethod("messageFormatParameters", String.class);
|
||||
AnnotatedLdapRepositoryQuery query = repositoryQuery(method);
|
||||
|
||||
LdapQuery ldapQuery = query.createQuery(new LdapParametersParameterAccessor(method, new Object[] { "John" }));
|
||||
|
||||
assertThat(ldapQuery.filter().encode()).isEqualTo("(cn=John)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEncodeBase() throws NoSuchMethodException {
|
||||
LdapQueryMethod method = queryMethod("baseNamedParameters");
|
||||
|
||||
LdapQueryMethod method = queryMethod("baseNamedParameters", String.class, String.class);
|
||||
AnnotatedLdapRepositoryQuery query = repositoryQuery(method);
|
||||
|
||||
LdapQuery ldapQuery = query.createQuery(
|
||||
@@ -61,8 +79,8 @@ class AnnotatedLdapRepositoryQueryTests {
|
||||
assertThat(ldapQuery.base()).hasToString("cn=John\\29");
|
||||
}
|
||||
|
||||
private LdapQueryMethod queryMethod(String methodName) throws NoSuchMethodException {
|
||||
return new LdapQueryMethod(QueryRepository.class.getMethod(methodName, String.class, String.class),
|
||||
private LdapQueryMethod queryMethod(String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
|
||||
return new LdapQueryMethod(QueryRepository.class.getMethod(methodName, parameterTypes),
|
||||
new DefaultRepositoryMetadata(QueryRepository.class), new SpelAwareProxyProjectionFactory());
|
||||
}
|
||||
|
||||
@@ -74,10 +92,13 @@ class AnnotatedLdapRepositoryQueryTests {
|
||||
interface QueryRepository extends LdapRepository<SchemaEntry> {
|
||||
|
||||
@Query(value = "(cn=:fullName)")
|
||||
List<SchemaEntry> namedParameters(@Param("fullName") String fullName, @Param("lastName") String lastName);
|
||||
List<SchemaEntry> namedParameters(String fullName);
|
||||
|
||||
@Query(value = "(cn={0})")
|
||||
List<SchemaEntry> messageFormatParameters(String fullName);
|
||||
|
||||
@Query(base = ":dc", value = "(cn=:fullName)")
|
||||
List<SchemaEntry> baseNamedParameters(@Param("fullName") String fullName, @Param("dc") String dc);
|
||||
List<SchemaEntry> baseNamedParameters(String fullName, String dc);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2024 the original author or authors.
|
||||
* Copyright 2024 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.
|
||||
@@ -13,13 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.ldap.repository.query;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
import org.springframework.ldap.odm.annotations.Attribute;
|
||||
import org.springframework.ldap.odm.annotations.DnAttribute;
|
||||
import org.springframework.ldap.odm.annotations.Entry;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
|
||||
@@ -27,8 +25,8 @@ import org.springframework.ldap.odm.annotations.Id;
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" })
|
||||
|
||||
public class SchemaEntry {
|
||||
|
||||
@Id
|
||||
Name dn;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user