27
27
import org .springframework .data .relational .core .sql .SqlIdentifier ;
28
28
import org .springframework .data .util .Lazy ;
29
29
import org .springframework .data .util .Optionals ;
30
+ import org .springframework .expression .EvaluationException ;
31
+ import org .springframework .expression .Expression ;
32
+ import org .springframework .expression .common .TemplateParserContext ;
33
+ import org .springframework .expression .spel .standard .SpelExpressionParser ;
34
+ import org .springframework .expression .spel .support .StandardEvaluationContext ;
30
35
import org .springframework .util .Assert ;
31
36
import org .springframework .util .StringUtils ;
32
37
37
42
* @author Greg Turnquist
38
43
* @author Florian Lüdiger
39
44
* @author Bastian Wilhelm
45
+ * @author Kurt Niemi
40
46
*/
41
47
public class BasicRelationalPersistentProperty extends AnnotationBasedPersistentProperty <RelationalPersistentProperty >
42
48
implements RelationalPersistentProperty {
@@ -48,6 +54,7 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
48
54
private final Lazy <String > embeddedPrefix ;
49
55
private final NamingStrategy namingStrategy ;
50
56
private boolean forceQuote = true ;
57
+ private SpelExpressionResultSanitizer sanitizer ;
51
58
52
59
/**
53
60
* Creates a new {@link BasicRelationalPersistentProperty}.
@@ -57,12 +64,12 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
57
64
* @param simpleTypeHolder must not be {@literal null}.
58
65
* @param context must not be {@literal null}
59
66
* @since 2.0, use
60
- * {@link #BasicRelationalPersistentProperty(Property, PersistentEntity, SimpleTypeHolder, NamingStrategy)}.
67
+ * {@link #BasicRelationalPersistentProperty(Property, PersistentEntity, SimpleTypeHolder, NamingStrategy, SpelExpressionResultSanitizer )}.
61
68
*/
62
- @ Deprecated
69
+ @ Deprecated ( since = "3.1" , forRemoval = true )
63
70
public BasicRelationalPersistentProperty (Property property , PersistentEntity <?, RelationalPersistentProperty > owner ,
64
71
SimpleTypeHolder simpleTypeHolder , RelationalMappingContext context ) {
65
- this (property , owner , simpleTypeHolder , context .getNamingStrategy ());
72
+ this (property , owner , simpleTypeHolder , context .getNamingStrategy (), context . getSpelExpressionResultSanitizer () );
66
73
}
67
74
68
75
/**
@@ -74,11 +81,34 @@ public BasicRelationalPersistentProperty(Property property, PersistentEntity<?,
74
81
* @param namingStrategy must not be {@literal null}
75
82
* @since 2.0
76
83
*/
77
- public BasicRelationalPersistentProperty (Property property , PersistentEntity <?, RelationalPersistentProperty > owner ,
78
- SimpleTypeHolder simpleTypeHolder , NamingStrategy namingStrategy ) {
84
+ @ Deprecated (since = "3.1" , forRemoval = true )
85
+ public BasicRelationalPersistentProperty (Property property ,
86
+ PersistentEntity <?, RelationalPersistentProperty > owner ,
87
+ SimpleTypeHolder simpleTypeHolder ,
88
+ NamingStrategy namingStrategy ) {
89
+ // ::TODO:: Where should I put the code that has the default sanitizer implementation??
90
+ this (property , owner , simpleTypeHolder , namingStrategy , null );
91
+ }
92
+
93
+ /**
94
+ * Creates a new {@link BasicRelationalPersistentProperty}.
95
+ *
96
+ * @param property must not be {@literal null}.
97
+ * @param owner must not be {@literal null}.
98
+ * @param simpleTypeHolder must not be {@literal null}.
99
+ * @param namingStrategy must not be {@literal null}
100
+ * @param sanitizer must not be {@literal null}
101
+ * @since 2.0
102
+ */
103
+ public BasicRelationalPersistentProperty (Property property ,
104
+ PersistentEntity <?, RelationalPersistentProperty > owner ,
105
+ SimpleTypeHolder simpleTypeHolder ,
106
+ NamingStrategy namingStrategy ,
107
+ SpelExpressionResultSanitizer sanitizer ) {
79
108
80
109
super (property , owner , simpleTypeHolder );
81
110
this .namingStrategy = namingStrategy ;
111
+ this .sanitizer = sanitizer ;
82
112
83
113
Assert .notNull (namingStrategy , "NamingStrategy must not be null" );
84
114
@@ -90,6 +120,7 @@ public BasicRelationalPersistentProperty(Property property, PersistentEntity<?,
90
120
91
121
this .columnName = Lazy .of (() -> Optional .ofNullable (findAnnotation (Column .class )) //
92
122
.map (Column ::value ) //
123
+ .map (this ::applySpelExpression )
93
124
.filter (StringUtils ::hasText ) //
94
125
.map (this ::createSqlIdentifier ) //
95
126
.orElseGet (() -> createDerivedSqlIdentifier (namingStrategy .getColumnName (this ))));
@@ -110,6 +141,47 @@ public BasicRelationalPersistentProperty(Property property, PersistentEntity<?,
110
141
.orElseGet (() -> createDerivedSqlIdentifier (namingStrategy .getKeyColumn (this ))));
111
142
}
112
143
144
+ // ::TODO:: Discuss where we want this common code for Table and Columns
145
+ private StandardEvaluationContext evalContext = new StandardEvaluationContext ();
146
+ private SpelExpressionParser parser = new SpelExpressionParser ();
147
+ private TemplateParserContext templateParserContext = new TemplateParserContext ();
148
+ private boolean isSpellExpression (String expression ) {
149
+
150
+ String trimmedExpression = expression .trim ();
151
+ if (trimmedExpression .startsWith (templateParserContext .getExpressionPrefix ()) &&
152
+ trimmedExpression .endsWith (templateParserContext .getExpressionSuffix ())) {
153
+ return true ;
154
+ }
155
+
156
+ return false ;
157
+ }
158
+
159
+ private String applySpelExpression (String expression ) throws EvaluationException {
160
+
161
+ Assert .notNull (expression , "Expression must not be null." );
162
+
163
+ // Only apply logic if we have the prefixes/suffixes required for a Spel expression as firstly
164
+ // there is nothing to evaluate (i.e. whatever literal passed in is returned as-is) and more
165
+ // importantly we do not want to perform any sanitization logic.
166
+ if (!isSpellExpression (expression )) {
167
+ return expression ;
168
+ }
169
+
170
+ Expression expr = parser .parseExpression (expression , templateParserContext );
171
+ String result = expr .getValue (evalContext , String .class );
172
+
173
+ // Normally an exception is thrown by the Spel parser on invalid syntax/errors but this will provide
174
+ // a consistent experience for any issues with Spel parsing.
175
+ if (result == null ) {
176
+ throw new EvaluationException ("Spel Parsing of expression \" " + expression + "\" failed." );
177
+ }
178
+
179
+ String sanitizedResult = sanitizer .sanitize (result );
180
+
181
+ return sanitizedResult ;
182
+ }
183
+
184
+
113
185
private SqlIdentifier createSqlIdentifier (String name ) {
114
186
return isForceQuote () ? SqlIdentifier .quoted (name ) : SqlIdentifier .unquoted (name );
115
187
}
0 commit comments