@@ -24,6 +24,7 @@ import org.springframework.util.StringUtils;
|
||||
* A wrapper for a String representation of a query offering information about the query.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.0.3
|
||||
*/
|
||||
interface DeclaredQuery {
|
||||
|
||||
@@ -31,7 +32,6 @@ interface DeclaredQuery {
|
||||
* Creates a {@literal DeclaredQuery} from a query {@literal String}.
|
||||
*
|
||||
* @param query might be {@literal null} or empty.
|
||||
*
|
||||
* @return a {@literal DeclaredQuery} instance even for a {@literal null} or empty argument.
|
||||
*/
|
||||
static DeclaredQuery of(@Nullable String query) {
|
||||
@@ -80,8 +80,7 @@ interface DeclaredQuery {
|
||||
*
|
||||
* @param countQuery an optional query string to be used if present.
|
||||
* @param countQueryProjection an optional return type for the query.
|
||||
* @return A new {@literal DeclaredQuery} instance.
|
||||
* @return a new {@literal DeclaredQuery} instance.
|
||||
*/
|
||||
DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable String countQueryProjection);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,16 +15,17 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* NULL-Object pattern implementation.
|
||||
* NULL-Object pattern implementation for {@link DeclaredQuery}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.0.3
|
||||
*/
|
||||
class EmptyDeclaredQuery implements DeclaredQuery {
|
||||
|
||||
@@ -33,36 +34,64 @@ class EmptyDeclaredQuery implements DeclaredQuery {
|
||||
*/
|
||||
static final DeclaredQuery EMPTY_QUERY = new EmptyDeclaredQuery();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#hasNamedParameter()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNamedParameter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#getQueryString()
|
||||
*/
|
||||
@Override
|
||||
public String getQueryString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#getAlias()
|
||||
*/
|
||||
@Override
|
||||
public String getAlias() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#hasConstructorExpression()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasConstructorExpression() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#isDefaultProjection()
|
||||
*/
|
||||
@Override
|
||||
public boolean isDefaultProjection() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#getParameterBindings()
|
||||
*/
|
||||
@Override
|
||||
public List<StringQuery.ParameterBinding> getParameterBindings() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#deriveCountQuery(java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable String countQueryProjection) {
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ class ParameterBinderFactory {
|
||||
}
|
||||
|
||||
private static Iterable<QueryParameterSetter> createSetters(List<ParameterBinding> parameterBindings,
|
||||
DeclaredQuery declaredQuery, QueryParameterSetterFactory... strategies) {
|
||||
DeclaredQuery declaredQuery, QueryParameterSetterFactory... strategies) {
|
||||
|
||||
return parameterBindings.stream() //
|
||||
.map(it -> createQueryParameterSetter(it, strategies, declaredQuery)) //
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 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.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Datastructure that analyses a String to determine the parts of the String that are quoted and offers an API to query
|
||||
* that information.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class QuotationMap {
|
||||
|
||||
private static final Set<Character> QUOTING_CHARACTERS = new HashSet<>(Arrays.asList('"', '\''));
|
||||
private List<Range<Integer>> quotedRanges = new ArrayList<>();
|
||||
|
||||
QuotationMap(@Nullable String query) {
|
||||
|
||||
if (query == null)
|
||||
return;
|
||||
|
||||
Character inQuotation = null;
|
||||
int start = 0;
|
||||
|
||||
for (int i = 0; i < query.length(); i++) {
|
||||
|
||||
char currentChar = query.charAt(i);
|
||||
if (QUOTING_CHARACTERS.contains(currentChar)) {
|
||||
|
||||
if (inQuotation == null) {
|
||||
|
||||
inQuotation = currentChar;
|
||||
start = i;
|
||||
} else if (currentChar == inQuotation) {
|
||||
|
||||
inQuotation = null;
|
||||
quotedRanges.add(Range.of(Range.Bound.inclusive(start), Range.Bound.inclusive(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inQuotation != null) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("The string <%s> starts a quoted range at %d, but never ends it.", query, start));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index to check if it is part of a quoted range.
|
||||
* @return whether the query contains a quoted range at {@literal index}.
|
||||
*/
|
||||
public boolean isQuoted(int index) {
|
||||
return quotedRanges.stream().anyMatch(r -> r.contains(index));
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,13 @@ import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.repository.query.parser.Part.Type;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -75,13 +78,23 @@ class StringQuery implements DeclaredQuery {
|
||||
return !bindings.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ParameterBinding}s registered.
|
||||
String getProjection() {
|
||||
return QueryUtils.getProjection(query);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#getParameterBindings()
|
||||
*/
|
||||
@Override
|
||||
public List<ParameterBinding> getParameterBindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#deriveCountQuery(java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public DeclaredQuery deriveCountQuery(@Nullable String countQuery, @Nullable String countQueryProjection) {
|
||||
|
||||
@@ -89,18 +102,18 @@ class StringQuery implements DeclaredQuery {
|
||||
.of(countQuery != null ? countQuery : QueryUtils.createCountQueryFor(query, countQueryProjection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query string.
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#getQueryString()
|
||||
*/
|
||||
@Override
|
||||
public String getQueryString() {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the main alias used in the query.
|
||||
*
|
||||
* @return the alias
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#getAlias()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@@ -108,33 +121,31 @@ class StringQuery implements DeclaredQuery {
|
||||
return alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the query is using a constructor expression.
|
||||
*
|
||||
* @since 1.10
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#hasConstructorExpression()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasConstructorExpression() {
|
||||
return hasConstructorExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the query uses the default projection, i.e. returns the main alias defined for the query.
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#isDefaultProjection()
|
||||
*/
|
||||
@Override
|
||||
public boolean isDefaultProjection() {
|
||||
return getProjection().equals(alias);
|
||||
}
|
||||
|
||||
public String getProjection() {
|
||||
return QueryUtils.getProjection(query);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jpa.repository.query.DeclaredQuery#hasNamedParameter()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNamedParameter() {
|
||||
|
||||
return bindings.stream() //
|
||||
.anyMatch(b -> b.getName() != null);
|
||||
return bindings.stream().anyMatch(b -> b.getName() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -760,4 +771,59 @@ class StringQuery implements DeclaredQuery {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object to analyze a String to determine the parts of the String that are quoted and offers an API to query
|
||||
* that information.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 3.0.3
|
||||
*/
|
||||
static class QuotationMap {
|
||||
|
||||
private static final Set<Character> QUOTING_CHARACTERS = new HashSet<>(Arrays.asList('"', '\''));
|
||||
|
||||
private List<Range<Integer>> quotedRanges = new ArrayList<>();
|
||||
|
||||
QuotationMap(@Nullable String query) {
|
||||
|
||||
if (query == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Character inQuotation = null;
|
||||
int start = 0;
|
||||
|
||||
for (int i = 0; i < query.length(); i++) {
|
||||
|
||||
char currentChar = query.charAt(i);
|
||||
|
||||
if (QUOTING_CHARACTERS.contains(currentChar)) {
|
||||
|
||||
if (inQuotation == null) {
|
||||
|
||||
inQuotation = currentChar;
|
||||
start = i;
|
||||
|
||||
} else if (currentChar == inQuotation) {
|
||||
|
||||
inQuotation = null;
|
||||
quotedRanges.add(Range.of(Range.Bound.inclusive(start), Range.Bound.inclusive(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inQuotation != null) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("The string <%s> starts a quoted range at %d, but never ends it.", query, start));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index to check if it is part of a quoted range.
|
||||
* @return whether the query contains a quoted range at {@literal index}.
|
||||
*/
|
||||
public boolean isQuoted(int index) {
|
||||
return quotedRanges.stream().anyMatch(r -> r.contains(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Query implementation to exectue queries against JPA.
|
||||
* Query implementation to execute queries against JPA.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
@@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.jpa.repository.query.StringQuery.QuotationMap;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link QuotationMap}.
|
||||
|
||||
Reference in New Issue
Block a user