DATAJPA-1519 - Polishing.
Moved the registration of the escape(…) SpEL function into an EvaluationContextExtension to avoid the need for a dedicated static method on EscapeCharacter. Moved Escape Character in the ….repository.query package to remove the package cycle.
This commit is contained in:
@@ -45,6 +45,7 @@ import org.springframework.dao.annotation.PersistenceExceptionTranslationPostPro
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.support.DefaultJpaContext;
|
||||
import org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor;
|
||||
import org.springframework.data.jpa.repository.support.JpaEvaluationContextExtension;
|
||||
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
@@ -76,6 +77,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi
|
||||
private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
|
||||
private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions";
|
||||
private static final String JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME = "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup";
|
||||
private static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter";
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -132,17 +134,18 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi
|
||||
Optional<String> transactionManagerRef = source.getAttribute("transactionManagerRef");
|
||||
builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
|
||||
builder.addPropertyValue("entityManager", getEntityManagerBeanDefinitionFor(source, source.getSource()));
|
||||
builder.addPropertyValue("escapeCharacter", getEscapeCharacter(source).orElse('\\'));
|
||||
builder.addPropertyValue(ESCAPE_CHARACTER_PROPERTY, getEscapeCharacter(source).orElse('\\'));
|
||||
builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* XML configurations do not support {@link Character} values. This method catches the exception thrown and returns an {@link Optional#empty()} instead.
|
||||
* XML configurations do not support {@link Character} values. This method catches the exception thrown and returns an
|
||||
* {@link Optional#empty()} instead.
|
||||
*/
|
||||
private static Optional<Character> getEscapeCharacter(RepositoryConfigurationSource source) {
|
||||
|
||||
try {
|
||||
return source.getAttribute("escapeCharacter", Character.class);
|
||||
return source.getAttribute(ESCAPE_CHARACTER_PROPERTY, Character.class);
|
||||
} catch (IllegalArgumentException ___) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -209,6 +212,21 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi
|
||||
|
||||
registerIfNotAlreadyRegistered(() -> new RootBeanDefinition(JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME), registry,
|
||||
JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME, source);
|
||||
|
||||
// EvaluationContextExtension for JPA specific SpEL functions
|
||||
|
||||
registerIfNotAlreadyRegistered(() -> {
|
||||
|
||||
Object value = AnnotationRepositoryConfigurationSource.class.isInstance(config) //
|
||||
? config.getRequiredAttribute(ESCAPE_CHARACTER_PROPERTY, Character.class) //
|
||||
: config.getAttribute(ESCAPE_CHARACTER_PROPERTY).orElse("\\");
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(JpaEvaluationContextExtension.class);
|
||||
builder.addConstructorArgValue(value);
|
||||
|
||||
return builder.getBeanDefinition();
|
||||
|
||||
}, registry, JpaEvaluationContextExtension.class.getName(), source);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -13,35 +13,40 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.support;
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A Value-class encapsulating an escape character for LIKE queries and the actually usage of it in escaping Strings.
|
||||
* A value type encapsulating an escape character for LIKE queries and the actually usage of it in escaping
|
||||
* {@link String}s.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
@Value(staticConstructor = "of")
|
||||
public class EscapeCharacter {
|
||||
|
||||
private static final List<String> TO_REPLACE = Arrays.asList("_", "%");
|
||||
|
||||
char value;
|
||||
|
||||
/**
|
||||
* Escapes all special like characters ({@code _}, {@code %}) using the configured escape character.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public String escape(String value) {
|
||||
|
||||
Assert.notNull(value, "Value must be not null.");
|
||||
|
||||
return value.replace("_", value + "_").replace("%", value + "%");
|
||||
}
|
||||
|
||||
// used for SpEL expressions
|
||||
static String escape(String value, String escape) {
|
||||
|
||||
Assert.hasText(escape, "escape must be a sinlge character String.");
|
||||
char[] chars = escape.toCharArray();
|
||||
Assert.isTrue(chars.length == 1, "escape must be a single character String.");
|
||||
|
||||
return EscapeCharacter.of(chars[0]).escape(value);
|
||||
return TO_REPLACE.stream() //
|
||||
.reduce(value, (it, character) -> it.replace(character, this.value + character));
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
|
||||
@@ -22,7 +22,6 @@ import javax.persistence.EntityManager;
|
||||
import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.provider.QueryExtractor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
|
||||
@@ -27,7 +27,6 @@ import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.ParameterExpression;
|
||||
|
||||
import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.DeleteExecution;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryExecution.ExistsExecution;
|
||||
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ReturnedType;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -26,7 +25,6 @@ import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter;
|
||||
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
|
||||
import org.springframework.data.jpa.repository.query.QueryParameterSetter.NamedOrIndexedQueryParameterSetter;
|
||||
import org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
@@ -36,7 +34,6 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Encapsulates different strategies for the creation of a {@link QueryParameterSetter} from a {@link Query} and a
|
||||
@@ -178,11 +175,7 @@ abstract class QueryParameterSetterFactory {
|
||||
private Object evaluateExpression(Expression expression, Object[] values) {
|
||||
|
||||
EvaluationContext context = evaluationContextProvider.getEvaluationContext(parameters, values);
|
||||
Method escapeMethod = ReflectionUtils.findMethod(EscapeCharacter.class, "escape", String.class, String.class);
|
||||
|
||||
Assert.notNull(escapeMethod, "Escape method must not be null.");
|
||||
|
||||
context.setVariable("escape", escapeMethod);
|
||||
return expression.getValue(context, Object.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2019 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
|
||||
*
|
||||
* http://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.jpa.repository.support;
|
||||
|
||||
import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.data.spel.spi.EvaluationContextExtension;
|
||||
|
||||
/**
|
||||
* {@link EvaluationContextExtension} to register {@link EscapeCharacter} as root object to essentially expose an
|
||||
* {@code expose(…)} function to SpEL.
|
||||
*
|
||||
* @author Oliver Drotbohm
|
||||
*/
|
||||
public class JpaEvaluationContextExtension implements EvaluationContextExtension {
|
||||
|
||||
private final EscapeCharacter character;
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaEvaluationContextExtension} for the given escape character.
|
||||
*
|
||||
* @param escapeCharacter the character to be used to escape parameters for LIKE expression.
|
||||
*/
|
||||
public JpaEvaluationContextExtension(char escapeCharacter) {
|
||||
this.character = EscapeCharacter.of(escapeCharacter);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.spel.spi.EvaluationContextExtension#getExtensionId()
|
||||
*/
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "jpa";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.spel.spi.EvaluationContextExtension#getRootObject()
|
||||
*/
|
||||
@Override
|
||||
public Object getRootObject() {
|
||||
return character;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.provider.QueryExtractor;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.query.AbstractJpaQuery;
|
||||
import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryMethod;
|
||||
import org.springframework.data.jpa.util.JpaMetamodel;
|
||||
|
||||
@@ -20,6 +20,7 @@ import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.jpa.repository.query.EscapeCharacter;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.querydsl.EntityPathResolver;
|
||||
import org.springframework.data.querydsl.SimpleEntityPathResolver;
|
||||
|
||||
@@ -3,4 +3,5 @@ http\://www.springframework.org/schema/data/jpa/spring-jpa-1.1.xsd=org/springfra
|
||||
http\://www.springframework.org/schema/data/jpa/spring-jpa-1.2.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.2.xsd
|
||||
http\://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.3.xsd
|
||||
http\://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.8.xsd
|
||||
http\://www.springframework.org/schema/data/jpa/spring-jpa.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.8.xsd
|
||||
http\://www.springframework.org/schema/data/jpa/spring-jpa-1.11.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.11.xsd
|
||||
http\://www.springframework.org/schema/data/jpa/spring-jpa.xsd=org/springframework/data/jpa/repository/config/spring-jpa-1.11.xsd
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/data/jpa"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:repository="http://www.springframework.org/schema/data/repository"
|
||||
targetNamespace="http://www.springframework.org/schema/data/jpa"
|
||||
elementFormDefault="qualified" attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool" />
|
||||
<xsd:import namespace="http://www.springframework.org/schema/context" />
|
||||
<xsd:import namespace="http://www.springframework.org/schema/data/repository"
|
||||
schemaLocation="https://www.springframework.org/schema/data/repository/spring-repository.xsd" />
|
||||
|
||||
<xsd:element name="repositories">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="repository:repositories">
|
||||
<xsd:attributeGroup ref="repository:transactional-repository-attributes" />
|
||||
<xsd:attribute name="entity-manager-factory-ref" type="entityManagerFactoryRef" />
|
||||
<xsd:attribute name="enable-default-transactions" type="xsd:boolean">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Controls whether repositories get transactions enabled by default.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="escape-character" type="xsd:string" default="\">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Configures what character is used to escape the LIKE wildcards _ and % in derived queries with contains, startsWith or endsWith clauses. Defaults to \.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="auditing">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="org.springframework.data.jpa.domain.support.AuditingEntityListener" />
|
||||
<tool:exports type="org.springframework.data.auditing.AuditingHandler" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:attributeGroup ref="repository:auditing-attributes" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:simpleType name="entityManagerFactoryRef">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:assignable-to type="org.springframework.orm.jpa.AbstractEntityManagerFactoryBean" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:union memberTypes="xsd:string" />
|
||||
</xsd:simpleType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -31,7 +31,6 @@ import org.springframework.data.jpa.domain.sample.Role;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.provider.HibernateUtils;
|
||||
import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.provider.QueryExtractor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.repository.query.DefaultParameters;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
@@ -24,7 +24,6 @@ import javax.persistence.criteria.CriteriaBuilder;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.provider.HibernateUtils;
|
||||
import org.springframework.data.jpa.provider.PersistenceProvider;
|
||||
import org.springframework.data.jpa.repository.Temporal;
|
||||
import org.springframework.data.jpa.repository.support.EscapeCharacter;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
@@ -559,7 +559,7 @@ public interface UserRepository
|
||||
List<NameOnlyDto> findByNamedQueryWithConstructorExpression();
|
||||
|
||||
// DATAJPA-1519
|
||||
@Query("select u from User u where u.firstname like %?#{#escape([0],'#')}% escape '#'")
|
||||
@Query("select u from User u where u.firstname like %?#{escape([0])}% escape '\\'")
|
||||
List<User> findContainingEscaped(String namePart);
|
||||
|
||||
interface RolesAndFirstname {
|
||||
|
||||
Reference in New Issue
Block a user