Skip to content

Commit 810c62b

Browse files
schaudermp911de
authored andcommitted
DATAJDBC-340 - Using new SQL generation DSL.
SqlGenerator basically got rewritten using the new SQL rendering API. The SQL rendering API got some minor improvements in the process. The whole generation process is now based on paths instead of properties which makes the actual logic of the construction much more obvious. Original pull request: #147.
1 parent 22dd364 commit 810c62b

34 files changed

+1886
-884
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc.core;
17+
18+
import java.util.Objects;
19+
20+
import org.springframework.data.mapping.PersistentPropertyPath;
21+
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
22+
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
23+
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
24+
import org.springframework.lang.Nullable;
25+
import org.springframework.util.Assert;
26+
27+
/**
28+
* A wrapper around a {@link org.springframework.data.mapping.PersistentPropertyPath} for making common operations
29+
* available used in SQL generation.
30+
*
31+
* @author Jens Schauder
32+
* @since 1.1
33+
*/
34+
class PersistentPropertyPathExtension {
35+
36+
private final RelationalPersistentEntity<?> entity;
37+
private final PersistentPropertyPath<RelationalPersistentProperty> path;
38+
private final RelationalMappingContext context;
39+
40+
PersistentPropertyPathExtension(RelationalMappingContext context, RelationalPersistentEntity<?> entity) {
41+
42+
Assert.notNull(context, "Context must not be null.");
43+
Assert.notNull(entity, "Entity must not be null.");
44+
45+
this.context = context;
46+
this.entity = entity;
47+
this.path = null;
48+
}
49+
50+
PersistentPropertyPathExtension(RelationalMappingContext context,
51+
PersistentPropertyPath<RelationalPersistentProperty> path) {
52+
53+
Assert.notNull(context, "Context must not be null.");
54+
Assert.notNull(path, "Path must not be null.");
55+
Assert.isTrue(!path.isEmpty(), "Path must not be empty.");
56+
57+
this.context = context;
58+
this.entity = Objects.requireNonNull(path.getBaseProperty()).getOwner();
59+
this.path = path;
60+
}
61+
62+
/**
63+
* Returns {@literal true} exactly when the path is non empty and the leaf property an embedded one.
64+
*
65+
* @return if the leaf property is embedded.
66+
*/
67+
boolean isEmbedded() {
68+
return path != null && path.getRequiredLeafProperty().isEmbedded();
69+
}
70+
71+
/**
72+
* Returns the path that has the same beginning but is one segment shorter than this path.
73+
*
74+
* @return the parent path. Guaranteed to be not {@literal null}.
75+
* @throws IllegalStateException when called on an empty path.
76+
*/
77+
PersistentPropertyPathExtension getParentPath() {
78+
79+
if (path == null) {
80+
throw new IllegalStateException("The parent path of a root path is not defined.");
81+
}
82+
83+
if (path.getLength() == 1) {
84+
return new PersistentPropertyPathExtension(context, entity);
85+
}
86+
87+
return new PersistentPropertyPathExtension(context, path.getParentPath());
88+
}
89+
90+
/**
91+
* Returns {@literal true} if there are multiple values for this path, i.e. if the path contains at least one element
92+
* that is a collection and array or a map.
93+
*
94+
* @return {@literal true} if the path contains a multivalued element.
95+
*/
96+
boolean isMultiValued() {
97+
98+
return path != null && //
99+
(path.getRequiredLeafProperty().isCollectionLike() //
100+
|| path.getRequiredLeafProperty().isQualified() //
101+
|| getParentPath().isMultiValued() //
102+
);
103+
}
104+
105+
/**
106+
* The {@link RelationalPersistentEntity} associated with the leaf of this path.
107+
*
108+
* @return Might return {@literal null} when called on a path that does not represent an entity.
109+
*/
110+
@Nullable
111+
RelationalPersistentEntity<?> getLeafEntity() {
112+
return path == null ? entity : context.getPersistentEntity(path.getRequiredLeafProperty().getActualType());
113+
}
114+
115+
/**
116+
* @return {@literal true} when this is an empty path or the path references an entity.
117+
*/
118+
boolean isEntity() {
119+
return path == null || path.getRequiredLeafProperty().isEntity();
120+
}
121+
122+
/**
123+
* @return {@literal true} when this is references a {@link java.util.List} or {@link java.util.Map}.
124+
*/
125+
boolean isQualified() {
126+
return path != null && path.getRequiredLeafProperty().isQualified();
127+
}
128+
129+
/**
130+
* @return {@literal true} when this is references a {@link java.util.Collection} or an array.
131+
*/
132+
boolean isCollectionLike() {
133+
return path != null && path.getRequiredLeafProperty().isCollectionLike();
134+
}
135+
136+
/**
137+
* The name of the column used to reference the id in the parent table.
138+
*
139+
* @throws IllegalStateException when called on an empty path.
140+
*/
141+
String getReverseColumnName() {
142+
143+
return path.getRequiredLeafProperty().getReverseColumnName();
144+
}
145+
146+
/**
147+
* The alias used in select for the column used to reference the id in the parent table.
148+
*
149+
* @throws IllegalStateException when called on an empty path.
150+
*/
151+
String getReverseColumnNameAlias() {
152+
153+
return prefixWithTableAlias(getReverseColumnName());
154+
}
155+
156+
/**
157+
* The name of the column used to represent this property in the database.
158+
*
159+
* @throws IllegalStateException when called on an empty path.
160+
*/
161+
String getColumnName() {
162+
163+
return assembleColumnName(path.getRequiredLeafProperty().getColumnName());
164+
}
165+
166+
/**
167+
* The alias for the column used to represent this property in the database.
168+
*
169+
* @throws IllegalStateException when called on an empty path.
170+
*/
171+
String getColumnAlias() {
172+
173+
return prefixWithTableAlias(getColumnName());
174+
}
175+
176+
/**
177+
* @return {@literal true} if this path represents an entity which has an Id attribute.
178+
*/
179+
boolean hasIdProperty() {
180+
181+
RelationalPersistentEntity<?> leafEntity = getLeafEntity();
182+
return leafEntity != null && leafEntity.hasIdProperty();
183+
}
184+
185+
PersistentPropertyPathExtension getIdDefiningParentPath() {
186+
187+
PersistentPropertyPathExtension parent = getParentPath();
188+
if (parent.path == null) {
189+
return parent;
190+
}
191+
if (parent.isEmbedded()) {
192+
return getParentPath().getIdDefiningParentPath();
193+
}
194+
return parent;
195+
}
196+
197+
/**
198+
* The name of the table this path is tied to or of the longest ancestor path that is actually tied to a table.
199+
*
200+
* @return the name of the table. Guaranteed to be not {@literal null}.
201+
*/
202+
String getTableName() {
203+
return getTableOwningAncestor().getRequiredLeafEntity().getTableName();
204+
}
205+
206+
/**
207+
* The alias used for the table on which this path is based.
208+
*
209+
* @return a table alias, {@literal null} if the table owning path is the empty path.
210+
*/
211+
@Nullable
212+
String getTableAlias() {
213+
214+
PersistentPropertyPathExtension tableOwner = getTableOwningAncestor();
215+
if (tableOwner.path == null) {
216+
return null;
217+
}
218+
219+
return tableOwner.assembleTableAlias();
220+
}
221+
222+
/**
223+
* The column name of the id column of the ancestor path that represents an actual table.
224+
*/
225+
String getIdColumnName() {
226+
return getTableOwningAncestor().getRequiredLeafEntity().getIdColumn();
227+
}
228+
229+
/**
230+
* If the table owning ancestor has an id the column name of that id property is returned. Otherwise the reverse
231+
* column is returned.
232+
*/
233+
String getEffectiveIdColumnName() {
234+
235+
PersistentPropertyPathExtension owner = getTableOwningAncestor();
236+
return owner.path == null ? owner.getRequiredLeafEntity().getIdColumn() : owner.getReverseColumnName();
237+
}
238+
239+
/**
240+
* The length of the path.
241+
*/
242+
int getLength() {
243+
return path == null ? 0 : path.getLength();
244+
}
245+
246+
/**
247+
* Finds and returns the longest path with ich identical or an ancestor to the current path and maps directly to a
248+
* table.
249+
*
250+
* @return a path. Guaranteed to be not {@literal null}.
251+
*/
252+
private PersistentPropertyPathExtension getTableOwningAncestor() {
253+
254+
if (isEntity() && !isEmbedded()) {
255+
return this;
256+
}
257+
return getParentPath().getTableOwningAncestor();
258+
}
259+
260+
private String assembleTableAlias() {
261+
262+
RelationalPersistentProperty leafProperty = path.getRequiredLeafProperty();
263+
String prefix = isEmbedded() ? leafProperty.getEmbeddedPrefix() : leafProperty.getName();
264+
265+
if (path.getLength() == 1) {
266+
Assert.notNull(prefix, "Prefix mus not be null.");
267+
return prefix;
268+
}
269+
270+
PersistentPropertyPathExtension parentPath = getParentPath();
271+
return parentPath.isEmbedded() ? parentPath.assembleTableAlias() + prefix
272+
: parentPath.assembleTableAlias() + "_" + prefix;
273+
}
274+
275+
private String assembleColumnName(String suffix) {
276+
277+
if (path.getLength() <= 1) {
278+
return suffix;
279+
}
280+
PersistentPropertyPath<RelationalPersistentProperty> parentPath = path.getParentPath();
281+
RelationalPersistentProperty parentLeaf = parentPath.getRequiredLeafProperty();
282+
if (!parentLeaf.isEmbedded()) {
283+
return suffix;
284+
}
285+
String embeddedPrefix = parentLeaf.getEmbeddedPrefix();
286+
return getParentPath().assembleColumnName(embeddedPrefix + suffix);
287+
}
288+
289+
private RelationalPersistentEntity<?> getRequiredLeafEntity() {
290+
return path == null ? entity : context.getRequiredPersistentEntity(path.getRequiredLeafProperty().getActualType());
291+
}
292+
293+
294+
private String prefixWithTableAlias(String columnName) {
295+
296+
String tableAlias = getTableAlias();
297+
return tableAlias == null ? columnName : tableAlias + "_" + columnName;
298+
}
299+
300+
}

0 commit comments

Comments
 (0)