Polishing.

Simplified the code structure.
Ensured backward compatibility by recreating some methods often immediately deprecating them.

Moved new classes to the places where they belong, so that the package ...core.sql.render depends on ...core.dialect and not the other way round.
This causes dependency cycles because dependencies in the other direction already exists.
This will be properly fixed by #1105.
For now the offending classes are ignored by the DependencyTests.

See #777
See #1105

Polishing
This commit is contained in:
Jens Schauder
2021-11-29 12:01:51 +01:00
parent 7551cbd763
commit 61c6438bc3
16 changed files with 132 additions and 112 deletions

View File

@@ -93,14 +93,12 @@ class SqlGenerator {
SqlGenerator(RelationalMappingContext mappingContext, JdbcConverter converter, RelationalPersistentEntity<?> entity,
Dialect dialect) {
final RenderContextFactory renderContextFactory = new RenderContextFactory(dialect);
this.mappingContext = mappingContext;
this.entity = entity;
this.sqlContext = new SqlContext(entity);
this.sqlRenderer = SqlRenderer.create(renderContextFactory.createRenderContext());
this.renderContext = new RenderContextFactory(dialect).createRenderContext();
this.sqlRenderer = SqlRenderer.create(renderContext);
this.columns = new Columns(entity, mappingContext, converter);
this.renderContext = renderContextFactory.createRenderContext();
}
/**

View File

@@ -888,9 +888,11 @@ public class JdbcAggregateTemplateIntegrationTests {
assertThat(loaded.testTime).isEqualTo(entity.testTime);
}
@Test // DATAJDBC-557
@Test // GH-777
public void insertWithIdOnly() {
WithIdOnly entity = new WithIdOnly();
assertThat(template.save(entity).id).isNotNull();
}

View File

@@ -219,7 +219,7 @@ public class DefaultDataAccessStrategyUnitTests {
assertThat(generatedId).isEqualTo(GENERATED_ID);
verify(namedJdbcOperations).update(eq("INSERT INTO \"DUMMY_ENTITY\" VALUES ()"),
verify(namedJdbcOperations).update(eq("INSERT INTO \"DUMMY_ENTITY\" VALUES (DEFAULT)"),
paramSourceCaptor.capture(), any(KeyHolder.class));
}

View File

@@ -398,18 +398,19 @@ class SqlGeneratorUnitTests {
SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class, PostgresDialect.INSTANCE);
String insertSqlStatement = sqlGenerator.getInsert(emptySet());
String insert = sqlGenerator.getInsert(emptySet());
assertThat(insertSqlStatement).endsWith(" VALUES (DEFAULT) ");
assertThat(insert).endsWith(" VALUES (DEFAULT)");
}
@Test //DATAJDBC-557
@Test // GH-777
void gerInsertForEmptyColumnListMsSqlServer() {
SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class, SqlServerDialect.INSTANCE);
String insertSqlStatement = sqlGenerator.getInsert(emptySet());
String insert = sqlGenerator.getInsert(emptySet());
assertThat(insertSqlStatement).endsWith(" DEFAULT VALUES ");
assertThat(insert).endsWith(" DEFAULT VALUES");
}
@Test // DATAJDBC-334

View File

@@ -112,11 +112,12 @@ public interface Dialect {
}
/**
* @return an appropriate {@link InsertWithDefaultValues } for that specific dialect.
* @return an appropriate {@link InsertRenderContext} for that specific dialect.
* for most of the Dialects the default implementation will be valid, but, for
* example, in case of {@link SqlServerDialect} it is not
* example, in case of {@link SqlServerDialect} it is not.
* @since 2.4
*/
default InsertWithDefaultValues getSqlInsertWithDefaultValues() {
return new InsertWithDefaultValues() {};
default InsertRenderContext getInsertRenderContext() {
return InsertRenderContexts.DEFAULT;
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.data.relational.core.dialect;
import org.springframework.data.relational.core.sql.Insert;
import org.springframework.data.relational.core.sql.render.RenderContext;
/**
* This interface encapsulates the details about how to process {@link Insert} SQL statement
*
* @see RenderContext
* @author Mikhail Polivakha
* @since 2.4
*/
public interface InsertRenderContext {
String getDefaultValuesInsertPart();
}

View File

@@ -0,0 +1,25 @@
package org.springframework.data.relational.core.dialect;
/**
* In the scope of Insert with default values SQL statement, for example {@literal INSERT INTO SCHEMA.TABLE VALUES
* (DEFAULT)} this enum represents the default values part in different {@link Dialect}s
*
* @author Mikhail Polivakha
* @since 2.4
*/
public enum InsertRenderContexts implements InsertRenderContext {
DEFAULT(" VALUES (DEFAULT)"), //
MS_SQL_SERVER(" DEFAULT VALUES");
private final String defaultInsertPart;
InsertRenderContexts(String defaultInsertPart) {
this.defaultInsertPart = defaultInsertPart;
}
public String getDefaultValuesInsertPart() {
return defaultInsertPart;
}
}

View File

@@ -1,17 +1,15 @@
package org.springframework.data.relational.core.dialect;
import org.springframework.data.relational.core.mapping.InsertDefaultValues;
/**
* This interface aggregates information about an Insert with default values statement.
*
* @author Mikhail Polivakha
* @since 2.4
*/
public interface InsertWithDefaultValues {
/**
* @return the part of the sql statement, that follows after <b>INSERT INTO table</b>
*/
default String getDefaultInsertPart() {
return InsertDefaultValues.DEFAULT.getDefaultInsertPart();
}
}
/**
* @return the part of the sql statement, that follows after <b>INSERT INTO table</b>
*/
String getDefaultInsertPart();
}

View File

@@ -16,7 +16,6 @@
package org.springframework.data.relational.core.dialect;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.relational.core.sql.render.InsertRenderContext;
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;
@@ -78,19 +77,23 @@ public class RenderContextFactory {
static class DialectRenderContext implements RenderContext {
private final RenderNamingStrategy renderNamingStrategy;
private final SelectRenderContext selectRenderContext;
private final Dialect renderingDialect;
private final SelectRenderContext selectRenderContext;
private final InsertRenderContext insertRenderContext;
DialectRenderContext(RenderNamingStrategy renderNamingStrategy, Dialect renderingDialect, SelectRenderContext selectRenderContext) {
DialectRenderContext(RenderNamingStrategy renderNamingStrategy, Dialect renderingDialect,
SelectRenderContext selectRenderContext) {
Assert.notNull(renderNamingStrategy, "RenderNamingStrategy must not be null");
Assert.notNull(renderingDialect, "renderingDialect must not be null");
Assert.notNull(renderingDialect.getIdentifierProcessing(), "IdentifierProcessing of renderingDialect must not be null");
Assert.notNull(renderingDialect.getIdentifierProcessing(),
"IdentifierProcessing of renderingDialect must not be null");
Assert.notNull(selectRenderContext, "SelectRenderContext must not be null");
this.renderNamingStrategy = renderNamingStrategy;
this.renderingDialect = renderingDialect;
this.selectRenderContext = selectRenderContext;
this.insertRenderContext = renderingDialect.getInsertRenderContext();
}
/*
@@ -111,6 +114,11 @@ public class RenderContextFactory {
return renderingDialect.getIdentifierProcessing();
}
@Override
public SelectRenderContext getSelect() {
return getSelectRenderContext();
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.RenderContext#getSelect()
@@ -122,12 +130,7 @@ public class RenderContextFactory {
@Override
public InsertRenderContext getInsertRenderContext() {
return new InsertRenderContext() {
@Override
public String getInsertDefaultValuesPartSQL() {
return renderingDialect.getSqlInsertWithDefaultValues().getDefaultInsertPart();
}
};
return insertRenderContext;
}
}
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.relational.core.dialect;
import org.springframework.data.relational.core.mapping.InsertDefaultValues;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.relational.core.sql.LockOptions;
import org.springframework.data.relational.core.sql.render.SelectRenderContext;
@@ -154,12 +153,7 @@ public class SqlServerDialect extends AbstractDialect {
}
@Override
public InsertWithDefaultValues getSqlInsertWithDefaultValues() {
return new InsertWithDefaultValues() {
@Override
public String getDefaultInsertPart() {
return InsertDefaultValues.MS_SQL_SERVER.getDefaultInsertPart();
}
};
public InsertRenderContext getInsertRenderContext() {
return InsertRenderContexts.MS_SQL_SERVER;
}
}

View File

@@ -1,28 +0,0 @@
package org.springframework.data.relational.core.mapping;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.InsertWithDefaultValues;
/**
* In the scope of Insert with default values SQL statement, for example
* <b>INSERT INTO SCHEMA.TABLE VALUES (DEFAULT)</b>
* this enum represents the default values part in different {@link Dialect}s
*
* @author Mikhail Polivakha
* @see InsertWithDefaultValues
*/
public enum InsertDefaultValues {
DEFAULT(" VALUES (DEFAULT) "),
MS_SQL_SERVER(" DEFAULT VALUES ");
private final String defaultInsertPart;
InsertDefaultValues(String defaultInsertPart) {
this.defaultInsertPart = defaultInsertPart;
}
public String getDefaultInsertPart() {
return defaultInsertPart;
}
}

View File

@@ -1,18 +0,0 @@
package org.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.mapping.InsertDefaultValues;
import org.springframework.data.relational.core.sql.Insert;
/**
* This interface encapsulates the details about how to
* process {@link Insert} SQL statement
*
* @see RenderContext
* @author Mikhail Polivakha
*/
public interface InsertRenderContext {
default String getInsertDefaultValuesPartSQL() {
return InsertDefaultValues.DEFAULT.getDefaultInsertPart();
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.relational.core.sql.render;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.relational.core.sql.Column;
import org.springframework.data.relational.core.sql.Insert;
import org.springframework.data.relational.core.sql.Into;
@@ -98,24 +97,6 @@ class InsertStatementVisitor extends DelegatingVisitor implements PartRenderer {
return Delegation.retain();
}
private void addInsertValuesIfPresentElseDefault() {
if (values.length() != 0) {
builder.append(" VALUES (").append(values).append(")");
} else {
addInsertWithDefaultValuesToBuilder();
}
}
private void addInsertColumnsIfPresent() {
if (columns.length() != 0) {
builder.append(" (").append(columns).append(")");
}
}
private void addInsertWithDefaultValuesToBuilder() {
builder.append(renderContext.getInsertRenderContext().getInsertDefaultValuesPartSQL());
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.sql.render.PartRenderer#getRenderedPart()
@@ -125,8 +106,28 @@ class InsertStatementVisitor extends DelegatingVisitor implements PartRenderer {
return builder;
}
@NotNull
private void addInsertValuesIfPresentElseDefault() {
if (values.length() != 0) {
builder.append(" VALUES (").append(values).append(")");
} else {
addInsertWithDefaultValuesToBuilder();
}
}
private void addInsertColumnsIfPresent() {
if (columns.length() != 0) {
builder.append(" (").append(columns).append(")");
}
}
private void addInsertWithDefaultValuesToBuilder() {
builder.append(renderContext.getInsertRenderContext().getDefaultValuesInsertPart());
}
private ColumnVisitor createColumnVisitor(RenderContext context) {
return new ColumnVisitor(context, false, it -> {
if (columns.length() != 0) {
@@ -137,8 +138,8 @@ class InsertStatementVisitor extends DelegatingVisitor implements PartRenderer {
});
}
@NotNull
private IntoClauseVisitor createIntoClauseVisitor(RenderContext context) {
return new IntoClauseVisitor(context, it -> {
if (into.length() != 0) {

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.dialect.InsertRenderContext;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
/**
@@ -43,8 +44,17 @@ public interface RenderContext {
/**
* @return the {@link SelectRenderContext}.
* @deprecated Use {@link #getInsertRenderContext()} instead.
*/
SelectRenderContext getSelectRenderContext();
@Deprecated
SelectRenderContext getSelect();
/**
* @return the {@link SelectRenderContext}.
*/
default SelectRenderContext getSelectRenderContext() {
return getSelect();
}
/**
* @return the {@link InsertRenderContext}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.dialect.InsertRenderContext;
import org.springframework.data.relational.core.dialect.InsertRenderContexts;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
/**
@@ -36,6 +38,11 @@ final class SimpleRenderContext implements RenderContext {
return IdentifierProcessing.NONE;
}
@Override
public SelectRenderContext getSelect() {
return getSelectRenderContext();
}
@Override
public SelectRenderContext getSelectRenderContext() {
return DefaultSelectRenderContext.INSTANCE;
@@ -43,7 +50,7 @@ final class SimpleRenderContext implements RenderContext {
@Override
public InsertRenderContext getInsertRenderContext() {
return new InsertRenderContext() {};
return InsertRenderContexts.DEFAULT;
}
public RenderNamingStrategy getNamingStrategy() {

View File

@@ -69,6 +69,16 @@ public class SqlRenderer implements Renderer {
return create().render(select);
}
/**
* Renders a {@link Insert} statement into its SQL representation.
*
* @param insert must not be {@literal null}.
* @return the rendered statement.
*/
public static String toString(Insert insert) {
return create().render(insert);
}
/**
* Renders a {@link Update} statement into its SQL representation.
*