diff --git a/pom.xml b/pom.xml index 4959b322ae..3e488ee85c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-relational-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAJDBC-476-SNAPSHOT pom Spring Data Relational Parent diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml index 858a5f4b4b..555cc5931b 100644 --- a/spring-data-jdbc-distribution/pom.xml +++ b/spring-data-jdbc-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAJDBC-476-SNAPSHOT ../pom.xml diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index 17204c4a0f..2e821d833a 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-jdbc - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAJDBC-476-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAJDBC-476-SNAPSHOT diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java index c89218d366..d79d7cfb90 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java @@ -18,10 +18,8 @@ import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.sql.Column; -import org.springframework.data.relational.core.sql.SQL; -import org.springframework.data.relational.core.sql.Table; -import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.data.relational.core.sql.Table; /** * Utility to get from path to SQL DSL elements. @@ -35,21 +33,19 @@ class SqlContext { private final RelationalPersistentEntity entity; private final Table table; - private final IdentifierProcessing identifierProcessing; - SqlContext(RelationalPersistentEntity entity, IdentifierProcessing identifierProcessing) { + SqlContext(RelationalPersistentEntity entity) { - this.identifierProcessing = identifierProcessing; this.entity = entity; - this.table = SQL.table(entity.getTableName().toSql(this.identifierProcessing)); + this.table = Table.create(entity.getTableName()); } Column getIdColumn() { - return table.column(entity.getIdColumn().toSql(identifierProcessing)); + return table.column(entity.getIdColumn()); } Column getVersionColumn() { - return table.column(entity.getRequiredVersionProperty().getColumnName().toSql(identifierProcessing)); + return table.column(entity.getRequiredVersionProperty().getColumnName()); } Table getTable() { @@ -59,17 +55,15 @@ Table getTable() { Table getTable(PersistentPropertyPathExtension path) { SqlIdentifier tableAlias = path.getTableAlias(); - Table table = SQL.table(path.getTableName().toSql(identifierProcessing)); - return tableAlias == null ? table : table.as(tableAlias.toSql(identifierProcessing)); + Table table = Table.create(path.getTableName()); + return tableAlias == null ? table : table.as(tableAlias); } Column getColumn(PersistentPropertyPathExtension path) { - return getTable(path).column(path.getColumnName().toSql(identifierProcessing)) - .as(path.getColumnAlias().toSql(identifierProcessing)); + return getTable(path).column(path.getColumnName()).as(path.getColumnAlias()); } Column getReverseColumn(PersistentPropertyPathExtension path) { - return getTable(path).column(path.getReverseColumnName().toSql(identifierProcessing)) - .as(path.getReverseColumnNameAlias().toSql(identifierProcessing)); + return getTable(path).column(path.getReverseColumnName()).as(path.getReverseColumnNameAlias()); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java index 9d9f79233b..f811f815c5 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java @@ -35,15 +35,16 @@ import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.RenderContextFactory; import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.sql.*; +import org.springframework.data.relational.core.sql.render.RenderContext; import org.springframework.data.relational.core.sql.render.SqlRenderer; import org.springframework.data.relational.domain.Identifier; -import org.springframework.data.relational.core.sql.IdentifierProcessing; -import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.util.Lazy; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -69,7 +70,7 @@ class SqlGenerator { private static final Pattern parameterPattern = Pattern.compile("\\W"); private final RelationalPersistentEntity entity; private final MappingContext, RelationalPersistentProperty> mappingContext; - private final IdentifierProcessing identifierProcessing; + private final RenderContext renderContext; private final SqlContext sqlContext; private final Columns columns; @@ -93,16 +94,15 @@ class SqlGenerator { * * @param mappingContext must not be {@literal null}. * @param entity must not be {@literal null}. - * @param identifierProcessing must not be {@literal null}. + * @param dialect must not be {@literal null}. */ - SqlGenerator(RelationalMappingContext mappingContext, RelationalPersistentEntity entity, - IdentifierProcessing identifierProcessing) { + SqlGenerator(RelationalMappingContext mappingContext, RelationalPersistentEntity entity, Dialect dialect) { this.mappingContext = mappingContext; this.entity = entity; - this.identifierProcessing = identifierProcessing; - this.sqlContext = new SqlContext(entity, identifierProcessing); + this.sqlContext = new SqlContext(entity); this.columns = new Columns(entity, mappingContext); + this.renderContext = new RenderContextFactory(dialect).createRenderContext(); } /** @@ -126,10 +126,9 @@ private Condition getSubselectCondition(PersistentPropertyPathExtension path, return rootCondition.apply(filterColumn); } - Table subSelectTable = SQL.table(parentPath.getTableName().toSql(identifierProcessing)); - Column idColumn = subSelectTable.column(parentPath.getIdColumnName().toSql(identifierProcessing)); - Column selectFilterColumn = subSelectTable - .column(parentPath.getEffectiveIdColumnName().toSql(identifierProcessing)); + Table subSelectTable = Table.create(parentPath.getTableName()); + Column idColumn = subSelectTable.column(parentPath.getIdColumnName()); + Column selectFilterColumn = subSelectTable.column(parentPath.getEffectiveIdColumnName()); Condition innerCondition; @@ -152,7 +151,7 @@ private Condition getSubselectCondition(PersistentPropertyPathExtension path, } private BindMarker getBindMarker(SqlIdentifier columnName) { - return SQL.bindMarker(":" + parameterPattern.matcher(columnName.getReference(identifierProcessing)).replaceAll("")); + return SQL.bindMarker(":" + parameterPattern.matcher(renderReference(columnName)).replaceAll("")); } /** @@ -191,21 +190,19 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable SqlIdentifier Assert.isTrue(keyColumn != null || !ordered, "If the SQL statement should be ordered a keyColumn to order by must be provided."); + Table table = getTable(); + SelectBuilder.SelectWhere builder = selectBuilder( // keyColumn == null // ? Collections.emptyList() // - : Collections.singleton(keyColumn.toSql(identifierProcessing)) // + : Collections.singleton(keyColumn) // ); - Table table = getTable(); - Condition condition = buildConditionForBackReference(parentIdentifier, table); SelectBuilder.SelectWhereAndOr withWhereClause = builder.where(condition); Select select = ordered // - ? withWhereClause - .orderBy(table.column(keyColumn.toSql(identifierProcessing)).as(keyColumn.toSql(identifierProcessing))) - .build() // + ? withWhereClause.orderBy(table.column(keyColumn).as(keyColumn)).build() // : withWhereClause.build(); return render(select); @@ -216,8 +213,7 @@ private Condition buildConditionForBackReference(Identifier parentIdentifier, Ta Condition condition = null; for (SqlIdentifier backReferenceColumn : parentIdentifier.toMap().keySet()) { - Condition newCondition = table.column(backReferenceColumn.toSql(identifierProcessing)) - .isEqualTo(getBindMarker(backReferenceColumn)); + Condition newCondition = table.column(backReferenceColumn).isEqualTo(getBindMarker(backReferenceColumn)); condition = condition == null ? newCondition : condition.and(newCondition); } @@ -353,7 +349,7 @@ private SelectBuilder.SelectWhere selectBuilder() { return selectBuilder(Collections.emptyList()); } - private SelectBuilder.SelectWhere selectBuilder(Collection keyColumns) { + private SelectBuilder.SelectWhere selectBuilder(Collection keyColumns) { Table table = getTable(); @@ -377,7 +373,7 @@ private SelectBuilder.SelectWhere selectBuilder(Collection keyColumns) { } } - for (String keyColumn : keyColumns) { + for (SqlIdentifier keyColumn : keyColumns) { columnExpressions.add(table.column(keyColumn).as(keyColumn)); } @@ -440,8 +436,8 @@ Join getJoin(PersistentPropertyPathExtension path) { return new Join( // currentTable, // - currentTable.column(path.getReverseColumnName().toSql(identifierProcessing)), // - parentTable.column(idDefiningParentPath.getIdColumnName().toSql(identifierProcessing)) // + currentTable.column(path.getReverseColumnName()), // + parentTable.column(idDefiningParentPath.getIdColumnName()) // ); } @@ -481,14 +477,14 @@ private String createInsertSql(Set additionalColumns) { Table table = getTable(); - Set columnNamesForInsert = new TreeSet<>(Comparator.comparing(id -> id.toSql(identifierProcessing))); + Set columnNamesForInsert = new TreeSet<>(Comparator.comparing(SqlIdentifier::getReference)); columnNamesForInsert.addAll(columns.getInsertableColumns()); columnNamesForInsert.addAll(additionalColumns); InsertBuilder.InsertIntoColumnsAndValuesWithBuild insert = Insert.builder().into(table); for (SqlIdentifier cn : columnNamesForInsert) { - insert = insert.column(table.column(cn.toSql(identifierProcessing))); + insert = insert.column(table.column(cn)); } InsertBuilder.InsertValuesWithBuild insertWithValues = null; @@ -506,8 +502,7 @@ private String createUpdateSql() { private String createUpdateWithVersionSql() { Update update = createBaseUpdate() // - .and(getVersionColumn() - .isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.getReference(identifierProcessing)))) // + .and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + renderReference(VERSION_SQL_PARAMETER)))) // .build(); return render(update); @@ -520,7 +515,7 @@ private UpdateBuilder.UpdateWhereAndOr createBaseUpdate() { List assignments = columns.getUpdateableColumns() // .stream() // .map(columnName -> Assignments.value( // - table.column(columnName.toSql(identifierProcessing)), // + table.column(columnName), // getBindMarker(columnName))) // .collect(Collectors.toList()); @@ -537,8 +532,7 @@ private String createDeleteSql() { private String createDeleteByIdAndVersionSql() { Delete delete = createBaseDeleteById(getTable()) // - .and(getVersionColumn() - .isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.getReference(identifierProcessing)))) // + .and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + renderReference(VERSION_SQL_PARAMETER)))) // .build(); return render(delete); @@ -546,19 +540,19 @@ private String createDeleteByIdAndVersionSql() { private DeleteBuilder.DeleteWhereAndOr createBaseDeleteById(Table table) { return Delete.builder().from(table) - .where(getIdColumn().isEqualTo(SQL.bindMarker(":" + ID_SQL_PARAMETER.getReference(identifierProcessing)))); + .where(getIdColumn().isEqualTo(SQL.bindMarker(":" + renderReference(ID_SQL_PARAMETER)))); } private String createDeleteByPathAndCriteria(PersistentPropertyPathExtension path, Function rootCondition) { - Table table = SQL.table(path.getTableName().toSql(identifierProcessing)); + Table table = Table.create(path.getTableName()); DeleteBuilder.DeleteWhere builder = Delete.builder() // .from(table); Delete delete; - Column filterColumn = table.column(path.getReverseColumnName().toSql(identifierProcessing)); + Column filterColumn = table.column(path.getReverseColumnName()); if (path.getLength() == 1) { @@ -587,19 +581,19 @@ private String createDeleteByListSql() { } private String render(Select select) { - return SqlRenderer.create().render(select); + return SqlRenderer.create(renderContext).render(select); } private String render(Insert insert) { - return SqlRenderer.create().render(insert); + return SqlRenderer.create(renderContext).render(insert); } private String render(Update update) { - return SqlRenderer.create().render(update); + return SqlRenderer.create(renderContext).render(update); } private String render(Delete delete) { - return SqlRenderer.create().render(delete); + return SqlRenderer.create(renderContext).render(delete); } private Table getTable() { @@ -614,6 +608,10 @@ private Column getVersionColumn() { return sqlContext.getVersionColumn(); } + private String renderReference(SqlIdentifier identifier) { + return identifier.getReference(renderContext.getIdentifierProcessing()); + } + /** * Value object representing a {@code JOIN} association. */ diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java index 963d414664..0c5c0d5573 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java @@ -28,6 +28,7 @@ * domain type, the same generator will get returned. * * @author Jens Schauder + * @author Mark Paluch */ @RequiredArgsConstructor public class SqlGeneratorSource { @@ -37,14 +38,15 @@ public class SqlGeneratorSource { private final Dialect dialect; /** - * @return the {@link Dialect} used by the created {@link SqlGenerator} instances. Guaranteed to be not {@literal null}. + * @return the {@link Dialect} used by the created {@link SqlGenerator} instances. Guaranteed to be not + * {@literal null}. */ public Dialect getDialect() { return dialect; } - SqlGenerator getSqlGenerator(Class domainType) { - return CACHE.computeIfAbsent(domainType, t -> new SqlGenerator(context, context.getRequiredPersistentEntity(t), dialect.getIdentifierProcessing())); + return CACHE.computeIfAbsent(domainType, + t -> new SqlGenerator(context, context.getRequiredPersistentEntity(t), dialect)); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcMappingContext.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcMappingContext.java index 98bd8a8911..886470fea7 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcMappingContext.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcMappingContext.java @@ -85,7 +85,10 @@ protected RelationalPersistentEntity createPersistentEntity(TypeInformati @Override protected RelationalPersistentProperty createPersistentProperty(Property property, RelationalPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { - return new BasicJdbcPersistentProperty(property, owner, simpleTypeHolder, this); + BasicJdbcPersistentProperty persistentProperty = new BasicJdbcPersistentProperty(property, owner, simpleTypeHolder, + this); + persistentProperty.setForceQuote(isForceQuote()); + return persistentProperty; } @Override diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdentifierProcessingAdapter.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdentifierProcessingAdapter.java new file mode 100644 index 0000000000..a20be2c00a --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdentifierProcessingAdapter.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.convert; + +import org.springframework.data.relational.core.dialect.AbstractDialect; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; +import org.springframework.data.relational.core.dialect.LimitClause; +import org.springframework.data.relational.core.sql.IdentifierProcessing; + +/** + * {@link Dialect} adapter that delegates to the given {@link IdentifierProcessing}. + * + * @author Mark Paluch + */ +public class IdentifierProcessingAdapter extends AbstractDialect implements Dialect { + + private final IdentifierProcessing identifierProcessing; + + public IdentifierProcessingAdapter(IdentifierProcessing identifierProcessing) { + this.identifierProcessing = identifierProcessing; + } + + @Override + public LimitClause limit() { + return HsqlDbDialect.INSTANCE.limit(); + } + + @Override + public IdentifierProcessing getIdentifierProcessing() { + return identifierProcessing; + } +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java index 8582f43af2..4c9a4e0446 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java @@ -24,6 +24,7 @@ import org.assertj.core.api.SoftAssertions; import org.junit.Test; + import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils; @@ -32,9 +33,6 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.data.relational.core.sql.IdentifierProcessing; -import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing; -import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting; /** * Unit tests to verify a contextual {@link NamingStrategy} implementation that customizes using a user-centric @@ -218,8 +216,7 @@ private SqlGenerator configureSqlGenerator(NamingStrategy namingStrategy) { RelationalMappingContext context = new JdbcMappingContext(namingStrategy); RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class); - return new SqlGenerator(context, persistentEntity, - IdentifierProcessing.create(new Quoting(""), LetterCasing.AS_IS)); + return new SqlGenerator(context, persistentEntity, UnquotedDialect.INSTANCE); } @SuppressWarnings("unused") diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java index 84960bf40f..c5c8a0a3b5 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java @@ -22,6 +22,7 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; + import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.PropertyPathTestingUtils; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; @@ -32,9 +33,7 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.sql.Aliased; -import org.springframework.data.relational.core.sql.IdentifierProcessing; -import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing; -import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting; +import org.springframework.data.relational.core.sql.SqlIdentifier; /** * Unit tests for the {@link SqlGenerator} in a context of the {@link Embedded} annotation. @@ -48,13 +47,13 @@ public class SqlGeneratorEmbeddedUnitTests { @Before public void setUp() { + this.context.setForceQuote(false); this.sqlGenerator = createSqlGenerator(DummyEntity.class); } SqlGenerator createSqlGenerator(Class type) { RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(type); - return new SqlGenerator(context, persistentEntity, - IdentifierProcessing.create(new Quoting(""), LetterCasing.AS_IS)); + return new SqlGenerator(context, persistentEntity, UnquotedDialect.INSTANCE); } @Test // DATAJDBC-111 @@ -201,7 +200,8 @@ public void columnForEmbeddedProperty() { assertThat(generatedColumn("embeddable.test", DummyEntity.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) - .containsExactly("test", "dummy_entity", null, "test"); + .containsExactly(SqlIdentifier.unquoted("test"), SqlIdentifier.unquoted("dummy_entity"), null, + SqlIdentifier.unquoted("test")); } @Test // DATAJDBC-340 @@ -224,7 +224,8 @@ public void columnForPrefixedEmbeddedProperty() { assertThat(generatedColumn("prefixedEmbeddable.test", DummyEntity.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) - .containsExactly("prefix_test", "dummy_entity", null, "prefix_test"); + .containsExactly(SqlIdentifier.unquoted("prefix_test"), SqlIdentifier.unquoted("dummy_entity"), null, + SqlIdentifier.unquoted("prefix_test")); } @Test // DATAJDBC-340 @@ -240,7 +241,8 @@ public void columnForCascadedEmbeddedProperty() { assertThat(generatedColumn("embeddable.embeddable.attr1", DummyEntity.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) - .containsExactly("attr1", "dummy_entity", null, "attr1"); + .containsExactly(SqlIdentifier.unquoted("attr1"), SqlIdentifier.unquoted("dummy_entity"), null, + SqlIdentifier.unquoted("attr1")); } @Test // DATAJDBC-340 @@ -250,11 +252,11 @@ public void joinForEmbeddedWithReference() { SoftAssertions.assertSoftly(softly -> { - softly.assertThat(join.getJoinTable().getName()).isEqualTo("other_entity"); + softly.assertThat(join.getJoinTable().getName()).isEqualTo(SqlIdentifier.unquoted("other_entity")); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable()); - softly.assertThat(join.getJoinColumn().getName()).isEqualTo("dummy_entity2"); - softly.assertThat(join.getParentId().getName()).isEqualTo("id"); - softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("dummy_entity2"); + softly.assertThat(join.getJoinColumn().getName()).isEqualTo(SqlIdentifier.unquoted("dummy_entity2")); + softly.assertThat(join.getParentId().getName()).isEqualTo(SqlIdentifier.unquoted("id")); + softly.assertThat(join.getParentId().getTable().getName()).isEqualTo(SqlIdentifier.unquoted("dummy_entity2")); }); } @@ -263,7 +265,8 @@ public void columnForEmbeddedWithReferenceProperty() { assertThat(generatedColumn("embedded.other.value", DummyEntity2.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) - .containsExactly("value", "other_entity", "prefix_other", "prefix_other_value"); + .containsExactly(SqlIdentifier.unquoted("value"), SqlIdentifier.unquoted("other_entity"), + SqlIdentifier.quoted("prefix_other"), SqlIdentifier.unquoted("prefix_other_value")); } private SqlGenerator.Join generateJoin(String path, Class type) { @@ -271,7 +274,7 @@ private SqlGenerator.Join generateJoin(String path, Class type) { .getJoin(new PersistentPropertyPathExtension(context, PropertyPathTestingUtils.toPath(path, type, context))); } - private String getAlias(Object maybeAliased) { + private SqlIdentifier getAlias(Object maybeAliased) { if (maybeAliased instanceof Aliased) { return ((Aliased) maybeAliased).getAlias(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorFixedNamingStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorFixedNamingStrategyUnitTests.java index e905d9f964..0ded6245f4 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorFixedNamingStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorFixedNamingStrategyUnitTests.java @@ -24,11 +24,11 @@ import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils; import org.springframework.data.mapping.PersistentPropertyPath; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.data.relational.core.sql.IdentifierProcessing; /** * Unit tests the {@link SqlGenerator} with a fixed {@link NamingStrategy} implementation containing a hard wired @@ -196,7 +196,7 @@ private SqlGenerator configureSqlGenerator(NamingStrategy namingStrategy) { RelationalMappingContext context = new JdbcMappingContext(namingStrategy); RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class); - return new SqlGenerator(context, persistentEntity, IdentifierProcessing.ANSI); + return new SqlGenerator(context, persistentEntity, HsqlDbDialect.INSTANCE); } @SuppressWarnings("unused") diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java index 92537b0a60..b260b12732 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java @@ -41,11 +41,12 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.sql.Aliased; -import org.springframework.data.relational.core.sql.Table; -import org.springframework.data.relational.domain.Identifier; import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing; import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting; +import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.data.relational.core.sql.Table; +import org.springframework.data.relational.domain.Identifier; /** * Unit tests for the {@link SqlGenerator}. @@ -79,7 +80,7 @@ SqlGenerator createSqlGenerator(Class type, IdentifierProcessing identifierPr RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(type); - return new SqlGenerator(context, persistentEntity, identifierProcessing); + return new SqlGenerator(context, persistentEntity, new IdentifierProcessingAdapter(identifierProcessing)); } @Test // DATAJDBC-112 @@ -451,11 +452,11 @@ public void joinForSimpleReference() { SoftAssertions.assertSoftly(softly -> { - softly.assertThat(join.getJoinTable().getName()).isEqualTo("\"REFERENCED_ENTITY\""); + softly.assertThat(join.getJoinTable().getName()).isEqualTo(SqlIdentifier.quoted("REFERENCED_ENTITY")); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable()); - softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"DUMMY_ENTITY\""); - softly.assertThat(join.getParentId().getName()).isEqualTo("\"id1\""); - softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"DUMMY_ENTITY\""); + softly.assertThat(join.getJoinColumn().getName()).isEqualTo(SqlIdentifier.quoted("DUMMY_ENTITY")); + softly.assertThat(join.getParentId().getName()).isEqualTo(SqlIdentifier.quoted("id1")); + softly.assertThat(join.getParentId().getTable().getName()).isEqualTo(SqlIdentifier.quoted("DUMMY_ENTITY")); }); } @@ -483,11 +484,12 @@ public void joinForSecondLevelReference() { SoftAssertions.assertSoftly(softly -> { - softly.assertThat(join.getJoinTable().getName()).isEqualTo("\"SECOND_LEVEL_REFERENCED_ENTITY\""); + softly.assertThat(join.getJoinTable().getName()) + .isEqualTo(SqlIdentifier.quoted("SECOND_LEVEL_REFERENCED_ENTITY")); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable()); - softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"REFERENCED_ENTITY\""); - softly.assertThat(join.getParentId().getName()).isEqualTo("\"X_L1ID\""); - softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"REFERENCED_ENTITY\""); + softly.assertThat(join.getJoinColumn().getName()).isEqualTo(SqlIdentifier.quoted("REFERENCED_ENTITY")); + softly.assertThat(join.getParentId().getName()).isEqualTo(SqlIdentifier.quoted("X_L1ID")); + softly.assertThat(join.getParentId().getTable().getName()).isEqualTo(SqlIdentifier.quoted("REFERENCED_ENTITY")); }); } @@ -499,13 +501,14 @@ public void joinForOneToOneWithoutId() { SoftAssertions.assertSoftly(softly -> { - softly.assertThat(joinTable.getName()).isEqualTo("\"NO_ID_CHILD\""); + softly.assertThat(joinTable.getName()).isEqualTo(SqlIdentifier.quoted("NO_ID_CHILD")); softly.assertThat(joinTable).isInstanceOf(Aliased.class); - softly.assertThat(((Aliased) joinTable).getAlias()).isEqualTo("\"child\""); + softly.assertThat(((Aliased) joinTable).getAlias()).isEqualTo(SqlIdentifier.quoted("child")); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(joinTable); - softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"PARENT_OF_NO_ID_CHILD\""); - softly.assertThat(join.getParentId().getName()).isEqualTo("\"X_ID\""); - softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"PARENT_OF_NO_ID_CHILD\""); + softly.assertThat(join.getJoinColumn().getName()).isEqualTo(SqlIdentifier.quoted("PARENT_OF_NO_ID_CHILD")); + softly.assertThat(join.getParentId().getName()).isEqualTo(SqlIdentifier.quoted("X_ID")); + softly.assertThat(join.getParentId().getTable().getName()) + .isEqualTo(SqlIdentifier.quoted("PARENT_OF_NO_ID_CHILD")); }); } @@ -520,7 +523,8 @@ public void simpleColumn() { assertThat(generatedColumn("id", DummyEntity.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) - .containsExactly("\"id1\"", "\"DUMMY_ENTITY\"", null, "\"id1\""); + .containsExactly(SqlIdentifier.quoted("id1"), SqlIdentifier.quoted("DUMMY_ENTITY"), null, + SqlIdentifier.quoted("id1")); } @Test // DATAJDBC-340 @@ -528,7 +532,8 @@ public void columnForIndirectProperty() { assertThat(generatedColumn("ref.l1id", DummyEntity.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) // - .containsExactly("\"X_L1ID\"", "\"REFERENCED_ENTITY\"", "\"ref\"", "\"REF_X_L1ID\""); + .containsExactly(SqlIdentifier.quoted("X_L1ID"), SqlIdentifier.quoted("REFERENCED_ENTITY"), + SqlIdentifier.quoted("ref"), SqlIdentifier.quoted("REF_X_L1ID")); } @Test // DATAJDBC-340 @@ -542,11 +547,11 @@ public void columnForReferencedEntityWithoutId() { assertThat(generatedColumn("child", ParentOfNoIdChild.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) // - .containsExactly("\"PARENT_OF_NO_ID_CHILD\"", "\"NO_ID_CHILD\"", "\"child\"", - "\"CHILD_PARENT_OF_NO_ID_CHILD\""); + .containsExactly(SqlIdentifier.quoted("PARENT_OF_NO_ID_CHILD"), SqlIdentifier.quoted("NO_ID_CHILD"), + SqlIdentifier.quoted("child"), SqlIdentifier.quoted("CHILD_PARENT_OF_NO_ID_CHILD")); } - private String getAlias(Object maybeAliased) { + private SqlIdentifier getAlias(Object maybeAliased) { if (maybeAliased instanceof Aliased) { return ((Aliased) maybeAliased).getAlias(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/UnquotedDialect.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/UnquotedDialect.java new file mode 100644 index 0000000000..8355177907 --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/UnquotedDialect.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.convert; + +import org.springframework.data.relational.core.dialect.AbstractDialect; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; +import org.springframework.data.relational.core.dialect.LimitClause; +import org.springframework.data.relational.core.sql.IdentifierProcessing; +import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting; + +/** + * Simple {@link Dialect} that provides unquoted {@link IdentifierProcessing}. + * + * @author Mark Paluch + */ +public class UnquotedDialect extends AbstractDialect implements Dialect { + + public static final UnquotedDialect INSTANCE = new UnquotedDialect(); + + private UnquotedDialect() {} + + @Override + public LimitClause limit() { + return HsqlDbDialect.INSTANCE.limit(); + } + + @Override + public IdentifierProcessing getIdentifierProcessing() { + return IdentifierProcessing.create(new Quoting(""), LetterCasing.AS_IS); + } +} diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index 0a529399c3..0d7d811664 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-relational - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAJDBC-476-SNAPSHOT Spring Data Relational Spring Data Relational support @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAJDBC-476-SNAPSHOT diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/RenderContextFactory.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/RenderContextFactory.java index 92fdba6407..20b576fe25 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/RenderContextFactory.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/RenderContextFactory.java @@ -17,6 +17,7 @@ import lombok.RequiredArgsConstructor; +import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.render.NamingStrategies; import org.springframework.data.relational.core.sql.render.RenderContext; import org.springframework.data.relational.core.sql.render.RenderNamingStrategy; @@ -68,7 +69,7 @@ public RenderContext createRenderContext() { SelectRenderContext select = dialect.getSelectContext(); - return new DialectRenderContext(namingStrategy, select); + return new DialectRenderContext(namingStrategy, dialect.getIdentifierProcessing(), select); } /** @@ -79,6 +80,8 @@ static class DialectRenderContext implements RenderContext { private final RenderNamingStrategy renderNamingStrategy; + private final IdentifierProcessing identifierProcessing; + private final SelectRenderContext selectRenderContext; /* @@ -90,6 +93,15 @@ public RenderNamingStrategy getNamingStrategy() { return renderNamingStrategy; } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.sql.render.RenderContext#getIdentifierProcessing() + */ + @Override + public IdentifierProcessing getIdentifierProcessing() { + return identifierProcessing; + } + /* * (non-Javadoc) * @see org.springframework.data.relational.core.sql.render.RenderContext#getSelect() diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java index fe6f351499..7d2db50682 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java @@ -130,11 +130,11 @@ protected Association createAssociation() { throw new UnsupportedOperationException(); } - boolean isForceQuote() { + public boolean isForceQuote() { return forceQuote; } - void setForceQuote(boolean forceQuote) { + public void setForceQuote(boolean forceQuote) { this.forceQuote = forceQuote; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java index b9ac437249..34dcde8f3d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java @@ -80,11 +80,14 @@ public String getReference(IdentifierProcessing processing) { @Override public boolean equals(Object o) { - if (this == o) + if (this == o) { return true; + } + if (o instanceof SqlIdentifier) { return toString().equals(o.toString()); } + return false; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImpl.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImpl.java index 9e5360aec6..262e5d830b 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImpl.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImpl.java @@ -64,11 +64,11 @@ private SqlIdentifier createDerivedSqlIdentifier(String name) { return new DerivedSqlIdentifier(name, isForceQuote()); } - boolean isForceQuote() { + public boolean isForceQuote() { return forceQuote; } - void setForceQuote(boolean forceQuote) { + public void setForceQuote(boolean forceQuote) { this.forceQuote = forceQuote; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Aliased.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Aliased.java index f1cebb4413..5616a25a67 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Aliased.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Aliased.java @@ -26,5 +26,5 @@ public interface Aliased { /** * @return the alias name. */ - String getAlias(); + SqlIdentifier getAlias(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AliasedExpression.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AliasedExpression.java index 9d6622405c..c6ec61b9f7 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AliasedExpression.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AliasedExpression.java @@ -24,12 +24,20 @@ class AliasedExpression extends AbstractSegment implements Aliased, Expression { private final Expression expression; - private final String alias; + private final SqlIdentifier alias; public AliasedExpression(Expression expression, String alias) { super(expression); + this.expression = expression; + this.alias = SqlIdentifier.unquoted(alias); + } + + public AliasedExpression(Expression expression, SqlIdentifier alias) { + + super(expression); + this.expression = expression; this.alias = alias; } @@ -39,7 +47,7 @@ public AliasedExpression(Expression expression, String alias) { * @see org.springframework.data.relational.core.sql.Aliased#getAlias() */ @Override - public String getAlias() { + public SqlIdentifier getAlias() { return alias; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/BindMarker.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/BindMarker.java index e63a9c659d..a295c6aece 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/BindMarker.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/BindMarker.java @@ -47,8 +47,8 @@ static class NamedBindMarker extends BindMarker implements Named { * @see org.springframework.data.relational.core.sql.Named#getName() */ @Override - public String getName() { - return name; + public SqlIdentifier getName() { + return SqlIdentifier.unquoted(name); } /* diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java index 537d1f9d7c..1707091511 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java @@ -29,7 +29,7 @@ */ public class Column extends AbstractSegment implements Expression, Named { - private final String name; + private final SqlIdentifier name; private final Table table; Column(String name, Table table) { @@ -37,6 +37,15 @@ public class Column extends AbstractSegment implements Expression, Named { super(table); Assert.notNull(name, "Name must not be null"); + this.name = SqlIdentifier.unquoted(name); + this.table = table; + } + + Column(SqlIdentifier name, Table table) { + + super(table); + Assert.notNull(name, "Name must not be null"); + this.name = name; this.table = table; } @@ -53,6 +62,22 @@ public static Column create(String name, Table table) { Assert.hasText(name, "Name must not be null or empty"); Assert.notNull(table, "Table must not be null"); + return new Column(SqlIdentifier.unquoted(name), table); + } + + /** + * Creates a new {@link Column} associated with a {@link Table}. + * + * @param name column name, must not {@literal null}. + * @param table the table, must not be {@literal null}. + * @return the new {@link Column}. + * @since 2.0 + */ + public static Column create(SqlIdentifier name, Table table) { + + Assert.notNull(name, "Name must not be null"); + Assert.notNull(table, "Table must not be null"); + return new Column(name, table); } @@ -83,6 +108,20 @@ public Column as(String alias) { Assert.hasText(alias, "Alias must not be null or empty"); + return new AliasedColumn(name, table, SqlIdentifier.unquoted(alias)); + } + + /** + * Creates a new aliased {@link Column}. + * + * @param alias column alias name, must not {@literal null}. + * @return the aliased {@link Column}. + * @since 2.0 + */ + public Column as(SqlIdentifier alias) { + + Assert.notNull(alias, "Alias must not be null"); + return new AliasedColumn(name, table, alias); } @@ -250,7 +289,7 @@ public AssignValue set(Expression value) { * @see org.springframework.data.relational.core.sql.Named#getName() */ @Override - public String getName() { + public SqlIdentifier getName() { return name; } @@ -258,8 +297,8 @@ public String getName() { * @return the column name as it is used in references. This can be the actual {@link #getName() name} or an * {@link Aliased#getAlias() alias}. */ - public String getReferenceName() { - return name; + public SqlIdentifier getReferenceName() { + return getName(); } /** @@ -294,9 +333,14 @@ String getPrefix() { */ static class AliasedColumn extends Column implements Aliased { - private final String alias; + private final SqlIdentifier alias; private AliasedColumn(String name, Table table, String alias) { + super(name, table); + this.alias = SqlIdentifier.unquoted(alias); + } + + private AliasedColumn(SqlIdentifier name, Table table, SqlIdentifier alias) { super(name, table); this.alias = alias; } @@ -306,7 +350,7 @@ private AliasedColumn(String name, Table table, String alias) { * @see org.springframework.data.relational.core.sql.Aliased#getAlias() */ @Override - public String getAlias() { + public SqlIdentifier getAlias() { return alias; } @@ -315,7 +359,7 @@ public String getAlias() { * @see org.springframework.data.relational.core.sql.Column#getReferenceName() */ @Override - public String getReferenceName() { + public SqlIdentifier getReferenceName() { return getAlias(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSqlIdentifier.java index d3ced46790..9e44f2531d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSqlIdentifier.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSqlIdentifier.java @@ -76,11 +76,14 @@ public String getReference(IdentifierProcessing processing) { @Override public boolean equals(Object o) { - if (this == o) + if (this == o) { return true; + } + if (o instanceof SqlIdentifier) { return toString().equals(o.toString()); } + return false; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Named.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Named.java index 8ef9facac5..4af7b67e9b 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Named.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Named.java @@ -26,5 +26,5 @@ public interface Named { /** * @return the name of the underlying element. */ - String getName(); + SqlIdentifier getName(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SimpleFunction.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SimpleFunction.java index ba97eb2aa8..b764bf0894 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SimpleFunction.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SimpleFunction.java @@ -65,6 +65,20 @@ public SimpleFunction as(String alias) { Assert.hasText(alias, "Alias must not be null or empty"); + return new AliasedFunction(functionName, expressions, SqlIdentifier.unquoted(alias)); + } + + /** + * Expose this function result under a column {@code alias}. + * + * @param alias column alias name, must not {@literal null}. + * @return the aliased {@link SimpleFunction}. + * @since 2.0 + */ + public SimpleFunction as(SqlIdentifier alias) { + + Assert.notNull(alias, "Alias must not be null"); + return new AliasedFunction(functionName, expressions, alias); } @@ -97,9 +111,9 @@ public String toString() { */ static class AliasedFunction extends SimpleFunction implements Aliased { - private final String alias; + private final SqlIdentifier alias; - AliasedFunction(String functionName, List expressions, String alias) { + AliasedFunction(String functionName, List expressions, SqlIdentifier alias) { super(functionName, expressions); this.alias = alias; } @@ -109,7 +123,7 @@ static class AliasedFunction extends SimpleFunction implements Aliased { * @see org.springframework.data.relational.core.sql.Aliased#getAlias() */ @Override - public String getAlias() { + public SqlIdentifier getAlias() { return alias; } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SqlIdentifier.java index 39e267968f..f3d43b0c4c 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SqlIdentifier.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SqlIdentifier.java @@ -69,6 +69,17 @@ public String toString() { */ String getReference(IdentifierProcessing processing); + /** + * Return the reference name without any further transformation. The reference name is used for programmatic access to + * the object identified by this {@link SqlIdentifier}. + * + * @return + * @see IdentifierProcessing#NONE + */ + default String getReference() { + return getReference(IdentifierProcessing.NONE); + } + /** * Return the identifier for SQL usage after applying {@link IdentifierProcessing} rules. The identifier name is used * to construct SQL statements. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java index e371f69f4d..98dad4339e 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java @@ -33,9 +33,14 @@ */ public class Table extends AbstractSegment { - private final String name; + private final SqlIdentifier name; Table(String name) { + super(); + this.name = SqlIdentifier.unquoted(name); + } + + Table(SqlIdentifier name) { super(); this.name = name; } @@ -48,7 +53,21 @@ public class Table extends AbstractSegment { */ public static Table create(String name) { - Assert.hasText(name, "Name must not be null or empty!"); + Assert.hasText(name, "Name must not be null or empty"); + + return new Table(name); + } + + /** + * Creates a new {@link Table} given {@code name}. + * + * @param name must not be {@literal null} or empty. + * @return the new {@link Table}. + * @since 2.0 + */ + public static Table create(SqlIdentifier name) { + + Assert.notNull(name, "Name must not be null"); return new Table(name); } @@ -78,6 +97,20 @@ public Table as(String alias) { Assert.hasText(alias, "Alias must not be null or empty!"); + return new AliasedTable(name, SqlIdentifier.unquoted(alias)); + } + + /** + * Creates a new {@link Table} aliased to {@code alias}. + * + * @param alias must not be {@literal null} or empty. + * @return the new {@link Table} using the {@code alias}. + * @since 2.0 + */ + public Table as(SqlIdentifier alias) { + + Assert.notNull(alias, "Alias must not be null"); + return new AliasedTable(name, alias); } @@ -97,6 +130,23 @@ public Column column(String name) { return new Column(name, this); } + /** + * Creates a new {@link Column} associated with this {@link Table}. + *

+ * Note: This {@link Table} does not track column creation and there is no possibility to enumerate all + * {@link Column}s that were created for this table. + * + * @param name column name, must not be {@literal null} or empty. + * @return a new {@link Column} associated with this {@link Table}. + * @since 2.0 + */ + public Column column(SqlIdentifier name) { + + Assert.notNull(name, "Name must not be null"); + + return new Column(name, this); + } + /** * Creates a {@link List} of {@link Column}s associated with this {@link Table}. *

@@ -113,6 +163,28 @@ public List columns(String... names) { return columns(Arrays.asList(names)); } + /** + * Creates a {@link List} of {@link Column}s associated with this {@link Table}. + *

+ * Note: This {@link Table} does not track column creation and there is no possibility to enumerate all + * {@link Column}s that were created for this table. + * + * @param names column names, must not be {@literal null} or empty. + * @return a new {@link List} of {@link Column}s associated with this {@link Table}. + * @since 2.0 + */ + public List columns(SqlIdentifier... names) { + + Assert.notNull(names, "Names must not be null"); + + List columns = new ArrayList<>(); + for (SqlIdentifier name : names) { + columns.add(column(name)); + } + + return columns; + } + /** * Creates a {@link List} of {@link Column}s associated with this {@link Table}. *

@@ -153,7 +225,7 @@ public AsteriskFromTable asterisk() { * (non-Javadoc) * @see org.springframework.data.relational.core.sql.Named#getName() */ - public String getName() { + public SqlIdentifier getName() { return name; } @@ -161,7 +233,7 @@ public String getName() { * @return the table name as it is used in references. This can be the actual {@link #getName() name} or an * {@link Aliased#getAlias() alias}. */ - public String getReferenceName() { + public SqlIdentifier getReferenceName() { return name; } @@ -171,7 +243,7 @@ public String getReferenceName() { */ @Override public String toString() { - return name; + return name.toString(); } /** @@ -179,12 +251,20 @@ public String toString() { */ static class AliasedTable extends Table implements Aliased { - private final String alias; + private final SqlIdentifier alias; AliasedTable(String name, String alias) { super(name); - Assert.hasText(alias, "Alias must not be null or empty!"); + Assert.hasText(alias, "Alias must not be null or empty"); + + this.alias = SqlIdentifier.unquoted(alias); + } + + AliasedTable(SqlIdentifier name, SqlIdentifier alias) { + super(name); + + Assert.notNull(alias, "Alias must not be null"); this.alias = alias; } @@ -194,7 +274,7 @@ static class AliasedTable extends Table implements Aliased { * @see org.springframework.data.relational.core.sql.Aliased#getAlias() */ @Override - public String getAlias() { + public SqlIdentifier getAlias() { return alias; } @@ -203,7 +283,7 @@ public String getAlias() { * @see org.springframework.data.relational.core.sql.Table#getReferenceName() */ @Override - public String getReferenceName() { + public SqlIdentifier getReferenceName() { return getAlias(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java index 27e2927088..6ac108cdb3 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java @@ -16,6 +16,7 @@ package org.springframework.data.relational.core.sql.render; import org.springframework.data.relational.core.sql.Column; +import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.Table; import org.springframework.data.relational.core.sql.Visitable; import org.springframework.lang.Nullable; @@ -32,7 +33,7 @@ class ColumnVisitor extends TypedSubtreeVisitor { private final RenderTarget target; private final boolean considerTablePrefix; - private @Nullable String tableName; + private @Nullable SqlIdentifier tableName; ColumnVisitor(RenderContext context, boolean considerTablePrefix, RenderTarget target) { this.context = context; @@ -47,13 +48,14 @@ class ColumnVisitor extends TypedSubtreeVisitor { @Override Delegation leaveMatched(Column segment) { - String column = context.getNamingStrategy().getName(segment); - StringBuilder builder = new StringBuilder( - tableName != null ? tableName.length() + column.length() : column.length()); + SqlIdentifier column = context.getNamingStrategy().getName(segment); + StringBuilder builder = new StringBuilder(); if (considerTablePrefix && tableName != null) { - builder.append(tableName); + + builder.append(NameRenderer.render(context, SqlIdentifier.from(tableName, column))); + } else { + builder.append(NameRenderer.render(context, segment)); } - builder.append(column); target.onRendered(builder); return super.leaveMatched(segment); @@ -67,7 +69,7 @@ Delegation leaveMatched(Column segment) { Delegation leaveNested(Visitable segment) { if (segment instanceof Table) { - tableName = context.getNamingStrategy().getReferenceName((Table) segment) + '.'; + tableName = context.getNamingStrategy().getReferenceName((Table) segment); } return super.leaveNested(segment); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java index 71ca846392..408da4df27 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java @@ -61,14 +61,13 @@ Delegation enterMatched(Expression segment) { if (segment instanceof Column) { - RenderNamingStrategy namingStrategy = context.getNamingStrategy(); Column column = (Column) segment; - value = namingStrategy.getReferenceName(column.getTable()) + "." + namingStrategy.getReferenceName(column); + value = NameRenderer.fullyQualifiedReference(context, column); } else if (segment instanceof BindMarker) { if (segment instanceof Named) { - value = ((Named) segment).getName(); + value = NameRenderer.render(context, (Named) segment); } else { value = segment.toString(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FromTableVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FromTableVisitor.java index 623f9b4a10..93d8a470a7 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FromTableVisitor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FromTableVisitor.java @@ -47,9 +47,9 @@ Delegation enterMatched(Table segment) { StringBuilder builder = new StringBuilder(); - builder.append(context.getNamingStrategy().getName(segment)); + builder.append(NameRenderer.render(context, segment)); if (segment instanceof Aliased) { - builder.append(" ").append(((Aliased) segment).getAlias()); + builder.append(" ").append(NameRenderer.render(context, (Aliased) segment)); } parent.onRendered(builder); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/JoinVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/JoinVisitor.java index 135606930a..e5934b586a 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/JoinVisitor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/JoinVisitor.java @@ -33,12 +33,14 @@ class JoinVisitor extends TypedSubtreeVisitor { private final RenderContext context; private final RenderTarget parent; private final StringBuilder joinClause = new StringBuilder(); + private final ConditionVisitor conditionVisitor; private boolean inCondition = false; private boolean hasSeenCondition = false; JoinVisitor(RenderContext context, RenderTarget parent) { this.context = context; this.parent = parent; + this.conditionVisitor = new ConditionVisitor(context); } /* @@ -61,18 +63,16 @@ Delegation enterMatched(Join segment) { Delegation enterNested(Visitable segment) { if (segment instanceof Table && !inCondition) { - joinClause.append(context.getNamingStrategy().getName(((Table) segment))); + joinClause.append(NameRenderer.render(context, (Table) segment)); if (segment instanceof Aliased) { - joinClause.append(" AS ").append(((Aliased) segment).getAlias()); + joinClause.append(" AS ").append(NameRenderer.render(context, (Aliased) segment)); } } else if (segment instanceof Condition) { - // TODO: Use proper delegation for condition rendering. inCondition = true; if (!hasSeenCondition) { hasSeenCondition = true; - joinClause.append(" ON "); - joinClause.append(segment); + return Delegation.delegateTo(conditionVisitor); } } @@ -88,6 +88,14 @@ Delegation leaveNested(Visitable segment) { if (segment instanceof Condition) { inCondition = false; + + if (hasSeenCondition) { + + joinClause.append(" ON "); + joinClause.append(conditionVisitor.getRenderedPart()); + + hasSeenCondition = false; + } } return super.leaveNested(segment); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NameRenderer.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NameRenderer.java new file mode 100644 index 0000000000..22a6a7e330 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NameRenderer.java @@ -0,0 +1,132 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.relational.core.sql.render; + +import org.springframework.data.relational.core.sql.Aliased; +import org.springframework.data.relational.core.sql.Column; +import org.springframework.data.relational.core.sql.IdentifierProcessing; +import org.springframework.data.relational.core.sql.Named; +import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.data.relational.core.sql.Table; + +/** + * Utility to render {@link Column} and {@link Table} names using {@link SqlIdentifier} and {@link RenderContext} to + * SQL. + * + * @author Mark Paluch + */ +class NameRenderer { + + /** + * Render the {@link Table#getName() table name } with considering the {@link RenderNamingStrategy#getName(Table) + * naming strategy}. + * + * @param context + * @param table + * @return + */ + static CharSequence render(RenderContext context, Table table) { + return render(context, context.getNamingStrategy().getName(table)); + } + + /** + * Render the {@link Column#getName() column name} with considering the {@link RenderNamingStrategy#getName(Column) + * naming strategy}. + * + * @param context + * @param table + * @return + */ + static CharSequence render(RenderContext context, Column column) { + return render(context, context.getNamingStrategy().getName(column)); + } + + /** + * Render the {@link Named#getName() name}. + * + * @param context + * @param table + * @return + */ + static CharSequence render(RenderContext context, Named named) { + return render(context, named.getName()); + } + + /** + * Render the {@link Aliased#getAlias() alias}. + * + * @param context + * @param table + * @return + */ + static CharSequence render(RenderContext context, Aliased aliased) { + return render(context, aliased.getAlias()); + } + + /** + * Render the {@link Table#getReferenceName()} table reference name} with considering the + * {@link RenderNamingStrategy#getReferenceName(Table) naming strategy}. + * + * @param context + * @param table + * @return + */ + static CharSequence reference(RenderContext context, Table table) { + return render(context, context.getNamingStrategy().getReferenceName(table)); + } + + /** + * Render the {@link Column#getReferenceName()} column reference name} with considering the + * {@link RenderNamingStrategy#getReferenceName(Column) naming strategy}. + * + * @param context + * @param table + * @return + */ + static CharSequence reference(RenderContext context, Column column) { + return render(context, context.getNamingStrategy().getReferenceName(column)); + } + + /** + * Render the fully-qualified table and column name with considering the naming strategies of each component. + * + * @param context + * @param column + * @return + * @see RenderNamingStrategy#getReferenceName + */ + static CharSequence fullyQualifiedReference(RenderContext context, Column column) { + + RenderNamingStrategy namingStrategy = context.getNamingStrategy(); + + return render(context, SqlIdentifier.from(namingStrategy.getReferenceName(column.getTable()), + namingStrategy.getReferenceName(column))); + } + + /** + * Render the {@link SqlIdentifier#toSql(IdentifierProcessing) identifier to SQL} considering + * {@link IdentifierProcessing}. + * + * @param context + * @param identifier + * @return + */ + static CharSequence render(RenderContext context, SqlIdentifier identifier) { + return identifier.toSql(context.getIdentifierProcessing()); + } + + private NameRenderer() {} +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NamingStrategies.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NamingStrategies.java index 7cd4ed0c8d..c9b4b56693 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NamingStrategies.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NamingStrategies.java @@ -21,6 +21,7 @@ import java.util.function.Function; import org.springframework.data.relational.core.sql.Column; +import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.Table; import org.springframework.util.Assert; @@ -121,23 +122,23 @@ static class DelegatingRenderNamingStrategy implements RenderNamingStrategy { private final Function mappingFunction; @Override - public String getName(Column column) { - return mappingFunction.apply(delegate.getName(column)); + public SqlIdentifier getName(Column column) { + return delegate.getName(column).transform(mappingFunction::apply); } @Override - public String getReferenceName(Column column) { - return mappingFunction.apply(delegate.getReferenceName(column)); + public SqlIdentifier getReferenceName(Column column) { + return delegate.getReferenceName(column).transform(mappingFunction::apply); } @Override - public String getName(Table table) { - return mappingFunction.apply(delegate.getName(table)); + public SqlIdentifier getName(Table table) { + return delegate.getName(table).transform(mappingFunction::apply); } @Override - public String getReferenceName(Table table) { - return mappingFunction.apply(delegate.getReferenceName(table)); + public SqlIdentifier getReferenceName(Table table) { + return delegate.getReferenceName(table).transform(mappingFunction::apply); } } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/OrderByClauseVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/OrderByClauseVisitor.java index 9f3e7b3de6..1916bf2c32 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/OrderByClauseVisitor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/OrderByClauseVisitor.java @@ -77,7 +77,7 @@ Delegation leaveMatched(OrderByField segment) { Delegation leaveNested(Visitable segment) { if (segment instanceof Column) { - builder.append(context.getNamingStrategy().getReferenceName(((Column) segment))); + builder.append(NameRenderer.reference(context, (Column) segment)); } return super.leaveNested(segment); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/RenderContext.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/RenderContext.java index d489d605ee..011175d6a5 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/RenderContext.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/RenderContext.java @@ -15,6 +15,8 @@ */ package org.springframework.data.relational.core.sql.render; +import org.springframework.data.relational.core.sql.IdentifierProcessing; + /** * Render context providing {@link RenderNamingStrategy} and other resources that are required during rendering. * @@ -30,6 +32,14 @@ public interface RenderContext { */ RenderNamingStrategy getNamingStrategy(); + /** + * Returns the configured {@link IdentifierProcessing}. + * + * @return the {@link IdentifierProcessing}. + * @since 2.0 + */ + IdentifierProcessing getIdentifierProcessing(); + /** * @return the {@link SelectRenderContext}. */ diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/RenderNamingStrategy.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/RenderNamingStrategy.java index 3bf18df6fe..50b12bf564 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/RenderNamingStrategy.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/RenderNamingStrategy.java @@ -18,6 +18,7 @@ import java.util.function.Function; import org.springframework.data.relational.core.sql.Column; +import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.Table; import org.springframework.data.relational.core.sql.render.NamingStrategies.DelegatingRenderNamingStrategy; import org.springframework.util.Assert; @@ -38,7 +39,7 @@ public interface RenderNamingStrategy { * @return the {@link Column#getName() column name}. * @see Column#getName() */ - default String getName(Column column) { + default SqlIdentifier getName(Column column) { return column.getName(); } @@ -49,7 +50,7 @@ default String getName(Column column) { * @return the {@link Column#getName() column reference name}. * @see Column#getReferenceName() () */ - default String getReferenceName(Column column) { + default SqlIdentifier getReferenceName(Column column) { return column.getReferenceName(); } @@ -60,7 +61,7 @@ default String getReferenceName(Column column) { * @return the {@link Table#getName() table name}. * @see Table#getName() */ - default String getName(Table table) { + default SqlIdentifier getName(Table table) { return table.getName(); } @@ -71,7 +72,7 @@ default String getName(Table table) { * @return the {@link Table#getReferenceName() table name}. * @see Table#getReferenceName() */ - default String getReferenceName(Table table) { + default SqlIdentifier getReferenceName(Table table) { return table.getReferenceName(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SelectListVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SelectListVisitor.java index 290ca0b82d..420bcbfb8e 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SelectListVisitor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SelectListVisitor.java @@ -40,7 +40,6 @@ class SelectListVisitor extends TypedSubtreeVisitor implements PartR private boolean insideFunction = false; // this is hackery and should be fix with a proper visitor for // subelements. - SelectListVisitor(RenderContext context, RenderTarget target) { this.context = context; this.target = target; @@ -84,14 +83,14 @@ Delegation leaveMatched(SelectList segment) { Delegation leaveNested(Visitable segment) { if (segment instanceof Table) { - builder.append(context.getNamingStrategy().getReferenceName((Table) segment)).append('.'); + builder.append(NameRenderer.reference(context, (Table) segment)).append('.'); } if (segment instanceof SimpleFunction) { builder.append(")"); if (segment instanceof Aliased) { - builder.append(" AS ").append(((Aliased) segment).getAlias()); + builder.append(" AS ").append(NameRenderer.render(context, (Aliased) segment)); } insideFunction = false; @@ -101,9 +100,9 @@ Delegation leaveNested(Visitable segment) { requiresComma = true; } else if (segment instanceof Column) { - builder.append(context.getNamingStrategy().getName((Column) segment)); + builder.append(NameRenderer.render(context, (Column) segment)); if (segment instanceof Aliased && !insideFunction) { - builder.append(" AS ").append(((Aliased) segment).getAlias()); + builder.append(" AS ").append(NameRenderer.render(context, (Aliased) segment)); } requiresComma = true; } else if (segment instanceof AsteriskFromTable) { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SimpleRenderContext.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SimpleRenderContext.java index 8edffe020d..cfbaf970fa 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SimpleRenderContext.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SimpleRenderContext.java @@ -17,6 +17,8 @@ import lombok.Value; +import org.springframework.data.relational.core.sql.IdentifierProcessing; + /** * Default {@link RenderContext} implementation. * @@ -28,6 +30,11 @@ class SimpleRenderContext implements RenderContext { private final RenderNamingStrategy namingStrategy; + @Override + public IdentifierProcessing getIdentifierProcessing() { + return IdentifierProcessing.NONE; + } + @Override public SelectRenderContext getSelect() { return DefaultSelectRenderContext.INSTANCE; diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SelectBuilderUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SelectBuilderUnitTests.java index 1d88a9672f..a71f92670e 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SelectBuilderUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SelectBuilderUnitTests.java @@ -73,7 +73,7 @@ public void selectWithWhere() { Column foo = table.column("foo"); Comparison condition = foo.isEqualTo(SQL.literalOf("bar")); - Select select = builder.select(foo).from(table.getName()).where(condition).build(); + Select select = builder.select(foo).from(table).where(condition).build(); CapturingVisitor visitor = new CapturingVisitor(); select.visit(visitor); diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SelectRendererUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SelectRendererUnitTests.java index 1e183b57ea..077d134584 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SelectRendererUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SelectRendererUnitTests.java @@ -17,8 +17,10 @@ import static org.assertj.core.api.Assertions.*; -import org.junit.Ignore; import org.junit.Test; + +import org.springframework.data.relational.core.dialect.PostgresDialect; +import org.springframework.data.relational.core.dialect.RenderContextFactory; import org.springframework.data.relational.core.sql.Column; import org.springframework.data.relational.core.sql.Conditions; import org.springframework.data.relational.core.sql.Expressions; @@ -26,6 +28,7 @@ import org.springframework.data.relational.core.sql.OrderByField; import org.springframework.data.relational.core.sql.SQL; import org.springframework.data.relational.core.sql.Select; +import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.Table; import org.springframework.util.StringUtils; @@ -329,4 +332,21 @@ public void shouldRenderFunctionWithAlias() { assertThat(rendered).isEqualTo("SELECT COUNT(foo.*) AS counter FROM foo"); } + + @Test // DATAJDBC-479 + public void shouldRenderWithRenderContext() { + + Table table = Table.create(SqlIdentifier.quoted("my_table")); + Table join_table = Table.create(SqlIdentifier.quoted("join_table")); + Select select = Select.builder() // + .select(Functions.count(table.asterisk()).as("counter"), table.column(SqlIdentifier.quoted("reserved_keyword"))) // + .from(table) // + .join(join_table).on(table.column("source")).equals(join_table.column("target")).build(); + + String rendered = SqlRenderer.create(new RenderContextFactory(PostgresDialect.INSTANCE).createRenderContext()) + .render(select); + + assertThat(rendered).isEqualTo( + "SELECT COUNT(\"my_table\".*) AS counter, \"my_table\".\"reserved_keyword\" FROM \"my_table\" JOIN \"join_table\" ON \"my_table\".source = \"join_table\".target"); + } }