Skip to content

Commit 1bdffd6

Browse files
committed
Simplify QueryParameterSetter.
Replace capturing ErrorHandling with Spring's ErrorHandler and try/catch. Simplify QueryParameterSetterFactory.create(…) to no longer require DeclaredQuery.
1 parent 4f24560 commit 1bdffd6

File tree

9 files changed

+158
-117
lines changed

9 files changed

+158
-117
lines changed

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ private Query applyLockMode(Query query, JpaQueryMethod method) {
235235
return lockModeType == null ? query : query.setLockMode(lockModeType);
236236
}
237237

238-
protected ParameterBinder createBinder() {
239-
return ParameterBinderFactory.createBinder(getQueryMethod().getParameters());
238+
ParameterBinder createBinder() {
239+
return ParameterBinderFactory.createBinder(getQueryMethod().getParameters(), false);
240240
}
241241

242242
protected Query createQuery(JpaParametersParameterAccessor parameters) {

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.data.jpa.repository.query.QueryParameterSetter.ErrorHandling;
2222
import org.springframework.data.jpa.support.PageableUtils;
2323
import org.springframework.util.Assert;
24+
import org.springframework.util.ErrorHandler;
2425

2526
/**
2627
* {@link ParameterBinder} is used to bind method parameters to a {@link Query}. This is usually done whenever an
@@ -33,9 +34,7 @@
3334
* @author Jens Schauder
3435
* @author Yanming Zhou
3536
*/
36-
// TODO: Refactor, do not create a QueryParameterSetter for each parameter but capture strategies and bindings for more
37-
// efficient binding.
38-
public class ParameterBinder {
37+
class ParameterBinder {
3938

4039
static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to provide names for method parameters; Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters";
4140

@@ -82,18 +81,17 @@ public <T extends Query> T bind(T jpaQuery,
8281
}
8382

8483
public void bind(QueryParameterSetter.BindableQuery query, JpaParametersParameterAccessor accessor,
85-
ErrorHandling errorHandling) {
84+
ErrorHandler errorHandler) {
8685

8786
for (QueryParameterSetter setter : parameterSetters) {
88-
setter.setParameter(query, accessor, errorHandling);
87+
setter.setParameter(query, accessor, errorHandler);
8988
}
9089
}
9190

9291
/**
9392
* Binds the parameters to the given query and applies special parameter types (e.g. pagination).
9493
*
9594
* @param query must not be {@literal null}.
96-
* @param metadata must not be {@literal null}.
9795
* @param accessor must not be {@literal null}.
9896
*/
9997
Query bindAndPrepare(Query query,

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,15 @@ class ParameterBinderFactory {
3939
* otherwise.
4040
*
4141
* @param parameters method parameters that are available for binding, must not be {@literal null}.
42+
* @param preferNamedParameters
4243
* @return a {@link ParameterBinder} that can assign values for the method parameters to query parameters of a
4344
* {@link jakarta.persistence.Query}
4445
*/
45-
static ParameterBinder createBinder(JpaParameters parameters) {
46+
static ParameterBinder createBinder(JpaParameters parameters, boolean preferNamedParameters) {
4647

4748
Assert.notNull(parameters, "JpaParameters must not be null");
4849

49-
QueryParameterSetterFactory setterFactory = QueryParameterSetterFactory.basic(parameters);
50+
QueryParameterSetterFactory setterFactory = QueryParameterSetterFactory.basic(parameters, preferNamedParameters);
5051
List<ParameterBinding> bindings = getBindings(parameters);
5152

5253
return new ParameterBinder(parameters, createSetters(bindings, setterFactory));
@@ -95,15 +96,16 @@ static ParameterBinder createQueryAwareBinder(JpaParameters parameters, Declared
9596
QueryParameterSetterFactory expressionSetterFactory = QueryParameterSetterFactory.parsing(parser,
9697
evaluationContextProvider, parameters);
9798

98-
QueryParameterSetterFactory basicSetterFactory = QueryParameterSetterFactory.basic(parameters);
99+
QueryParameterSetterFactory basicSetterFactory = QueryParameterSetterFactory.basic(parameters,
100+
query.hasNamedParameter());
99101

100102
return new ParameterBinder(parameters, createSetters(bindings, query, expressionSetterFactory, basicSetterFactory),
101103
!query.usesPaging());
102104
}
103105

104106
static List<ParameterBinding> getBindings(JpaParameters parameters) {
105107

106-
List<ParameterBinding> result = new ArrayList<>();
108+
List<ParameterBinding> result = new ArrayList<>(parameters.getNumberOfParameters());
107109
int bindableParameterIndex = 0;
108110

109111
for (JpaParameter parameter : parameters) {
@@ -141,7 +143,7 @@ private static QueryParameterSetter createQueryParameterSetter(ParameterBinding
141143

142144
for (QueryParameterSetterFactory strategy : strategies) {
143145

144-
QueryParameterSetter setter = strategy.create(binding, declaredQuery);
146+
QueryParameterSetter setter = strategy.create(binding);
145147

146148
if (setter != null) {
147149
return setter;

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java

Lines changed: 100 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import org.springframework.lang.Nullable;
3434
import org.springframework.util.Assert;
35+
import org.springframework.util.ErrorHandler;
3536

3637
/**
3738
* The interface encapsulates the setting of query parameters which might use a significant number of variations of
@@ -43,119 +44,159 @@
4344
*/
4445
interface QueryParameterSetter {
4546

46-
void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandling errorHandling);
47-
4847
/** Noop implementation */
49-
QueryParameterSetter NOOP = (query, values, errorHandling) -> {};
48+
QueryParameterSetter NOOP = (query, values, errorHandler) -> {};
49+
50+
/**
51+
* Creates a new {@link QueryParameterSetter} for the given value extractor, JPA parameter and potentially the
52+
* temporal type.
53+
*
54+
* @param valueExtractor
55+
* @param parameter
56+
* @param temporalType
57+
* @return
58+
*/
59+
static QueryParameterSetter create(Function<JpaParametersParameterAccessor, Object> valueExtractor,
60+
Parameter<?> parameter, @Nullable TemporalType temporalType) {
61+
62+
return temporalType == null ? new NamedOrIndexedQueryParameterSetter(valueExtractor, parameter)
63+
: new TemporalParameterSetter(valueExtractor, parameter, temporalType);
64+
}
65+
66+
void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandler errorHandler);
5067

5168
/**
52-
* {@link QueryParameterSetter} for named or indexed parameters that might have a {@link TemporalType} specified.
69+
* {@link QueryParameterSetter} for named or indexed parameters.
5370
*/
5471
class NamedOrIndexedQueryParameterSetter implements QueryParameterSetter {
5572

5673
private final Function<JpaParametersParameterAccessor, Object> valueExtractor;
5774
private final Parameter<?> parameter;
58-
private final @Nullable TemporalType temporalType;
5975

6076
/**
6177
* @param valueExtractor must not be {@literal null}.
6278
* @param parameter must not be {@literal null}.
63-
* @param temporalType may be {@literal null}.
6479
*/
65-
NamedOrIndexedQueryParameterSetter(Function<JpaParametersParameterAccessor, Object> valueExtractor,
66-
Parameter<?> parameter, @Nullable TemporalType temporalType) {
80+
private NamedOrIndexedQueryParameterSetter(Function<JpaParametersParameterAccessor, Object> valueExtractor,
81+
Parameter<?> parameter) {
6782

6883
Assert.notNull(valueExtractor, "ValueExtractor must not be null");
6984

7085
this.valueExtractor = valueExtractor;
7186
this.parameter = parameter;
72-
this.temporalType = temporalType;
7387
}
7488

75-
// TODO: Refactor to use Spring's ErrorHandler instead of using a capturing ErrorHandling approach.
76-
@SuppressWarnings("unchecked")
7789
@Override
78-
public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor,
79-
ErrorHandling errorHandling) {
90+
public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandler errorHandler) {
8091

81-
if (temporalType != null) {
92+
Object value = valueExtractor.apply(accessor);
8293

83-
Object extractedValue = valueExtractor.apply(accessor);
84-
85-
Date value = (Date) accessor.potentiallyUnwrap(extractedValue);
94+
try {
95+
setParameter(query, value, errorHandler);
96+
} catch (RuntimeException e) {
97+
errorHandler.handleError(e);
98+
}
99+
}
86100

87-
// One would think we can simply use parameter to identify the parameter we want to set.
88-
// But that does not work with list valued parameters. At least Hibernate tries to bind them by name.
89-
// TODO: move to using setParameter(Parameter, value) when https://hibernate.atlassian.net/browse/HHH-11870 is
90-
// fixed.
101+
@SuppressWarnings("unchecked")
102+
private void setParameter(BindableQuery query, Object value, ErrorHandler errorHandler) {
91103

92-
if (parameter instanceof ParameterExpression) {
93-
errorHandling.execute(() -> query.setParameter((Parameter<Date>) parameter, value, temporalType));
94-
} else if (query.hasNamedParameters() && parameter.getName() != null) {
95-
errorHandling.execute(() -> query.setParameter(parameter.getName(), value, temporalType));
96-
} else {
104+
if (parameter instanceof ParameterExpression) {
105+
query.setParameter((Parameter<Object>) parameter, value);
106+
} else if (query.hasNamedParameters() && parameter.getName() != null) {
107+
query.setParameter(parameter.getName(), value);
97108

98-
Integer position = parameter.getPosition();
109+
} else {
99110

100-
if (position != null //
101-
&& (query.getParameters().size() >= parameter.getPosition() //
102-
|| query.registerExcessParameters() //
103-
|| errorHandling == LENIENT)) {
111+
Integer position = parameter.getPosition();
104112

105-
errorHandling.execute(() -> query.setParameter(parameter.getPosition(), value, temporalType));
106-
}
113+
if (position != null //
114+
&& (query.getParameters().size() >= position //
115+
|| errorHandler == LENIENT //
116+
|| query.registerExcessParameters())) {
117+
query.setParameter(position, value);
107118
}
119+
}
120+
}
121+
}
108122

109-
} else {
123+
/**
124+
* {@link QueryParameterSetter} for named or indexed parameters that have a {@link TemporalType} specified.
125+
*/
126+
class TemporalParameterSetter implements QueryParameterSetter {
110127

111-
Object value = valueExtractor.apply(accessor);
128+
private final Function<JpaParametersParameterAccessor, Object> valueExtractor;
129+
private final Parameter<?> parameter;
130+
private final TemporalType temporalType;
131+
132+
private TemporalParameterSetter(Function<JpaParametersParameterAccessor, Object> valueExtractor,
133+
Parameter<?> parameter, TemporalType temporalType) {
134+
this.valueExtractor = valueExtractor;
135+
this.parameter = parameter;
136+
this.temporalType = temporalType;
137+
}
138+
139+
@Override
140+
public void setParameter(BindableQuery query, JpaParametersParameterAccessor accessor, ErrorHandler errorHandler) {
141+
142+
Date value = (Date) accessor.potentiallyUnwrap(valueExtractor.apply(accessor));
143+
144+
try {
145+
setParameter(query, value, errorHandler);
146+
} catch (RuntimeException e) {
147+
errorHandler.handleError(e);
148+
}
149+
}
112150

113-
if (parameter instanceof ParameterExpression) {
114-
errorHandling.execute(() -> query.setParameter((Parameter<Object>) parameter, value));
115-
} else if (query.hasNamedParameters() && parameter.getName() != null) {
116-
errorHandling.execute(() -> query.setParameter(parameter.getName(), value));
151+
@SuppressWarnings("unchecked")
152+
private void setParameter(BindableQuery query, Date date, ErrorHandler errorHandler) {
153+
154+
// One would think we can simply use parameter to identify the parameter we want to set.
155+
// But that does not work with list valued parameters. At least Hibernate tries to bind them by name.
156+
// TODO: move to using setParameter(Parameter, value) when https://hibernate.atlassian.net/browse/HHH-11870 is
157+
// fixed.
117158

118-
} else {
159+
if (parameter instanceof ParameterExpression) {
160+
query.setParameter((Parameter<Date>) parameter, date, temporalType);
161+
} else if (query.hasNamedParameters() && parameter.getName() != null) {
162+
query.setParameter(parameter.getName(), date, temporalType);
163+
} else {
119164

120-
Integer position = parameter.getPosition();
165+
Integer position = parameter.getPosition();
121166

122-
if (position != null //
123-
&& (query.getParameters().size() >= position //
124-
|| errorHandling == LENIENT //
125-
|| query.registerExcessParameters())) {
126-
errorHandling.execute(() -> query.setParameter(position, value));
127-
}
167+
if (position != null //
168+
&& (query.getParameters().size() >= parameter.getPosition() //
169+
|| query.registerExcessParameters() //
170+
|| errorHandler == LENIENT)) {
171+
172+
query.setParameter(parameter.getPosition(), date, temporalType);
128173
}
129174
}
130175
}
131176
}
132177

133-
enum ErrorHandling {
178+
enum ErrorHandling implements ErrorHandler {
134179

135180
STRICT {
136181

137182
@Override
138-
public void execute(Runnable block) {
139-
block.run();
183+
public void handleError(Throwable t) {
184+
if (t instanceof RuntimeException rx) {
185+
throw rx;
186+
}
187+
throw new RuntimeException(t);
140188
}
141189
},
142190

143191
LENIENT {
144192

145193
@Override
146-
public void execute(Runnable block) {
147-
148-
try {
149-
block.run();
150-
} catch (RuntimeException rex) {
151-
LOG.info("Silently ignoring", rex);
152-
}
194+
public void handleError(Throwable t) {
195+
LOG.info("Silently ignoring", t);
153196
}
154197
};
155198

156199
private static final Log LOG = LogFactory.getLog(ErrorHandling.class);
157-
158-
abstract void execute(Runnable block);
159200
}
160201

161202
/**

0 commit comments

Comments
 (0)