21
21
import org .springframework .data .relational .core .mapping .RelationalPersistentEntity ;
22
22
import org .springframework .data .relational .core .mapping .RelationalPersistentProperty ;
23
23
import org .springframework .data .relational .core .sql .*;
24
+ import org .springframework .data .relational .core .sql .render .RenderNamingStrategy ;
24
25
import org .springframework .data .repository .query .parser .Part ;
25
26
import org .springframework .util .Assert ;
26
27
31
32
*/
32
33
class ConditionFactory {
33
34
private final MappingContext <? extends RelationalPersistentEntity <?>, ? extends RelationalPersistentProperty > mappingContext ;
35
+ private final RenderNamingStrategy namingStrategy ;
34
36
private final ParameterMetadataProvider parameterMetadataProvider ;
35
37
36
38
/**
37
39
* Creates new instance of this class with the given {@link MappingContext} and {@link ParameterMetadataProvider}.
38
40
*
39
41
* @param mappingContext mapping context (must not be {@literal null})
42
+ * @param namingStrategy naming strategy for SQL rendering (must not be {@literal null})
40
43
* @param parameterMetadataProvider parameter metadata provider (must not be {@literal null})
41
44
*/
42
45
ConditionFactory (MappingContext <? extends RelationalPersistentEntity <?>, ? extends RelationalPersistentProperty > mappingContext ,
46
+ RenderNamingStrategy namingStrategy ,
43
47
ParameterMetadataProvider parameterMetadataProvider ) {
44
48
Assert .notNull (mappingContext , "Mapping context must not be null" );
49
+ Assert .notNull (namingStrategy , "Render naming strategy must not be null" );
45
50
Assert .notNull (parameterMetadataProvider , "Parameter metadata provider must not be null" );
51
+
46
52
this .mappingContext = mappingContext ;
53
+ this .namingStrategy = namingStrategy ;
47
54
this .parameterMetadataProvider = parameterMetadataProvider ;
48
55
}
49
56
@@ -87,60 +94,64 @@ public Condition createCondition(Part part) {
87
94
BindMarker bindMarker = createBindMarker (parameterMetadataProvider .next (part ));
88
95
return Conditions .isLessOrEqualTo (pathExpression , bindMarker );
89
96
}
90
- case IS_NULL : {
91
- return Conditions .isNull (createPropertyPathExpression (part .getProperty ()));
92
- }
97
+ case IS_NULL :
93
98
case IS_NOT_NULL : {
94
- return Conditions .isNull (createPropertyPathExpression (part .getProperty ())).not ();
95
- }
96
- case IN : {
97
- Expression pathExpression = createPropertyPathExpression (part .getProperty ());
98
- BindMarker bindMarker = createBindMarker (parameterMetadataProvider .next (part ));
99
- return Conditions .in (pathExpression , bindMarker );
99
+ IsNull isNullCondition = Conditions .isNull (createPropertyPathExpression (part .getProperty ()));
100
+ return part .getType () == Part .Type .IS_NULL ? isNullCondition : isNullCondition .not ();
100
101
}
102
+ case IN :
101
103
case NOT_IN : {
102
- Expression pathExpression = createPropertyPathExpression (part .getProperty ());
104
+ Expression pathExpression = upperIfIgnoreCase ( part , createPropertyPathExpression (part .getProperty () ));
103
105
BindMarker bindMarker = createBindMarker (parameterMetadataProvider .next (part ));
104
- return Conditions .in (pathExpression , bindMarker ).not ();
106
+ In inCondition = Conditions .in (pathExpression , bindMarker );
107
+ return part .getType () == Part .Type .IN ? inCondition : inCondition .not ();
105
108
}
106
109
case STARTING_WITH :
107
110
case ENDING_WITH :
108
111
case CONTAINING :
109
- case LIKE : {
110
- Expression pathExpression = createPropertyPathExpression (part .getProperty ());
111
- BindMarker bindMarker = createBindMarker (parameterMetadataProvider .next (part ));
112
- return Conditions .like (pathExpression , bindMarker );
113
- }
112
+ case NOT_CONTAINING :
113
+ case LIKE :
114
114
case NOT_LIKE : {
115
115
Expression pathExpression = createPropertyPathExpression (part .getProperty ());
116
- BindMarker bindMarker = createBindMarker ( parameterMetadataProvider .next (part ) );
117
- return NotLike . create ( pathExpression , bindMarker );
118
- }
119
- case TRUE : {
120
- Expression pathExpression = createPropertyPathExpression ( part .getProperty ());
121
- // TODO: include factory method for '= TRUE' condition into spring-data-relational
122
- return Conditions .isEqual ( pathExpression , SQL . literalOf (( Object ) "TRUE" ) );
116
+ ParameterMetadata parameterMetadata = parameterMetadataProvider .next (part );
117
+ BindMarker bindMarker = createBindMarker ( parameterMetadata );
118
+ Expression lhs = upperIfIgnoreCase ( part , pathExpression );
119
+ Expression rhs = upperIfIgnoreCase ( part , bindMarker , parameterMetadata . getType ());
120
+ return part . getType () == Part . Type . NOT_LIKE || part .getType () == Part . Type . NOT_CONTAINING
121
+ ? NotLike . create ( lhs , rhs )
122
+ : Conditions .like ( lhs , rhs );
123
123
}
124
+ case TRUE :
124
125
case FALSE : {
125
126
Expression pathExpression = createPropertyPathExpression (part .getProperty ());
126
- // TODO: include factory method for '= FALSE' condition into spring-data-relational
127
- return Conditions .isEqual (pathExpression , SQL .literalOf ((Object ) "FALSE" ));
127
+ // TODO: include factory methods for '= TRUE/FALSE' conditions into spring-data-relational
128
+ return Conditions .isEqual (pathExpression ,
129
+ SQL .literalOf ((Object ) (part .getType () == Part .Type .TRUE ? "TRUE" : "FALSE" )));
128
130
}
129
131
case SIMPLE_PROPERTY : {
130
132
Expression pathExpression = createPropertyPathExpression (part .getProperty ());
131
133
ParameterMetadata parameterMetadata = parameterMetadataProvider .next (part );
132
134
if (parameterMetadata .isIsNullParameter ()) {
133
135
return Conditions .isNull (pathExpression );
134
136
}
135
- return Conditions .isEqual (pathExpression , createBindMarker (parameterMetadata ));
137
+
138
+ BindMarker bindMarker = createBindMarker (parameterMetadata );
139
+ Expression lhs = upperIfIgnoreCase (part , pathExpression );
140
+ Expression rhs = upperIfIgnoreCase (part , bindMarker , parameterMetadata .getType ());
141
+ return Conditions .isEqual (lhs , rhs );
136
142
}
137
143
case NEGATING_SIMPLE_PROPERTY : {
138
144
Expression pathExpression = createPropertyPathExpression (part .getProperty ());
139
145
ParameterMetadata parameterMetadata = parameterMetadataProvider .next (part );
140
- return Conditions .isEqual (pathExpression , createBindMarker (parameterMetadata )).not ();
146
+ BindMarker bindMarker = createBindMarker (parameterMetadata );
147
+ Expression lhs = upperIfIgnoreCase (part , pathExpression );
148
+ Expression rhs = upperIfIgnoreCase (part , bindMarker , parameterMetadata .getType ());
149
+ return Conditions .isEqual (lhs , rhs ).not ();
141
150
}
151
+ default :
152
+ throw new UnsupportedOperationException ("Creating conditions for type " + type + " is unsupported" );
142
153
}
143
- throw new UnsupportedOperationException ( "Creating conditions for type " + type + " is unsupported" );
154
+
144
155
}
145
156
146
157
@ NotNull
@@ -160,6 +171,91 @@ private BindMarker createBindMarker(ParameterMetadata parameterMetadata) {
160
171
return SQL .bindMarker ();
161
172
}
162
173
174
+ /**
175
+ * Applies an {@code UPPERCASE} conversion to the given {@link Expression} in case the underlying {@link Part}
176
+ * requires ignoring case.
177
+ *
178
+ * @param part method name part (must not be {@literal null})
179
+ * @param expression expression to be uppercased (must not be {@literal null})
180
+ * @return uppercased expression or original expression if ignoring case is not strictly required
181
+ */
182
+ private Expression upperIfIgnoreCase (Part part , Expression expression ) {
183
+ return upperIfIgnoreCase (part , expression , part .getProperty ().getType ());
184
+ }
185
+
186
+ /**
187
+ * Applies an {@code UPPERCASE} conversion to the given {@link Expression} in case the underlying {@link Part}
188
+ * requires ignoring case.
189
+ *
190
+ * @param part method name part (must not be {@literal null})
191
+ * @param expression expression to be uppercased (must not be {@literal null})
192
+ * @param expressionType type of the given expression (must not be {@literal null})
193
+ * @return uppercased expression or original expression if ignoring case is not strictly required
194
+ */
195
+ private Expression upperIfIgnoreCase (Part part , Expression expression , Class <?> expressionType ) {
196
+ switch (part .shouldIgnoreCase ()) {
197
+ case ALWAYS :
198
+ Assert .state (canUpperCase (expressionType ), "Unable to ignore case of " + expressionType .getName ()
199
+ + " type, the property '" + part .getProperty ().getSegment () + "' must reference a string" );
200
+ return new Upper (expression );
201
+ case WHEN_POSSIBLE :
202
+ if (canUpperCase (expressionType )) {
203
+ return new Upper (expression );
204
+ }
205
+ case NEVER :
206
+ default :
207
+ return expression ;
208
+ }
209
+ }
210
+
211
+ private boolean canUpperCase (Class <?> expressionType ) {
212
+ return expressionType == String .class ;
213
+ }
214
+
215
+ // TODO: include support of functions in WHERE conditions into spring-data-relational
216
+ /**
217
+ * Models the ANSI SQL {@code UPPER} function.
218
+ */
219
+ private class Upper implements Expression {
220
+ private Literal <Object > delegate ;
221
+
222
+ /**
223
+ * Creates new instance of this class with the given expression. Only expressions of type {@link Column} and
224
+ * {@link BindMarker} are supported.
225
+ *
226
+ * @param expression expression to be uppercased (must not be {@literal null})
227
+ */
228
+ private Upper (Expression expression ) {
229
+ Assert .notNull (expression , "Expression must not be null!" );
230
+ String functionArgument ;
231
+ if (expression instanceof BindMarker ) {
232
+ functionArgument = expression instanceof Named ? ((Named ) expression ).getName () : expression .toString ();
233
+ } else if (expression instanceof Column ) {
234
+ functionArgument = "" ;
235
+ Table table = ((Column ) expression ).getTable ();
236
+ if (table != null ) {
237
+ functionArgument = namingStrategy .getReferenceName (table ) + "." ;
238
+ }
239
+ functionArgument += namingStrategy .getReferenceName ((Column ) expression );
240
+ } else {
241
+ throw new IllegalArgumentException ("Unable to ignore case expression of type "
242
+ + expression .getClass ().getName () + ". Only " + Column .class .getName () + " and "
243
+ + BindMarker .class .getName () + " types are supported" );
244
+ }
245
+ this .delegate = SQL .literalOf ((Object ) ("UPPER(" + functionArgument + ")" ));
246
+ }
247
+
248
+ @ Override
249
+ public void visit (Visitor visitor ) {
250
+ delegate .visit (visitor );
251
+ }
252
+
253
+ @ Override
254
+ public String toString () {
255
+ return delegate .toString ();
256
+ }
257
+ }
258
+
163
259
// TODO: include support of NOT LIKE operator into spring-data-relational
164
260
/**
165
261
* Negated LIKE {@link Condition} comparing two {@link Expression}s.
@@ -192,17 +288,9 @@ public void visit(Visitor visitor) {
192
288
delegate .visit (visitor );
193
289
}
194
290
195
- public Expression getLeft () {
196
- return delegate .getLeft ();
197
- }
198
-
199
- public Expression getRight () {
200
- return delegate .getRight ();
201
- }
202
-
203
291
@ Override
204
292
public String toString () {
205
- return getLeft () .toString () + " NOT LIKE " + getRight ();
293
+ return delegate .toString ();
206
294
}
207
295
}
208
296
}
0 commit comments