Skip to content

Commit 4e36b16

Browse files
committed
Polishing.
Use ObjectUtils instead of Enum.valueOf(…), move class presence check into field. Allow force-selection of JSQLParser. Add more tests. See #2989 Original pull request: #3623
1 parent b1c349a commit 4e36b16

File tree

4 files changed

+84
-39
lines changed

4 files changed

+84
-39
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.springframework.lang.Nullable;
2222

2323
/**
24-
* The implementation of {@link QueryEnhancer} using {@link QueryUtils}.
24+
* The implementation of the Regex-based {@link QueryEnhancer} using {@link QueryUtils}.
2525
*
2626
* @author Diego Krupitza
2727
* @since 2.7.0

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

+41-22
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import org.apache.commons.logging.LogFactory;
2020
import org.springframework.core.SpringProperties;
2121
import org.springframework.data.jpa.provider.PersistenceProvider;
22-
import org.springframework.lang.Nullable;
2322
import org.springframework.util.ClassUtils;
23+
import org.springframework.util.ObjectUtils;
2424
import org.springframework.util.StringUtils;
2525

2626
/**
@@ -39,7 +39,7 @@ public final class QueryEnhancerFactory {
3939

4040
static {
4141

42-
NATIVE_QUERY_ENHANCER = NativeQueryEnhancer.select(QueryEnhancerFactory.class.getClassLoader());
42+
NATIVE_QUERY_ENHANCER = NativeQueryEnhancer.select();
4343

4444
if (PersistenceProvider.ECLIPSELINK.isPresent()) {
4545
LOG.info("EclipseLink is in classpath; If applicable, EQL parser will be used.");
@@ -81,47 +81,66 @@ public static QueryEnhancer forQuery(DeclaredQuery query) {
8181
*/
8282
private static QueryEnhancer getNativeQueryEnhancer(DeclaredQuery query) {
8383

84-
if (NATIVE_QUERY_ENHANCER.equals(NativeQueryEnhancer.JSQL)) {
84+
if (NATIVE_QUERY_ENHANCER.equals(NativeQueryEnhancer.JSQLPARSER)) {
8585
return new JSqlParserQueryEnhancer(query);
8686
}
87+
8788
return new DefaultQueryEnhancer(query);
8889
}
8990

9091
/**
91-
* Possible choices for the {@link #NATIVE_PARSER_PROPERTY}. Read current selection via {@link #select(ClassLoader)}.
92+
* Possible choices for the {@link #NATIVE_PARSER_PROPERTY}. Resolve the parser through {@link #select()}.
93+
*
94+
* @since 3.3.5
9295
*/
9396
enum NativeQueryEnhancer {
9497

95-
AUTO, DEFAULT, JSQL;
98+
AUTO, REGEX, JSQLPARSER;
9699

97100
static final String NATIVE_PARSER_PROPERTY = "spring.data.jpa.query.native.parser";
98101

99-
private static NativeQueryEnhancer from(@Nullable String name) {
100-
if (!StringUtils.hasText(name)) {
101-
return AUTO;
102-
}
103-
return NativeQueryEnhancer.valueOf(name.toUpperCase());
104-
}
102+
static final boolean JSQLPARSER_PRESENT = ClassUtils.isPresent("net.sf.jsqlparser.parser.JSqlParser", null);
105103

106104
/**
107-
* @param classLoader ClassLoader to look up available libraries.
108-
* @return the current selection considering classpath avialability and user selection via
105+
* @return the current selection considering classpath availability and user selection via
109106
* {@link #NATIVE_PARSER_PROPERTY}.
110107
*/
111-
static NativeQueryEnhancer select(ClassLoader classLoader) {
108+
static NativeQueryEnhancer select() {
112109

113-
if (!ClassUtils.isPresent("net.sf.jsqlparser.parser.JSqlParser", classLoader)) {
114-
return NativeQueryEnhancer.DEFAULT;
110+
NativeQueryEnhancer selected = resolve();
111+
112+
if (selected.equals(NativeQueryEnhancer.JSQLPARSER)) {
113+
LOG.info("User choice: Using JSqlParser");
114+
return NativeQueryEnhancer.JSQLPARSER;
115+
}
116+
117+
if (selected.equals(NativeQueryEnhancer.REGEX)) {
118+
LOG.info("Using Regex QueryEnhancer");
119+
return NativeQueryEnhancer.REGEX;
115120
}
116121

117-
NativeQueryEnhancer selected = NativeQueryEnhancer.from(SpringProperties.getProperty(NATIVE_PARSER_PROPERTY));
118-
if (selected.equals(NativeQueryEnhancer.AUTO) || selected.equals(NativeQueryEnhancer.JSQL)) {
119-
LOG.info("JSqlParser is in classpath; If applicable, JSqlParser will be used.");
120-
return NativeQueryEnhancer.JSQL;
122+
if (!JSQLPARSER_PRESENT) {
123+
return NativeQueryEnhancer.REGEX;
124+
}
125+
126+
LOG.info("JSqlParser is in classpath; If applicable, JSqlParser will be used.");
127+
return NativeQueryEnhancer.JSQLPARSER;
128+
}
129+
130+
/**
131+
* Resolve {@link NativeQueryEnhancer} from {@link SpringProperties}.
132+
*
133+
* @return the {@link NativeQueryEnhancer} constant.
134+
*/
135+
private static NativeQueryEnhancer resolve() {
136+
137+
String name = SpringProperties.getProperty(NATIVE_PARSER_PROPERTY);
138+
139+
if (StringUtils.hasText(name)) {
140+
return ObjectUtils.caseInsensitiveValueOf(NativeQueryEnhancer.values(), name);
121141
}
122142

123-
LOG.info("JSqlParser is in classpath but won't be used due to user choice.");
124-
return selected;
143+
return AUTO;
125144
}
126145
}
127146

spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/query/QueryEnhancerFactoryUnitTests.java

+35-15
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18-
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.assertj.core.api.Assertions.*;
1919

2020
import java.util.stream.Stream;
2121

2222
import org.junit.jupiter.api.Test;
2323
import org.junit.jupiter.params.ParameterizedTest;
2424
import org.junit.jupiter.params.provider.Arguments;
2525
import org.junit.jupiter.params.provider.MethodSource;
26+
2627
import org.springframework.data.jpa.repository.query.QueryEnhancerFactory.NativeQueryEnhancer;
2728
import org.springframework.data.jpa.util.ClassPathExclusions;
2829
import org.springframework.lang.Nullable;
@@ -33,6 +34,7 @@
3334
* @author Diego Krupitza
3435
* @author Greg Turnquist
3536
* @author Christoph Strobl
37+
* @author Mark Paluch
3638
*/
3739
class QueryEnhancerFactoryUnitTests {
3840

@@ -66,26 +68,48 @@ void createsJSqlImplementationForNativeQuery() {
6668
@MethodSource("nativeEnhancerSelectionArgs")
6769
void createsNativeImplementationAccordingToUserChoice(@Nullable String selection, NativeQueryEnhancer enhancer) {
6870

71+
assertThat(NativeQueryEnhancer.JSQLPARSER_PRESENT).isTrue();
72+
6973
withSystemProperty(NativeQueryEnhancer.NATIVE_PARSER_PROPERTY, selection, () -> {
70-
assertThat(NativeQueryEnhancer.select(this.getClass().getClassLoader())).isEqualTo(enhancer);
74+
assertThat(NativeQueryEnhancer.select()).isEqualTo(enhancer);
7175
});
7276
}
7377

74-
@Test // GH-2989
78+
static Stream<Arguments> nativeEnhancerSelectionArgs() {
79+
return Stream.of(Arguments.of(null, NativeQueryEnhancer.JSQLPARSER), //
80+
Arguments.of("", NativeQueryEnhancer.JSQLPARSER), //
81+
Arguments.of("auto", NativeQueryEnhancer.JSQLPARSER), //
82+
Arguments.of("regex", NativeQueryEnhancer.REGEX), //
83+
Arguments.of("jsqlparser", NativeQueryEnhancer.JSQLPARSER));
84+
}
85+
86+
@ParameterizedTest // GH-2989
87+
@MethodSource("nativeEnhancerExclusionSelectionArgs")
7588
@ClassPathExclusions(packages = { "net.sf.jsqlparser.parser" })
76-
void selectedDefaultImplementationIfJsqlNotAvailable() {
89+
void createsNativeImplementationAccordingWithoutJsqlParserToUserChoice(@Nullable String selection,
90+
NativeQueryEnhancer enhancer) {
91+
92+
assertThat(NativeQueryEnhancer.JSQLPARSER_PRESENT).isFalse();
7793

78-
assertThat(assertThat(NativeQueryEnhancer.select(this.getClass().getClassLoader()))
79-
.isEqualTo(NativeQueryEnhancer.DEFAULT));
94+
withSystemProperty(NativeQueryEnhancer.NATIVE_PARSER_PROPERTY, selection, () -> {
95+
assertThat(NativeQueryEnhancer.select()).isEqualTo(enhancer);
96+
});
97+
}
98+
99+
static Stream<Arguments> nativeEnhancerExclusionSelectionArgs() {
100+
return Stream.of(Arguments.of(null, NativeQueryEnhancer.REGEX), //
101+
Arguments.of("", NativeQueryEnhancer.REGEX), //
102+
Arguments.of("auto", NativeQueryEnhancer.REGEX), //
103+
Arguments.of("regex", NativeQueryEnhancer.REGEX), //
104+
Arguments.of("jsqlparser", NativeQueryEnhancer.JSQLPARSER));
80105
}
81106

82107
@Test // GH-2989
83108
@ClassPathExclusions(packages = { "net.sf.jsqlparser.parser" })
84-
void selectedDefaultImplementationIfJsqlNotAvailableEvenIfExplicitlyStated/* or should we raise an error? */() {
109+
void selectedDefaultImplementationIfJsqlNotAvailable() {
85110

86-
withSystemProperty(NativeQueryEnhancer.NATIVE_PARSER_PROPERTY, "jsql", () -> {
87-
assertThat(NativeQueryEnhancer.select(this.getClass().getClassLoader())).isEqualTo(NativeQueryEnhancer.DEFAULT);
88-
});
111+
assertThat(NativeQueryEnhancer.JSQLPARSER_PRESENT).isFalse();
112+
assertThat(NativeQueryEnhancer.select()).isEqualTo(NativeQueryEnhancer.REGEX);
89113
}
90114

91115
void withSystemProperty(String property, @Nullable String value, Runnable exeution) {
@@ -108,9 +132,5 @@ void withSystemProperty(String property, @Nullable String value, Runnable exeuti
108132

109133
}
110134

111-
static Stream<Arguments> nativeEnhancerSelectionArgs() {
112-
return Stream.of(Arguments.of(null, NativeQueryEnhancer.JSQL), Arguments.of("", NativeQueryEnhancer.JSQL),
113-
Arguments.of("auto", NativeQueryEnhancer.JSQL), Arguments.of("default", NativeQueryEnhancer.DEFAULT),
114-
Arguments.of("jsql", NativeQueryEnhancer.JSQL));
115-
}
135+
116136
}

src/main/antora/modules/ROOT/pages/jpa/query-methods.adoc

+7-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,13 @@ public interface UserRepository extends JpaRepository<User, Long> {
307307

308308
[TIP]
309309
====
310-
It is possible to disable usage of `JSqlParser` for parsing natvie queries although it is available in classpath by setting `spring.data.jpa.query.native.parser=default` via the `spring.properties` file or a system property.
310+
It is possible to disable usage of `JSqlParser` for parsing native queries although it is available on the classpath by setting `spring.data.jpa.query.native.parser=regex` via the `spring.properties` file or a system property.
311+
312+
Valid values are (case-insensitive):
313+
314+
* `auto` (default, automatic selection)
315+
* `regex` (Use the builtin regex-based Query Enhancer)
316+
* `jsqlparser` (Use JSqlParser)
311317
====
312318

313319
A similar approach also works with named native queries, by adding the `.count` suffix to a copy of your query. You probably need to register a result set mapping for your count query, though.

0 commit comments

Comments
 (0)