Skip to content

Commit 592f483

Browse files
mp911deschauder
authored andcommitted
DATAJDBC-479 - Use SqlIdentifier in SQL AST.
We now use SqlIdentifier to encapsulate names and aliases of tables, columns and functions. We now use proper delegation to ConditionVisitor to render a JOIN condition. Previously, we used toString() of Condition segments which rendered an approximation of the condition. ConditionVisitor applies RenderContext settings that consider identifier quoting and normalization strategies. Original pull request: #187.
1 parent 595559b commit 592f483

36 files changed

+606
-146
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java

+9-15
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
1919
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
2020
import org.springframework.data.relational.core.sql.Column;
21-
import org.springframework.data.relational.core.sql.SQL;
22-
import org.springframework.data.relational.core.sql.Table;
23-
import org.springframework.data.relational.core.sql.IdentifierProcessing;
2421
import org.springframework.data.relational.core.sql.SqlIdentifier;
22+
import org.springframework.data.relational.core.sql.Table;
2523

2624
/**
2725
* Utility to get from path to SQL DSL elements.
@@ -35,21 +33,19 @@ class SqlContext {
3533

3634
private final RelationalPersistentEntity<?> entity;
3735
private final Table table;
38-
private final IdentifierProcessing identifierProcessing;
3936

40-
SqlContext(RelationalPersistentEntity<?> entity, IdentifierProcessing identifierProcessing) {
37+
SqlContext(RelationalPersistentEntity<?> entity) {
4138

42-
this.identifierProcessing = identifierProcessing;
4339
this.entity = entity;
44-
this.table = SQL.table(entity.getTableName().toSql(this.identifierProcessing));
40+
this.table = Table.create(entity.getTableName());
4541
}
4642

4743
Column getIdColumn() {
48-
return table.column(entity.getIdColumn().toSql(identifierProcessing));
44+
return table.column(entity.getIdColumn());
4945
}
5046

5147
Column getVersionColumn() {
52-
return table.column(entity.getRequiredVersionProperty().getColumnName().toSql(identifierProcessing));
48+
return table.column(entity.getRequiredVersionProperty().getColumnName());
5349
}
5450

5551
Table getTable() {
@@ -59,17 +55,15 @@ Table getTable() {
5955
Table getTable(PersistentPropertyPathExtension path) {
6056

6157
SqlIdentifier tableAlias = path.getTableAlias();
62-
Table table = SQL.table(path.getTableName().toSql(identifierProcessing));
63-
return tableAlias == null ? table : table.as(tableAlias.toSql(identifierProcessing));
58+
Table table = Table.create(path.getTableName());
59+
return tableAlias == null ? table : table.as(tableAlias);
6460
}
6561

6662
Column getColumn(PersistentPropertyPathExtension path) {
67-
return getTable(path).column(path.getColumnName().toSql(identifierProcessing))
68-
.as(path.getColumnAlias().toSql(identifierProcessing));
63+
return getTable(path).column(path.getColumnName()).as(path.getColumnAlias());
6964
}
7065

7166
Column getReverseColumn(PersistentPropertyPathExtension path) {
72-
return getTable(path).column(path.getReverseColumnName().toSql(identifierProcessing))
73-
.as(path.getReverseColumnNameAlias().toSql(identifierProcessing));
67+
return getTable(path).column(path.getReverseColumnName()).as(path.getReverseColumnNameAlias());
7468
}
7569
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java

+30-32
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
3636
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
3737
import org.springframework.data.relational.core.sql.*;
38+
import org.springframework.data.relational.core.sql.render.RenderContext;
3839
import org.springframework.data.relational.core.sql.render.SqlRenderer;
3940
import org.springframework.data.relational.domain.Identifier;
4041
import org.springframework.data.util.Lazy;
@@ -64,7 +65,7 @@ class SqlGenerator {
6465
private final JdbcConverter converter;
6566
private final RelationalPersistentEntity<?> entity;
6667
private final MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> mappingContext;
67-
private final IdentifierProcessing identifierProcessing;
68+
private final RenderContext renderContext;
6869

6970
private final SqlContext sqlContext;
7071
private final SqlRenderer sqlRenderer;
@@ -92,16 +93,15 @@ class SqlGenerator {
9293
* @param entity must not be {@literal null}.
9394
* @param dialect must not be {@literal null}.
9495
*/
95-
SqlGenerator(RelationalMappingContext mappingContext, JdbcConverter converter, RelationalPersistentEntity<?> entity,
96-
Dialect dialect) {
96+
SqlGenerator(RelationalMappingContext mappingContext, JdbcConverter converter,RelationalPersistentEntity<?> entity, Dialect dialect) {
9797

9898
this.mappingContext = mappingContext;
9999
this.converter = converter;
100100
this.entity = entity;
101-
this.identifierProcessing = dialect.getIdentifierProcessing();
102-
this.sqlContext = new SqlContext(entity, this.identifierProcessing);
101+
this.sqlContext = new SqlContext(entity);
103102
this.sqlRenderer = SqlRenderer.create(new RenderContextFactory(dialect).createRenderContext());
104103
this.columns = new Columns(entity, mappingContext, converter);
104+
this.renderContext = new RenderContextFactory(dialect).createRenderContext();
105105
}
106106

107107
/**
@@ -125,10 +125,9 @@ private Condition getSubselectCondition(PersistentPropertyPathExtension path,
125125
return rootCondition.apply(filterColumn);
126126
}
127127

128-
Table subSelectTable = SQL.table(parentPath.getTableName().toSql(identifierProcessing));
129-
Column idColumn = subSelectTable.column(parentPath.getIdColumnName().toSql(identifierProcessing));
130-
Column selectFilterColumn = subSelectTable
131-
.column(parentPath.getEffectiveIdColumnName().toSql(identifierProcessing));
128+
Table subSelectTable = Table.create(parentPath.getTableName());
129+
Column idColumn = subSelectTable.column(parentPath.getIdColumnName());
130+
Column selectFilterColumn = subSelectTable.column(parentPath.getEffectiveIdColumnName());
132131

133132
Condition innerCondition;
134133

@@ -151,7 +150,7 @@ private Condition getSubselectCondition(PersistentPropertyPathExtension path,
151150
}
152151

153152
private BindMarker getBindMarker(SqlIdentifier columnName) {
154-
return SQL.bindMarker(":" + parameterPattern.matcher(columnName.getReference(identifierProcessing)).replaceAll(""));
153+
return SQL.bindMarker(":" + parameterPattern.matcher(renderReference(columnName)).replaceAll(""));
155154
}
156155

157156
/**
@@ -210,21 +209,19 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable SqlIdentifier
210209
Assert.isTrue(keyColumn != null || !ordered,
211210
"If the SQL statement should be ordered a keyColumn to order by must be provided.");
212211

212+
Table table = getTable();
213+
213214
SelectBuilder.SelectWhere builder = selectBuilder( //
214215
keyColumn == null //
215216
? Collections.emptyList() //
216-
: Collections.singleton(keyColumn.toSql(identifierProcessing)) //
217+
: Collections.singleton(keyColumn) //
217218
);
218219

219-
Table table = getTable();
220-
221220
Condition condition = buildConditionForBackReference(parentIdentifier, table);
222221
SelectBuilder.SelectWhereAndOr withWhereClause = builder.where(condition);
223222

224223
Select select = ordered //
225-
? withWhereClause
226-
.orderBy(table.column(keyColumn.toSql(identifierProcessing)).as(keyColumn.toSql(identifierProcessing)))
227-
.build() //
224+
? withWhereClause.orderBy(table.column(keyColumn).as(keyColumn)).build() //
228225
: withWhereClause.build();
229226

230227
return render(select);
@@ -235,8 +232,7 @@ private Condition buildConditionForBackReference(Identifier parentIdentifier, Ta
235232
Condition condition = null;
236233
for (SqlIdentifier backReferenceColumn : parentIdentifier.toMap().keySet()) {
237234

238-
Condition newCondition = table.column(backReferenceColumn.toSql(identifierProcessing))
239-
.isEqualTo(getBindMarker(backReferenceColumn));
235+
Condition newCondition = table.column(backReferenceColumn).isEqualTo(getBindMarker(backReferenceColumn));
240236
condition = condition == null ? newCondition : condition.and(newCondition);
241237
}
242238

@@ -372,7 +368,7 @@ private SelectBuilder.SelectWhere selectBuilder() {
372368
return selectBuilder(Collections.emptyList());
373369
}
374370

375-
private SelectBuilder.SelectWhere selectBuilder(Collection<String> keyColumns) {
371+
private SelectBuilder.SelectWhere selectBuilder(Collection<SqlIdentifier> keyColumns) {
376372

377373
Table table = getTable();
378374

@@ -396,7 +392,7 @@ private SelectBuilder.SelectWhere selectBuilder(Collection<String> keyColumns) {
396392
}
397393
}
398394

399-
for (String keyColumn : keyColumns) {
395+
for (SqlIdentifier keyColumn : keyColumns) {
400396
columnExpressions.add(table.column(keyColumn).as(keyColumn));
401397
}
402398

@@ -485,8 +481,8 @@ Join getJoin(PersistentPropertyPathExtension path) {
485481

486482
return new Join( //
487483
currentTable, //
488-
currentTable.column(path.getReverseColumnName().toSql(identifierProcessing)), //
489-
parentTable.column(idDefiningParentPath.getIdColumnName().toSql(identifierProcessing)) //
484+
currentTable.column(path.getReverseColumnName()), //
485+
parentTable.column(idDefiningParentPath.getIdColumnName()) //
490486
);
491487
}
492488

@@ -526,14 +522,14 @@ private String createInsertSql(Set<SqlIdentifier> additionalColumns) {
526522

527523
Table table = getTable();
528524

529-
Set<SqlIdentifier> columnNamesForInsert = new TreeSet<>(Comparator.comparing(id -> id.toSql(identifierProcessing)));
525+
Set<SqlIdentifier> columnNamesForInsert = new TreeSet<>(Comparator.comparing(SqlIdentifier::getReference));
530526
columnNamesForInsert.addAll(columns.getInsertableColumns());
531527
columnNamesForInsert.addAll(additionalColumns);
532528

533529
InsertBuilder.InsertIntoColumnsAndValuesWithBuild insert = Insert.builder().into(table);
534530

535531
for (SqlIdentifier cn : columnNamesForInsert) {
536-
insert = insert.column(table.column(cn.toSql(identifierProcessing)));
532+
insert = insert.column(table.column(cn));
537533
}
538534

539535
InsertBuilder.InsertValuesWithBuild insertWithValues = null;
@@ -551,8 +547,7 @@ private String createUpdateSql() {
551547
private String createUpdateWithVersionSql() {
552548

553549
Update update = createBaseUpdate() //
554-
.and(getVersionColumn()
555-
.isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.getReference(identifierProcessing)))) //
550+
.and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + renderReference(VERSION_SQL_PARAMETER)))) //
556551
.build();
557552

558553
return render(update);
@@ -565,7 +560,7 @@ private UpdateBuilder.UpdateWhereAndOr createBaseUpdate() {
565560
List<AssignValue> assignments = columns.getUpdateableColumns() //
566561
.stream() //
567562
.map(columnName -> Assignments.value( //
568-
table.column(columnName.toSql(identifierProcessing)), //
563+
table.column(columnName), //
569564
getBindMarker(columnName))) //
570565
.collect(Collectors.toList());
571566

@@ -582,28 +577,27 @@ private String createDeleteSql() {
582577
private String createDeleteByIdAndVersionSql() {
583578

584579
Delete delete = createBaseDeleteById(getTable()) //
585-
.and(getVersionColumn()
586-
.isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.getReference(identifierProcessing)))) //
580+
.and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + renderReference(VERSION_SQL_PARAMETER)))) //
587581
.build();
588582

589583
return render(delete);
590584
}
591585

592586
private DeleteBuilder.DeleteWhereAndOr createBaseDeleteById(Table table) {
593587
return Delete.builder().from(table)
594-
.where(getIdColumn().isEqualTo(SQL.bindMarker(":" + ID_SQL_PARAMETER.getReference(identifierProcessing))));
588+
.where(getIdColumn().isEqualTo(SQL.bindMarker(":" + renderReference(ID_SQL_PARAMETER))));
595589
}
596590

597591
private String createDeleteByPathAndCriteria(PersistentPropertyPathExtension path,
598592
Function<Column, Condition> rootCondition) {
599593

600-
Table table = SQL.table(path.getTableName().toSql(identifierProcessing));
594+
Table table = Table.create(path.getTableName());
601595

602596
DeleteBuilder.DeleteWhere builder = Delete.builder() //
603597
.from(table);
604598
Delete delete;
605599

606-
Column filterColumn = table.column(path.getReverseColumnName().toSql(identifierProcessing));
600+
Column filterColumn = table.column(path.getReverseColumnName());
607601

608602
if (path.getLength() == 1) {
609603

@@ -659,6 +653,10 @@ private Column getVersionColumn() {
659653
return sqlContext.getVersionColumn();
660654
}
661655

656+
private String renderReference(SqlIdentifier identifier) {
657+
return identifier.getReference(renderContext.getIdentifierProcessing());
658+
}
659+
662660
private List<OrderByField> extractOrderByFields(Sort sort) {
663661
return sort.stream()
664662
.map(order -> OrderByField.from(Column.create(order.getProperty(), this.getTable()), order.getDirection()))

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,16 @@ public class SqlGeneratorSource {
4040
private final Dialect dialect;
4141

4242
/**
43-
* @return the {@link Dialect} used by the created {@link SqlGenerator} instances. Guaranteed to be not {@literal null}.
43+
* @return the {@link Dialect} used by the created {@link SqlGenerator} instances. Guaranteed to be not
44+
* {@literal null}.
4445
*/
4546
public Dialect getDialect() {
4647
return dialect;
4748
}
4849

4950
SqlGenerator getSqlGenerator(Class<?> domainType) {
50-
return CACHE.computeIfAbsent(domainType, t -> new SqlGenerator(context, converter,
51+
return CACHE.computeIfAbsent(domainType,
52+
t -> new SqlGenerator(context, converter,
5153
context.getRequiredPersistentEntity(t), dialect));
5254
}
5355
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcMappingContext.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ protected <T> RelationalPersistentEntity<T> createPersistentEntity(TypeInformati
8585
@Override
8686
protected RelationalPersistentProperty createPersistentProperty(Property property,
8787
RelationalPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
88-
return new BasicJdbcPersistentProperty(property, owner, simpleTypeHolder, this.getNamingStrategy());
88+
BasicJdbcPersistentProperty persistentProperty = new BasicJdbcPersistentProperty(property, owner, simpleTypeHolder,
89+
this.getNamingStrategy());
90+
persistentProperty.setForceQuote(isForceQuote());
91+
return persistentProperty;
8992
}
9093

9194
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2020 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+
* https://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.convert;
17+
18+
import org.springframework.data.relational.core.dialect.AbstractDialect;
19+
import org.springframework.data.relational.core.dialect.Dialect;
20+
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
21+
import org.springframework.data.relational.core.dialect.LimitClause;
22+
import org.springframework.data.relational.core.sql.IdentifierProcessing;
23+
24+
/**
25+
* {@link Dialect} adapter that delegates to the given {@link IdentifierProcessing}.
26+
*
27+
* @author Mark Paluch
28+
*/
29+
public class IdentifierProcessingAdapter extends AbstractDialect implements Dialect {
30+
31+
private final IdentifierProcessing identifierProcessing;
32+
33+
public IdentifierProcessingAdapter(IdentifierProcessing identifierProcessing) {
34+
this.identifierProcessing = identifierProcessing;
35+
}
36+
37+
@Override
38+
public LimitClause limit() {
39+
return HsqlDbDialect.INSTANCE.limit();
40+
}
41+
42+
@Override
43+
public IdentifierProcessing getIdentifierProcessing() {
44+
return identifierProcessing;
45+
}
46+
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import org.assertj.core.api.SoftAssertions;
2626
import org.junit.Test;
27+
2728
import org.springframework.data.annotation.Id;
2829
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
2930
import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils;

0 commit comments

Comments
 (0)