Support inserts for id only entities.
This broke in the past for some databases that do not support empty value lists. Closes #777 Original pull request #1047
This commit is contained in:
committed by
Jens Schauder
parent
a2a5953cda
commit
7551cbd763
@@ -50,6 +50,7 @@ import java.util.stream.Collectors;
|
||||
* @author Tyler Van Gorder
|
||||
* @author Milan Milanov
|
||||
* @author Myeonghyeon Lee
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
class SqlGenerator {
|
||||
|
||||
@@ -92,12 +93,14 @@ 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(new RenderContextFactory(dialect).createRenderContext());
|
||||
this.sqlRenderer = SqlRenderer.create(renderContextFactory.createRenderContext());
|
||||
this.columns = new Columns(entity, mappingContext, converter);
|
||||
this.renderContext = new RenderContextFactory(dialect).createRenderContext();
|
||||
this.renderContext = renderContextFactory.createRenderContext();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,6 +80,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
* @author Tyler Van Gorder
|
||||
* @author Clemens Hahn
|
||||
* @author Milan Milanov
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@Transactional
|
||||
@@ -887,6 +888,12 @@ public class JdbcAggregateTemplateIntegrationTests {
|
||||
assertThat(loaded.testTime).isEqualTo(entity.testTime);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-557
|
||||
public void insertWithIdOnly() {
|
||||
WithIdOnly entity = new WithIdOnly();
|
||||
assertThat(template.save(entity).id).isNotNull();
|
||||
}
|
||||
|
||||
private <T extends Number> void saveAndUpdateAggregateWithVersion(VersionedAggregate aggregate,
|
||||
Function<Number, T> toConcreteNumber) {
|
||||
saveAndUpdateAggregateWithVersion(aggregate, toConcreteNumber, 0);
|
||||
@@ -1254,6 +1261,11 @@ public class JdbcAggregateTemplateIntegrationTests {
|
||||
LocalDateTime testTime;
|
||||
}
|
||||
|
||||
@Table
|
||||
class WithIdOnly {
|
||||
@Id Long id;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(TestConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@@ -39,6 +39,8 @@ import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtil
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.relational.core.dialect.AnsiDialect;
|
||||
import org.springframework.data.relational.core.dialect.Dialect;
|
||||
import org.springframework.data.relational.core.dialect.PostgresDialect;
|
||||
import org.springframework.data.relational.core.dialect.SqlServerDialect;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
import org.springframework.data.relational.core.mapping.NamingStrategy;
|
||||
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
|
||||
@@ -61,6 +63,7 @@ import org.springframework.data.relational.core.sql.Table;
|
||||
* @author Tom Hombergs
|
||||
* @author Milan Milanov
|
||||
* @author Myeonghyeon Lee
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
class SqlGeneratorUnitTests {
|
||||
|
||||
@@ -391,13 +394,22 @@ class SqlGeneratorUnitTests {
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-264
|
||||
void getInsertForEmptyColumnList() {
|
||||
void getInsertForEmptyColumnListPostgres() {
|
||||
|
||||
SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class);
|
||||
SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class, PostgresDialect.INSTANCE);
|
||||
|
||||
String insert = sqlGenerator.getInsert(emptySet());
|
||||
String insertSqlStatement = sqlGenerator.getInsert(emptySet());
|
||||
|
||||
assertThat(insert).endsWith("()");
|
||||
assertThat(insertSqlStatement).endsWith(" VALUES (DEFAULT) ");
|
||||
}
|
||||
|
||||
@Test //DATAJDBC-557
|
||||
void gerInsertForEmptyColumnListMsSqlServer() {
|
||||
SqlGenerator sqlGenerator = createSqlGenerator(IdOnlyEntity.class, SqlServerDialect.INSTANCE);
|
||||
|
||||
String insertSqlStatement = sqlGenerator.getInsert(emptySet());
|
||||
|
||||
assertThat(insertSqlStatement).endsWith(" DEFAULT VALUES ");
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-334
|
||||
|
||||
@@ -37,6 +37,8 @@ DROP TABLE WITH_READ_ONLY;
|
||||
DROP TABLE VERSIONED_AGGREGATE;
|
||||
DROP TABLE WITH_LOCAL_DATE_TIME;
|
||||
|
||||
DROP TABLE WITH_ID_ONLY;
|
||||
|
||||
CREATE TABLE LEGO_SET
|
||||
(
|
||||
"id1" BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
|
||||
@@ -350,4 +352,9 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
|
||||
(
|
||||
ID BIGINT NOT NULL PRIMARY KEY,
|
||||
TEST_TIME TIMESTAMP(9)
|
||||
);
|
||||
|
||||
CREATE TABLE WITH_ID_ONLY
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY
|
||||
);
|
||||
@@ -321,4 +321,9 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
|
||||
(
|
||||
ID BIGINT PRIMARY KEY,
|
||||
TEST_TIME TIMESTAMP(9) WITHOUT TIME ZONE
|
||||
);
|
||||
|
||||
CREATE TABLE WITH_ID_ONLY
|
||||
(
|
||||
ID SERIAL PRIMARY KEY
|
||||
);
|
||||
@@ -323,4 +323,9 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
|
||||
(
|
||||
ID BIGINT PRIMARY KEY,
|
||||
TEST_TIME TIMESTAMP(9)
|
||||
);
|
||||
);
|
||||
|
||||
CREATE TABLE WITH_ID_ONLY
|
||||
(
|
||||
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY
|
||||
)
|
||||
@@ -296,4 +296,9 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
|
||||
(
|
||||
ID BIGINT PRIMARY KEY,
|
||||
TEST_TIME TIMESTAMP(6)
|
||||
);
|
||||
|
||||
CREATE TABLE WITH_ID_ONLY
|
||||
(
|
||||
ID BIGINT AUTO_INCREMENT PRIMARY KEY
|
||||
);
|
||||
@@ -324,4 +324,11 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
|
||||
(
|
||||
ID BIGINT PRIMARY KEY,
|
||||
TEST_TIME datetime2(7)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS WITH_ID_ONLY;
|
||||
|
||||
CREATE TABLE WITH_ID_ONLY
|
||||
(
|
||||
ID BIGINT IDENTITY PRIMARY KEY
|
||||
);
|
||||
@@ -301,4 +301,9 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
|
||||
(
|
||||
ID BIGINT PRIMARY KEY,
|
||||
TEST_TIME TIMESTAMP(6)
|
||||
);
|
||||
|
||||
CREATE TABLE WITH_ID_ONLY
|
||||
(
|
||||
ID BIGINT AUTO_INCREMENT PRIMARY KEY
|
||||
);
|
||||
@@ -28,6 +28,7 @@ DROP TABLE NO_ID_MAP_CHAIN4 CASCADE CONSTRAINTS PURGE;
|
||||
DROP TABLE VERSIONED_AGGREGATE CASCADE CONSTRAINTS PURGE;
|
||||
DROP TABLE WITH_READ_ONLY CASCADE CONSTRAINTS PURGE;
|
||||
DROP TABLE WITH_LOCAL_DATE_TIME CASCADE CONSTRAINTS PURGE;
|
||||
DROP TABLE WITH_ID_ONLY CASCADE CONSTRAINTS PURGE;
|
||||
|
||||
CREATE TABLE LEGO_SET
|
||||
(
|
||||
@@ -332,4 +333,9 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
|
||||
(
|
||||
ID NUMBER PRIMARY KEY,
|
||||
TEST_TIME TIMESTAMP(9)
|
||||
);
|
||||
|
||||
CREATE TABLE WITH_ID_ONLY
|
||||
(
|
||||
ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY
|
||||
);
|
||||
@@ -12,6 +12,7 @@ DROP TABLE CHAIN2;
|
||||
DROP TABLE CHAIN1;
|
||||
DROP TABLE CHAIN0;
|
||||
DROP TABLE WITH_READ_ONLY;
|
||||
DROP TABLE WITH_ID_ONLY;
|
||||
|
||||
CREATE TABLE LEGO_SET
|
||||
(
|
||||
@@ -335,4 +336,9 @@ CREATE TABLE WITH_LOCAL_DATE_TIME
|
||||
(
|
||||
ID BIGINT PRIMARY KEY,
|
||||
TEST_TIME TIMESTAMP(9) WITHOUT TIME ZONE
|
||||
);
|
||||
|
||||
CREATE TABLE WITH_ID_ONLY
|
||||
(
|
||||
ID SERIAL PRIMARY KEY
|
||||
);
|
||||
@@ -32,6 +32,7 @@ import org.springframework.data.relational.core.sql.render.SelectRenderContext;
|
||||
* @author Jens Schauder
|
||||
* @author Myeonghyeon Lee
|
||||
* @author Christoph Strobl
|
||||
* @author Mikhail Polivakha
|
||||
* @since 1.1
|
||||
*/
|
||||
public interface Dialect {
|
||||
@@ -109,4 +110,13 @@ public interface Dialect {
|
||||
default Set<Class<?>> simpleTypes() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an appropriate {@link InsertWithDefaultValues } 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
|
||||
*/
|
||||
default InsertWithDefaultValues getSqlInsertWithDefaultValues() {
|
||||
return new InsertWithDefaultValues() {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
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
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
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;
|
||||
@@ -26,6 +27,7 @@ import org.springframework.util.Assert;
|
||||
* Factory for {@link RenderContext} based on {@link Dialect}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Mikhail Polivakha
|
||||
* @since 1.1
|
||||
*/
|
||||
public class RenderContextFactory {
|
||||
@@ -65,9 +67,9 @@ public class RenderContextFactory {
|
||||
*/
|
||||
public RenderContext createRenderContext() {
|
||||
|
||||
SelectRenderContext select = dialect.getSelectContext();
|
||||
SelectRenderContext selectRenderContext = dialect.getSelectContext();
|
||||
|
||||
return new DialectRenderContext(namingStrategy, dialect.getIdentifierProcessing(), select);
|
||||
return new DialectRenderContext(namingStrategy, dialect, selectRenderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,17 +78,18 @@ public class RenderContextFactory {
|
||||
static class DialectRenderContext implements RenderContext {
|
||||
|
||||
private final RenderNamingStrategy renderNamingStrategy;
|
||||
private final IdentifierProcessing identifierProcessing;
|
||||
private final SelectRenderContext selectRenderContext;
|
||||
private final Dialect renderingDialect;
|
||||
|
||||
DialectRenderContext(RenderNamingStrategy renderNamingStrategy, IdentifierProcessing identifierProcessing, SelectRenderContext selectRenderContext) {
|
||||
DialectRenderContext(RenderNamingStrategy renderNamingStrategy, Dialect renderingDialect, SelectRenderContext selectRenderContext) {
|
||||
|
||||
Assert.notNull(renderNamingStrategy, "RenderNamingStrategy must not be null");
|
||||
Assert.notNull(identifierProcessing, "IdentifierProcessing 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(selectRenderContext, "SelectRenderContext must not be null");
|
||||
|
||||
this.renderNamingStrategy = renderNamingStrategy;
|
||||
this.identifierProcessing = identifierProcessing;
|
||||
this.renderingDialect = renderingDialect;
|
||||
this.selectRenderContext = selectRenderContext;
|
||||
}
|
||||
|
||||
@@ -105,7 +108,7 @@ public class RenderContextFactory {
|
||||
*/
|
||||
@Override
|
||||
public IdentifierProcessing getIdentifierProcessing() {
|
||||
return identifierProcessing;
|
||||
return renderingDialect.getIdentifierProcessing();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -113,8 +116,18 @@ public class RenderContextFactory {
|
||||
* @see org.springframework.data.relational.core.sql.render.RenderContext#getSelect()
|
||||
*/
|
||||
@Override
|
||||
public SelectRenderContext getSelect() {
|
||||
public SelectRenderContext getSelectRenderContext() {
|
||||
return selectRenderContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertRenderContext getInsertRenderContext() {
|
||||
return new InsertRenderContext() {
|
||||
@Override
|
||||
public String getInsertDefaultValuesPartSQL() {
|
||||
return renderingDialect.getSqlInsertWithDefaultValues().getDefaultInsertPart();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
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;
|
||||
@@ -26,6 +27,7 @@ import org.springframework.data.util.Lazy;
|
||||
* @author Mark Paluch
|
||||
* @author Myeonghyeon Lee
|
||||
* @author Jens Schauder
|
||||
* @author Mikhail Polivakha
|
||||
* @since 1.1
|
||||
*/
|
||||
public class SqlServerDialect extends AbstractDialect {
|
||||
@@ -150,4 +152,14 @@ public class SqlServerDialect extends AbstractDialect {
|
||||
public IdentifierProcessing getIdentifierProcessing() {
|
||||
return IdentifierProcessing.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertWithDefaultValues getSqlInsertWithDefaultValues() {
|
||||
return new InsertWithDefaultValues() {
|
||||
@Override
|
||||
public String getDefaultInsertPart() {
|
||||
return InsertDefaultValues.MS_SQL_SERVER.getDefaultInsertPart();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -15,51 +15,42 @@
|
||||
*/
|
||||
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;
|
||||
import org.springframework.data.relational.core.sql.Values;
|
||||
import org.springframework.data.relational.core.sql.Visitable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link PartRenderer} for {@link Insert} statements.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author Mikhail Polivakha
|
||||
* @since 1.1
|
||||
*/
|
||||
class InsertStatementVisitor extends DelegatingVisitor implements PartRenderer {
|
||||
|
||||
private StringBuilder builder = new StringBuilder();
|
||||
private StringBuilder into = new StringBuilder();
|
||||
private StringBuilder columns = new StringBuilder();
|
||||
private StringBuilder values = new StringBuilder();
|
||||
private final StringBuilder builder = new StringBuilder();
|
||||
private final StringBuilder into = new StringBuilder();
|
||||
private final StringBuilder columns = new StringBuilder();
|
||||
private final StringBuilder values = new StringBuilder();
|
||||
|
||||
private IntoClauseVisitor intoClauseVisitor;
|
||||
private ColumnVisitor columnVisitor;
|
||||
private ValuesVisitor valuesVisitor;
|
||||
private final IntoClauseVisitor intoClauseVisitor;
|
||||
private final ColumnVisitor columnVisitor;
|
||||
private final ValuesVisitor valuesVisitor;
|
||||
private final RenderContext renderContext;
|
||||
|
||||
InsertStatementVisitor(RenderContext context) {
|
||||
InsertStatementVisitor(RenderContext renderContext) {
|
||||
|
||||
this.intoClauseVisitor = new IntoClauseVisitor(context, it -> {
|
||||
Assert.notNull(renderContext, "renderContext must not be null!");
|
||||
|
||||
if (into.length() != 0) {
|
||||
into.append(", ");
|
||||
}
|
||||
|
||||
into.append(it);
|
||||
});
|
||||
|
||||
this.columnVisitor = new ColumnVisitor(context, false, it -> {
|
||||
|
||||
if (columns.length() != 0) {
|
||||
columns.append(", ");
|
||||
}
|
||||
|
||||
columns.append(it);
|
||||
});
|
||||
|
||||
this.valuesVisitor = new ValuesVisitor(context, values::append);
|
||||
this.renderContext = renderContext;
|
||||
this.intoClauseVisitor = createIntoClauseVisitor(renderContext);
|
||||
this.columnVisitor = createColumnVisitor(renderContext);
|
||||
this.valuesVisitor = new ValuesVisitor(renderContext, values::append);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -97,11 +88,9 @@ class InsertStatementVisitor extends DelegatingVisitor implements PartRenderer {
|
||||
|
||||
builder.append(" INTO ").append(into);
|
||||
|
||||
if (columns.length() != 0) {
|
||||
builder.append(" (").append(columns).append(")");
|
||||
}
|
||||
addInsertColumnsIfPresent();
|
||||
|
||||
builder.append(" VALUES (").append(values).append(")");
|
||||
addInsertValuesIfPresentElseDefault();
|
||||
|
||||
return Delegation.leave();
|
||||
}
|
||||
@@ -109,6 +98,24 @@ 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()
|
||||
@@ -117,4 +124,28 @@ class InsertStatementVisitor extends DelegatingVisitor implements PartRenderer {
|
||||
public CharSequence getRenderedPart() {
|
||||
return builder;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ColumnVisitor createColumnVisitor(RenderContext context) {
|
||||
return new ColumnVisitor(context, false, it -> {
|
||||
|
||||
if (columns.length() != 0) {
|
||||
columns.append(", ");
|
||||
}
|
||||
|
||||
columns.append(it);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private IntoClauseVisitor createIntoClauseVisitor(RenderContext context) {
|
||||
return new IntoClauseVisitor(context, it -> {
|
||||
|
||||
if (into.length() != 0) {
|
||||
into.append(", ");
|
||||
}
|
||||
|
||||
into.append(it);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
* Render context providing {@link RenderNamingStrategy} and other resources that are required during rendering.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Mikhail Polivakha
|
||||
* @since 1.1
|
||||
*/
|
||||
public interface RenderContext {
|
||||
@@ -43,5 +44,10 @@ public interface RenderContext {
|
||||
/**
|
||||
* @return the {@link SelectRenderContext}.
|
||||
*/
|
||||
SelectRenderContext getSelect();
|
||||
SelectRenderContext getSelectRenderContext();
|
||||
|
||||
/**
|
||||
* @return the {@link InsertRenderContext}
|
||||
*/
|
||||
InsertRenderContext getInsertRenderContext();
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class SelectStatementVisitor extends DelegatingVisitor implements PartRenderer {
|
||||
SelectStatementVisitor(RenderContext context) {
|
||||
|
||||
this.context = context;
|
||||
this.selectRenderContext = context.getSelect();
|
||||
this.selectRenderContext = context.getSelectRenderContext();
|
||||
this.selectListVisitor = new SelectListVisitor(context, selectList::append);
|
||||
this.orderByClauseVisitor = new OrderByClauseVisitor(context);
|
||||
this.fromClauseVisitor = new FromClauseVisitor(context, it -> {
|
||||
|
||||
@@ -37,10 +37,15 @@ final class SimpleRenderContext implements RenderContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectRenderContext getSelect() {
|
||||
public SelectRenderContext getSelectRenderContext() {
|
||||
return DefaultSelectRenderContext.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertRenderContext getInsertRenderContext() {
|
||||
return new InsertRenderContext() {};
|
||||
}
|
||||
|
||||
public RenderNamingStrategy getNamingStrategy() {
|
||||
return this.namingStrategy;
|
||||
}
|
||||
|
||||
@@ -69,16 +69,6 @@ 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.
|
||||
*
|
||||
@@ -120,10 +110,8 @@ public class SqlRenderer implements Renderer {
|
||||
*/
|
||||
@Override
|
||||
public String render(Insert insert) {
|
||||
|
||||
InsertStatementVisitor visitor = new InsertStatementVisitor(context);
|
||||
insert.visit(visitor);
|
||||
|
||||
return visitor.getRenderedPart().toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public class InsertRendererUnitTests {
|
||||
|
||||
Insert insert = Insert.builder().into(bar).build();
|
||||
|
||||
assertThat(SqlRenderer.toString(insert)).isEqualTo("INSERT INTO bar VALUES ()");
|
||||
assertThat(SqlRenderer.toString(insert)).isEqualTo("INSERT INTO bar VALUES (DEFAULT)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user