diff --git a/src/main/java/org/mybatis/dynamic/sql/StringConstant.java b/src/main/java/org/mybatis/dynamic/sql/StringConstant.java index ca38ed1da..bced8b446 100644 --- a/src/main/java/org/mybatis/dynamic/sql/StringConstant.java +++ b/src/main/java/org/mybatis/dynamic/sql/StringConstant.java @@ -20,6 +20,7 @@ import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.FragmentAndParameters; +import org.mybatis.dynamic.sql.util.StringUtilities; public class StringConstant implements BindableColumn { @@ -42,8 +43,7 @@ public Optional alias() { @Override public FragmentAndParameters render(RenderingContext renderingContext) { - String escaped = value.replace("'", "''"); //$NON-NLS-1$ //$NON-NLS-2$ - return FragmentAndParameters.fromFragment("'" + escaped + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + return FragmentAndParameters.fromFragment(StringUtilities.formatConstantForSQL(value)); } @Override diff --git a/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionModel.java b/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionModel.java index 998df3af9..0913fe17c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionModel.java @@ -25,11 +25,11 @@ public abstract class AbstractBooleanExpressionModel { private final SqlCriterion initialCriterion; - private final List subCriteria = new ArrayList<>(); + private final List subCriteria ; - protected AbstractBooleanExpressionModel(SqlCriterion initialCriterion, List subCriteria) { - this.initialCriterion = initialCriterion; - this.subCriteria.addAll(subCriteria); + protected AbstractBooleanExpressionModel(AbstractBuilder builder) { + initialCriterion = builder.initialCriterion; + subCriteria = builder.subCriteria; } public Optional initialCriterion() { @@ -39,4 +39,21 @@ public Optional initialCriterion() { public List subCriteria() { return Collections.unmodifiableList(subCriteria); } + + public abstract static class AbstractBuilder> { + private SqlCriterion initialCriterion; + private final List subCriteria = new ArrayList<>(); + + public T withInitialCriterion(SqlCriterion initialCriterion) { + this.initialCriterion = initialCriterion; + return getThis(); + } + + public T withSubCriteria(List subCriteria) { + this.subCriteria.addAll(subCriteria); + return getThis(); + } + + protected abstract T getThis(); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionRenderer.java b/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionRenderer.java index 8f2834e5e..910e20bd3 100644 --- a/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionRenderer.java @@ -28,15 +28,17 @@ import org.mybatis.dynamic.sql.where.render.CriterionRenderer; import org.mybatis.dynamic.sql.where.render.RenderedCriterion; -public abstract class AbstractBooleanExpressionRenderer { - protected final M model; +public abstract class AbstractBooleanExpressionRenderer { + protected final AbstractBooleanExpressionModel model; private final String prefix; private final CriterionRenderer criterionRenderer; + protected final RenderingContext renderingContext; - protected AbstractBooleanExpressionRenderer(String prefix, AbstractBuilder builder) { + protected AbstractBooleanExpressionRenderer(String prefix, AbstractBuilder builder) { model = Objects.requireNonNull(builder.model); this.prefix = Objects.requireNonNull(prefix); - criterionRenderer = new CriterionRenderer(builder.renderingContext); + renderingContext = Objects.requireNonNull(builder.renderingContext); + criterionRenderer = new CriterionRenderer(renderingContext); } public Optional render() { @@ -80,11 +82,11 @@ private String addPrefix(String fragment) { return spaceAfter(prefix) + fragment; } - public abstract static class AbstractBuilder> { - private final M model; + public abstract static class AbstractBuilder> { + private final AbstractBooleanExpressionModel model; private RenderingContext renderingContext; - protected AbstractBuilder(M model) { + protected AbstractBuilder(AbstractBooleanExpressionModel model) { this.model = model; } diff --git a/src/main/java/org/mybatis/dynamic/sql/common/CommonBuilder.java b/src/main/java/org/mybatis/dynamic/sql/common/CommonBuilder.java index a9cfb1fa4..f25457398 100644 --- a/src/main/java/org/mybatis/dynamic/sql/common/CommonBuilder.java +++ b/src/main/java/org/mybatis/dynamic/sql/common/CommonBuilder.java @@ -16,7 +16,8 @@ package org.mybatis.dynamic.sql.common; import org.mybatis.dynamic.sql.SqlTable; -import org.mybatis.dynamic.sql.where.WhereModel; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; /** * Builder class shared between the delete and update model builders. @@ -26,9 +27,10 @@ public abstract class CommonBuilder> { private SqlTable table; private String tableAlias; - private WhereModel whereModel; + private EmbeddedWhereModel whereModel; private Long limit; private OrderByModel orderByModel; + private StatementConfiguration statementConfiguration; public SqlTable table() { return table; @@ -38,7 +40,7 @@ public String tableAlias() { return tableAlias; } - public WhereModel whereModel() { + public EmbeddedWhereModel whereModel() { return whereModel; } @@ -50,6 +52,10 @@ public OrderByModel orderByModel() { return orderByModel; } + public StatementConfiguration statementConfiguration() { + return statementConfiguration; + } + public T withTable(SqlTable table) { this.table = table; return getThis(); @@ -60,7 +66,7 @@ public T withTableAlias(String tableAlias) { return getThis(); } - public T withWhereModel(WhereModel whereModel) { + public T withWhereModel(EmbeddedWhereModel whereModel) { this.whereModel = whereModel; return getThis(); } @@ -75,5 +81,10 @@ public T withOrderByModel(OrderByModel orderByModel) { return getThis(); } + public T withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return getThis(); + } + protected abstract T getThis(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/delete/DeleteDSL.java b/src/main/java/org/mybatis/dynamic/sql/delete/DeleteDSL.java index 1e17235cb..e7c1ad184 100644 --- a/src/main/java/org/mybatis/dynamic/sql/delete/DeleteDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/delete/DeleteDSL.java @@ -30,7 +30,7 @@ import org.mybatis.dynamic.sql.util.Utilities; import org.mybatis.dynamic.sql.where.AbstractWhereFinisher; import org.mybatis.dynamic.sql.where.AbstractWhereStarter; -import org.mybatis.dynamic.sql.where.WhereModel; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class DeleteDSL extends AbstractWhereStarter.DeleteWhereBuilder, DeleteDSL> implements Buildable { @@ -83,6 +83,7 @@ public R build() { .withLimit(limit) .withOrderByModel(orderByModel) .withWhereModel(whereBuilder == null ? null : whereBuilder.buildWhereModel()) + .withStatementConfiguration(statementConfiguration) .build(); return adapterFunction.apply(deleteModel); @@ -110,7 +111,7 @@ public static DeleteDSL deleteFrom(SqlTable table, String tableAlia public class DeleteWhereBuilder extends AbstractWhereFinisher implements Buildable { private DeleteWhereBuilder() { - super(statementConfiguration); + super(DeleteDSL.this); } public DeleteDSL limit(long limit) { @@ -137,7 +138,7 @@ protected DeleteWhereBuilder getThis() { return this; } - protected WhereModel buildWhereModel() { + protected EmbeddedWhereModel buildWhereModel() { return buildModel(); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/delete/DeleteModel.java b/src/main/java/org/mybatis/dynamic/sql/delete/DeleteModel.java index dc9e61d8e..54770d5d4 100644 --- a/src/main/java/org/mybatis/dynamic/sql/delete/DeleteModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/delete/DeleteModel.java @@ -22,17 +22,19 @@ import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.common.CommonBuilder; import org.mybatis.dynamic.sql.common.OrderByModel; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.delete.render.DeleteRenderer; import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategy; -import org.mybatis.dynamic.sql.where.WhereModel; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class DeleteModel { private final SqlTable table; private final String tableAlias; - private final WhereModel whereModel; + private final EmbeddedWhereModel whereModel; private final Long limit; private final OrderByModel orderByModel; + private final StatementConfiguration statementConfiguration; private DeleteModel(Builder builder) { table = Objects.requireNonNull(builder.table()); @@ -40,6 +42,7 @@ private DeleteModel(Builder builder) { tableAlias = builder.tableAlias(); limit = builder.limit(); orderByModel = builder.orderByModel(); + statementConfiguration = Objects.requireNonNull(builder.statementConfiguration()); } public SqlTable table() { @@ -50,7 +53,7 @@ public Optional tableAlias() { return Optional.ofNullable(tableAlias); } - public Optional whereModel() { + public Optional whereModel() { return Optional.ofNullable(whereModel); } @@ -66,6 +69,7 @@ public Optional orderByModel() { public DeleteStatementProvider render(RenderingStrategy renderingStrategy) { return DeleteRenderer.withDeleteModel(this) .withRenderingStrategy(renderingStrategy) + .withStatementConfiguration(statementConfiguration) .build() .render(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java b/src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java index d95464f1b..ce46c4c10 100644 --- a/src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/delete/render/DeleteRenderer.java @@ -21,6 +21,7 @@ import org.mybatis.dynamic.sql.common.OrderByModel; import org.mybatis.dynamic.sql.common.OrderByRenderer; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.delete.DeleteModel; import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderedParameterInfo; @@ -29,8 +30,7 @@ import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.FragmentCollector; -import org.mybatis.dynamic.sql.where.WhereModel; -import org.mybatis.dynamic.sql.where.render.WhereRenderer; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class DeleteRenderer { private final DeleteModel deleteModel; @@ -44,6 +44,7 @@ private DeleteRenderer(Builder builder) { renderingContext = RenderingContext .withRenderingStrategy(Objects.requireNonNull(builder.renderingStrategy)) .withTableAliasCalculator(tableAliasCalculator) + .withStatementConfiguration(builder.statementConfiguration) .build(); } @@ -74,11 +75,8 @@ private Optional calculateWhereClause() { return deleteModel.whereModel().flatMap(this::renderWhereClause); } - private Optional renderWhereClause(WhereModel whereModel) { - return WhereRenderer.withWhereModel(whereModel) - .withRenderingContext(renderingContext) - .build() - .render(); + private Optional renderWhereClause(EmbeddedWhereModel whereModel) { + return whereModel.render(renderingContext); } private Optional calculateLimitClause() { @@ -108,6 +106,7 @@ public static Builder withDeleteModel(DeleteModel deleteModel) { public static class Builder { private DeleteModel deleteModel; private RenderingStrategy renderingStrategy; + private StatementConfiguration statementConfiguration; public Builder withDeleteModel(DeleteModel deleteModel) { this.deleteModel = deleteModel; @@ -119,6 +118,11 @@ public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public DeleteRenderer build() { return new DeleteRenderer(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertDSL.java index 20bd60357..f36fd6544 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertDSL.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.Buildable; import org.mybatis.dynamic.sql.util.ConstantMapping; @@ -52,6 +53,7 @@ public GeneralInsertModel build() { return new GeneralInsertModel.Builder() .withTable(table) .withInsertMappings(columnMappings) + .withStatementConfiguration(new StatementConfiguration()) // nothing configurable in this statement yet .build(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertModel.java b/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertModel.java index 7657ac64b..f29b0a926 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertModel.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.insert.render.GeneralInsertRenderer; import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategy; @@ -33,11 +34,13 @@ public class GeneralInsertModel { private final SqlTable table; private final List insertMappings; + private final StatementConfiguration statementConfiguration; private GeneralInsertModel(Builder builder) { table = Objects.requireNonNull(builder.table); Validator.assertNotEmpty(builder.insertMappings, "ERROR.6"); //$NON-NLS-1$ insertMappings = builder.insertMappings; + statementConfiguration = Objects.requireNonNull(builder.statementConfiguration); } public Stream mapColumnMappings(Function mapper) { @@ -52,6 +55,7 @@ public SqlTable table() { public GeneralInsertStatementProvider render(RenderingStrategy renderingStrategy) { return GeneralInsertRenderer.withInsertModel(this) .withRenderingStrategy(renderingStrategy) + .withStatementConfiguration(statementConfiguration) .build() .render(); } @@ -59,6 +63,7 @@ public GeneralInsertStatementProvider render(RenderingStrategy renderingStrategy public static class Builder { private SqlTable table; private final List insertMappings = new ArrayList<>(); + private StatementConfiguration statementConfiguration; public Builder withTable(SqlTable table) { this.table = table; @@ -70,6 +75,11 @@ public Builder withInsertMappings(List insertMa return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public GeneralInsertModel build() { return new GeneralInsertModel(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/InsertSelectDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/InsertSelectDSL.java index dd751f263..20fad06ad 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/InsertSelectDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/InsertSelectDSL.java @@ -18,18 +18,22 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; import org.jetbrains.annotations.NotNull; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.select.SelectModel; import org.mybatis.dynamic.sql.util.Buildable; +import org.mybatis.dynamic.sql.util.ConfigurableStatement; -public class InsertSelectDSL implements Buildable { +public class InsertSelectDSL implements Buildable, ConfigurableStatement { private final SqlTable table; private final InsertColumnListModel columnList; private final SelectModel selectModel; + private final StatementConfiguration statementConfiguration = new StatementConfiguration(); private InsertSelectDSL(SqlTable table, InsertColumnListModel columnList, SelectModel selectModel) { this.table = Objects.requireNonNull(table); @@ -49,6 +53,7 @@ public InsertSelectModel build() { return InsertSelectModel.withTable(table) .withColumnList(columnList) .withSelectModel(selectModel) + .withStatementConfiguration(statementConfiguration) .build(); } @@ -56,6 +61,12 @@ public static InsertColumnGatherer insertInto(SqlTable table) { return new InsertColumnGatherer(table); } + @Override + public InsertSelectDSL configureStatement(Consumer consumer) { + consumer.accept(statementConfiguration); + return this; + } + public static class InsertColumnGatherer { private final SqlTable table; diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/InsertSelectModel.java b/src/main/java/org/mybatis/dynamic/sql/insert/InsertSelectModel.java index af80a6493..4da2cef1c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/InsertSelectModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/InsertSelectModel.java @@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.insert.render.InsertSelectRenderer; import org.mybatis.dynamic.sql.insert.render.InsertSelectStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategy; @@ -29,11 +30,13 @@ public class InsertSelectModel { private final SqlTable table; private final InsertColumnListModel columnList; private final SelectModel selectModel; + private final StatementConfiguration statementConfiguration; private InsertSelectModel(Builder builder) { table = Objects.requireNonNull(builder.table); columnList = builder.columnList; selectModel = Objects.requireNonNull(builder.selectModel); + statementConfiguration = Objects.requireNonNull(builder.statementConfiguration); } public SqlTable table() { @@ -52,6 +55,7 @@ public Optional columnList() { public InsertSelectStatementProvider render(RenderingStrategy renderingStrategy) { return InsertSelectRenderer.withInsertSelectModel(this) .withRenderingStrategy(renderingStrategy) + .withStatementConfiguration(statementConfiguration) .build() .render(); } @@ -64,6 +68,7 @@ public static class Builder { private SqlTable table; private InsertColumnListModel columnList; private SelectModel selectModel; + private StatementConfiguration statementConfiguration; public Builder withTable(SqlTable table) { this.table = table; @@ -80,6 +85,11 @@ public Builder withSelectModel(SelectModel selectModel) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public InsertSelectModel build() { return new InsertSelectModel(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertRenderer.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertRenderer.java index cfdb01e43..5a42d9422 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertRenderer.java @@ -18,6 +18,7 @@ import java.util.Objects; import java.util.Optional; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.insert.GeneralInsertModel; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategy; @@ -26,16 +27,16 @@ public class GeneralInsertRenderer { private final GeneralInsertModel model; - private final RenderingStrategy renderingStrategy; + private final RenderingContext renderingContext; private GeneralInsertRenderer(Builder builder) { model = Objects.requireNonNull(builder.model); - renderingStrategy = Objects.requireNonNull(builder.renderingStrategy); + renderingContext = RenderingContext.withRenderingStrategy(builder.renderingStrategy) + .withStatementConfiguration(builder.statementConfiguration) + .build(); } public GeneralInsertStatementProvider render() { - RenderingContext renderingContext = RenderingContext.withRenderingStrategy(renderingStrategy).build(); - GeneralInsertValuePhraseVisitor visitor = new GeneralInsertValuePhraseVisitor(renderingContext); FieldAndValueCollector collector = model.mapColumnMappings(m -> m.accept(visitor)) .filter(Optional::isPresent) @@ -58,6 +59,7 @@ public static Builder withInsertModel(GeneralInsertModel model) { public static class Builder { private GeneralInsertModel model; private RenderingStrategy renderingStrategy; + private StatementConfiguration statementConfiguration; public Builder withInsertModel(GeneralInsertModel model) { this.model = model; @@ -69,6 +71,11 @@ public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public GeneralInsertRenderer build() { return new GeneralInsertRenderer(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertValuePhraseVisitor.java index 5f7613cf5..49b239f77 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertValuePhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertValuePhraseVisitor.java @@ -25,6 +25,7 @@ import org.mybatis.dynamic.sql.util.GeneralInsertMappingVisitor; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.StringConstantMapping; +import org.mybatis.dynamic.sql.util.StringUtilities; import org.mybatis.dynamic.sql.util.ValueMapping; import org.mybatis.dynamic.sql.util.ValueOrNullMapping; import org.mybatis.dynamic.sql.util.ValueWhenPresentMapping; @@ -52,7 +53,7 @@ public Optional visit(ConstantMapping mapping) { @Override public Optional visit(StringConstantMapping mapping) { return FieldAndValueAndParameters.withFieldName(mapping.columnName()) - .withValuePhrase("'" + mapping.constant() + "'") //$NON-NLS-1$ //$NON-NLS-2$ + .withValuePhrase(StringUtilities.formatConstantForSQL(mapping.constant())) .buildOptional(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java index 522f6b5cb..951426606 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java @@ -22,8 +22,10 @@ import java.util.stream.Collectors; import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.insert.InsertColumnListModel; import org.mybatis.dynamic.sql.insert.InsertSelectModel; +import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -31,15 +33,17 @@ public class InsertSelectRenderer { private final InsertSelectModel model; - private final RenderingStrategy renderingStrategy; + private final RenderingContext renderingContext; private InsertSelectRenderer(Builder builder) { model = Objects.requireNonNull(builder.model); - renderingStrategy = Objects.requireNonNull(builder.renderingStrategy); + renderingContext = RenderingContext.withRenderingStrategy(builder.renderingStrategy) + .withStatementConfiguration(builder.statementConfiguration) + .build(); } public InsertSelectStatementProvider render() { - SelectStatementProvider selectStatement = model.selectModel().render(renderingStrategy); + SelectStatementProvider selectStatement = model.selectModel().render(renderingContext); String statementStart = InsertRenderingUtilities.calculateInsertStatementStart(model.table()); Optional columnsPhrase = calculateColumnsPhrase(); @@ -70,6 +74,7 @@ public static Builder withInsertSelectModel(InsertSelectModel model) { public static class Builder { private InsertSelectModel model; private RenderingStrategy renderingStrategy; + private StatementConfiguration statementConfiguration; public Builder withInsertSelectModel(InsertSelectModel model) { this.model = model; @@ -81,6 +86,11 @@ public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public InsertSelectRenderer build() { return new InsertSelectRenderer(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java index 1271a395a..89bf63319 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java @@ -23,6 +23,7 @@ import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.RowMapping; import org.mybatis.dynamic.sql.util.StringConstantMapping; +import org.mybatis.dynamic.sql.util.StringUtilities; public class MultiRowValuePhraseVisitor extends MultiRowInsertMappingVisitor { protected final RenderingStrategy renderingStrategy; @@ -50,7 +51,7 @@ public FieldAndValueAndParameters visit(ConstantMapping mapping) { @Override public FieldAndValueAndParameters visit(StringConstantMapping mapping) { return FieldAndValueAndParameters.withFieldName(mapping.columnName()) - .withValuePhrase("'" + mapping.constant() + "'") //$NON-NLS-1$ //$NON-NLS-2$ + .withValuePhrase(StringUtilities.formatConstantForSQL(mapping.constant())) .build(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java index d4aceadca..d85b88759 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java @@ -26,6 +26,7 @@ import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping; import org.mybatis.dynamic.sql.util.RowMapping; import org.mybatis.dynamic.sql.util.StringConstantMapping; +import org.mybatis.dynamic.sql.util.StringUtilities; public class ValuePhraseVisitor extends InsertMappingVisitor> { @@ -52,7 +53,7 @@ public Optional visit(ConstantMapping mapping) { @Override public Optional visit(StringConstantMapping mapping) { return FieldAndValueAndParameters.withFieldName(mapping.columnName()) - .withValuePhrase("'" + mapping.constant() + "'") //$NON-NLS-1$ //$NON-NLS-2$ + .withValuePhrase(StringUtilities.formatConstantForSQL(mapping.constant())) .buildOptional(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/render/RenderingContext.java b/src/main/java/org/mybatis/dynamic/sql/render/RenderingContext.java index a2e863ebc..a23c8d07a 100644 --- a/src/main/java/org/mybatis/dynamic/sql/render/RenderingContext.java +++ b/src/main/java/org/mybatis/dynamic/sql/render/RenderingContext.java @@ -23,6 +23,7 @@ import org.mybatis.dynamic.sql.BindableColumn; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; /** * This class encapsulates all the supporting items related to rendering, and contains many utility methods @@ -38,11 +39,13 @@ public class RenderingContext { private final TableAliasCalculator tableAliasCalculator; private final String configuredParameterName; private final String calculatedParameterName; + private final StatementConfiguration statementConfiguration; private RenderingContext(Builder builder) { renderingStrategy = Objects.requireNonNull(builder.renderingStrategy); configuredParameterName = builder.parameterName; tableAliasCalculator = Objects.requireNonNull(builder.tableAliasCalculator); + statementConfiguration = Objects.requireNonNull(builder.statementConfiguration); // reasonable defaults sequence = builder.sequence == null ? new AtomicInteger(1) : builder.sequence; @@ -94,6 +97,10 @@ public String aliasedTableName(SqlTable table) { .orElseGet(table::tableNameAtRuntime); } + public boolean isNonRenderingClauseAllowed() { + return statementConfiguration.isNonRenderingWhereClauseAllowed(); + } + /** * Create a new rendering context based on this, with the table alias calculator modified to include the * specified child table alias calculator. This is used by the query expression renderer when the alias calculator @@ -114,6 +121,7 @@ public RenderingContext withChildTableAliasCalculator(TableAliasCalculator child .withSequence(this.sequence) .withParameterName(this.configuredParameterName) .withTableAliasCalculator(tac) + .withStatementConfiguration(statementConfiguration) .build(); } @@ -126,6 +134,7 @@ public static class Builder { private AtomicInteger sequence; private TableAliasCalculator tableAliasCalculator = TableAliasCalculator.empty(); private String parameterName; + private StatementConfiguration statementConfiguration; public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) { this.renderingStrategy = renderingStrategy; @@ -147,6 +156,11 @@ public Builder withParameterName(String parameterName) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public RenderingContext build() { return new RenderingContext(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/AbstractHavingFinisher.java b/src/main/java/org/mybatis/dynamic/sql/select/AbstractHavingFinisher.java index 779225718..2a668c72c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/AbstractHavingFinisher.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/AbstractHavingFinisher.java @@ -33,6 +33,9 @@ void initialize(SqlCriterion sqlCriterion, List subCriteria) } protected HavingModel buildModel() { - return new HavingModel(getInitialCriterion(), subCriteria); + return new HavingModel.Builder() + .withInitialCriterion(getInitialCriterion()) + .withSubCriteria(subCriteria) + .build(); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/CountDSL.java b/src/main/java/org/mybatis/dynamic/sql/select/CountDSL.java index fcdee35c3..a684bae66 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/CountDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/CountDSL.java @@ -27,7 +27,7 @@ import org.mybatis.dynamic.sql.util.Buildable; import org.mybatis.dynamic.sql.util.Utilities; import org.mybatis.dynamic.sql.where.AbstractWhereFinisher; -import org.mybatis.dynamic.sql.where.WhereModel; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; /** * DSL for building count queries. Count queries are specializations of select queries. They have joins and where @@ -81,6 +81,7 @@ private SelectModel buildModel() { return new SelectModel.Builder() .withQueryExpression(queryExpressionModel) + .withStatementConfiguration(statementConfiguration) .build(); } @@ -130,7 +131,7 @@ public CountDSL from(SqlTable table) { public class CountWhereBuilder extends AbstractWhereFinisher implements Buildable { private CountWhereBuilder() { - super(statementConfiguration); + super(CountDSL.this); } @NotNull @@ -144,7 +145,7 @@ protected CountWhereBuilder getThis() { return this; } - protected WhereModel buildWhereModel() { + protected EmbeddedWhereModel buildWhereModel() { return super.buildModel(); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/HavingModel.java b/src/main/java/org/mybatis/dynamic/sql/select/HavingModel.java index 3a129eb4b..5f773cf3b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/HavingModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/HavingModel.java @@ -15,14 +15,21 @@ */ package org.mybatis.dynamic.sql.select; -import java.util.List; - -import org.mybatis.dynamic.sql.AndOrCriteriaGroup; -import org.mybatis.dynamic.sql.SqlCriterion; import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionModel; public class HavingModel extends AbstractBooleanExpressionModel { - public HavingModel(SqlCriterion initialCriterion, List subCriteria) { - super(initialCriterion, subCriteria); + private HavingModel(Builder builder) { + super(builder); + } + + public static class Builder extends AbstractBuilder { + public HavingModel build() { + return new HavingModel(this); + } + + @Override + protected Builder getThis() { + return this; + } } } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/MultiSelectDSL.java b/src/main/java/org/mybatis/dynamic/sql/select/MultiSelectDSL.java index 2524ae591..ad0f2aba3 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/MultiSelectDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/MultiSelectDSL.java @@ -20,19 +20,23 @@ import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import org.jetbrains.annotations.NotNull; import org.mybatis.dynamic.sql.SortSpecification; import org.mybatis.dynamic.sql.common.OrderByModel; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.util.Buildable; +import org.mybatis.dynamic.sql.util.ConfigurableStatement; -public class MultiSelectDSL implements Buildable { +public class MultiSelectDSL implements Buildable, ConfigurableStatement { private final List unionQueries = new ArrayList<>(); private final SelectModel initialSelect; private OrderByModel orderByModel; private Long limit; private Long offset; private Long fetchFirstRows; + private final StatementConfiguration statementConfiguration = new StatementConfiguration(); public MultiSelectDSL(Buildable builder) { initialSelect = builder.build(); @@ -80,6 +84,7 @@ public MultiSelectModel build() { .withUnionQueries(unionQueries) .withOrderByModel(orderByModel) .withPagingModel(buildPagingModel().orElse(null)) + .withStatementConfiguration(statementConfiguration) .build(); } @@ -91,6 +96,12 @@ private Optional buildPagingModel() { .build(); } + @Override + public MultiSelectDSL configureStatement(Consumer consumer) { + consumer.accept(statementConfiguration); + return this; + } + public class LimitFinisher implements Buildable { public OffsetFinisher offset(long offset) { MultiSelectDSL.this.offset(offset); diff --git a/src/main/java/org/mybatis/dynamic/sql/select/MultiSelectModel.java b/src/main/java/org/mybatis/dynamic/sql/select/MultiSelectModel.java index 87b2e54a2..10317a335 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/MultiSelectModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/MultiSelectModel.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull; import org.mybatis.dynamic.sql.common.OrderByModel; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.render.MultiSelectRenderer; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; @@ -34,12 +35,14 @@ public class MultiSelectModel { private final List unionQueries; private final OrderByModel orderByModel; private final PagingModel pagingModel; + private final StatementConfiguration statementConfiguration; private MultiSelectModel(Builder builder) { initialSelect = Objects.requireNonNull(builder.initialSelect); unionQueries = builder.unionQueries; orderByModel = builder.orderByModel; pagingModel = builder.pagingModel; + statementConfiguration = Objects.requireNonNull(builder.statementConfiguration); Validator.assertNotEmpty(unionQueries, "ERROR.35"); //$NON-NLS-1$ } @@ -64,6 +67,7 @@ public SelectStatementProvider render(RenderingStrategy renderingStrategy) { return new MultiSelectRenderer.Builder() .withMultiSelectModel(this) .withRenderingStrategy(renderingStrategy) + .withStatementConfiguration(statementConfiguration) .build() .render(); } @@ -73,6 +77,7 @@ public static class Builder { private final List unionQueries = new ArrayList<>(); private OrderByModel orderByModel; private PagingModel pagingModel; + private StatementConfiguration statementConfiguration; public Builder withInitialSelect(SelectModel initialSelect) { this.initialSelect = initialSelect; @@ -94,6 +99,11 @@ public Builder withPagingModel(PagingModel pagingModel) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public MultiSelectModel build() { return new MultiSelectModel(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java b/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java index 4cf0ed56b..593252979 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java @@ -38,7 +38,7 @@ import org.mybatis.dynamic.sql.util.Utilities; import org.mybatis.dynamic.sql.where.AbstractWhereFinisher; import org.mybatis.dynamic.sql.where.AbstractWhereStarter; -import org.mybatis.dynamic.sql.where.WhereModel; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class QueryExpressionDSL extends AbstractQueryExpressionDSL.QueryExpressionWhereBuilder, QueryExpressionDSL> @@ -50,7 +50,6 @@ public class QueryExpressionDSL private final List selectList; private QueryExpressionWhereBuilder whereBuilder; private GroupByModel groupByModel; - private final StatementConfiguration statementConfiguration = new StatementConfiguration(); private QueryExpressionHavingBuilder havingBuilder; protected QueryExpressionDSL(FromGatherer fromGatherer, TableExpression table) { @@ -75,7 +74,7 @@ public QueryExpressionWhereBuilder where() { @Override public QueryExpressionDSL configureStatement(Consumer consumer) { - consumer.accept(statementConfiguration); + selectDSL.configureStatement(consumer); return this; } @@ -276,7 +275,7 @@ public FromGatherer build() { public class QueryExpressionWhereBuilder extends AbstractWhereFinisher implements Buildable { private QueryExpressionWhereBuilder() { - super(statementConfiguration); + super(QueryExpressionDSL.this); } public UnionBuilder union() { @@ -326,7 +325,7 @@ protected QueryExpressionWhereBuilder getThis() { return this; } - protected WhereModel buildWhereModel() { + protected EmbeddedWhereModel buildWhereModel() { return super.buildModel(); } } @@ -394,7 +393,7 @@ public R build() { @Override public JoinSpecificationFinisher configureStatement(Consumer consumer) { - consumer.accept(statementConfiguration); + selectDSL.configureStatement(consumer); return this; } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionModel.java b/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionModel.java index 6855afb1a..f6a67c04b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionModel.java @@ -29,7 +29,7 @@ import org.mybatis.dynamic.sql.TableExpression; import org.mybatis.dynamic.sql.select.join.JoinModel; import org.mybatis.dynamic.sql.util.Validator; -import org.mybatis.dynamic.sql.where.WhereModel; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class QueryExpressionModel { private final String connector; @@ -38,7 +38,7 @@ public class QueryExpressionModel { private final TableExpression table; private final JoinModel joinModel; private final Map tableAliases; - private final WhereModel whereModel; + private final EmbeddedWhereModel whereModel; private final GroupByModel groupByModel; private final HavingModel havingModel; @@ -75,7 +75,7 @@ public Map tableAliases() { return tableAliases; } - public Optional whereModel() { + public Optional whereModel() { return Optional.ofNullable(whereModel); } @@ -101,7 +101,7 @@ public static class Builder { private final List selectList = new ArrayList<>(); private TableExpression table; private final Map tableAliases = new HashMap<>(); - private WhereModel whereModel; + private EmbeddedWhereModel whereModel; private JoinModel joinModel; private GroupByModel groupByModel; private HavingModel havingModel; @@ -136,7 +136,7 @@ public Builder withTableAliases(Map tableAliases) { return this; } - public Builder withWhereModel(WhereModel whereModel) { + public Builder withWhereModel(EmbeddedWhereModel whereModel) { this.whereModel = whereModel; return this; } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/SelectDSL.java b/src/main/java/org/mybatis/dynamic/sql/select/SelectDSL.java index 7ba76a432..a509e974b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/SelectDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/SelectDSL.java @@ -50,6 +50,7 @@ public class SelectDSL implements Buildable, ConfigurableStatement adapterFunction) { this.adapterFunction = Objects.requireNonNull(adapterFunction); @@ -124,7 +125,7 @@ public FetchFirstFinisher fetchFirst(long fetchFirstRows) { @Override public SelectDSL configureStatement(Consumer consumer) { - queryExpressions.forEach(q -> q.configureStatement(consumer)); + consumer.accept(statementConfiguration); return this; } @@ -134,6 +135,7 @@ public R build() { SelectModel selectModel = SelectModel.withQueryExpressions(buildModels()) .withOrderByModel(orderByModel) .withPagingModel(buildPagingModel().orElse(null)) + .withStatementConfiguration(statementConfiguration) .build(); return adapterFunction.apply(selectModel); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java b/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java index 653431622..44044c4ff 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull; import org.mybatis.dynamic.sql.common.OrderByModel; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.render.SelectRenderer; @@ -34,12 +35,14 @@ public class SelectModel { private final List queryExpressions; private final OrderByModel orderByModel; private final PagingModel pagingModel; + private final StatementConfiguration statementConfiguration; private SelectModel(Builder builder) { queryExpressions = Objects.requireNonNull(builder.queryExpressions); Validator.assertNotEmpty(queryExpressions, "ERROR.14"); //$NON-NLS-1$ orderByModel = builder.orderByModel; pagingModel = builder.pagingModel; + statementConfiguration = Objects.requireNonNull(builder.statementConfiguration); } public Stream mapQueryExpressions(Function mapper) { @@ -56,7 +59,20 @@ public Optional pagingModel() { @NotNull public SelectStatementProvider render(RenderingStrategy renderingStrategy) { - RenderingContext renderingContext = RenderingContext.withRenderingStrategy(renderingStrategy).build(); + RenderingContext renderingContext = RenderingContext.withRenderingStrategy(renderingStrategy) + .withStatementConfiguration(statementConfiguration) + .build(); + return render(renderingContext); + } + + /** + * This version is for rendering sub-queries, union queries, etc. + * + * @param renderingContext the rendering context + * @return a rendered select statement and parameters + */ + @NotNull + public SelectStatementProvider render(RenderingContext renderingContext) { return SelectRenderer.withSelectModel(this) .withRenderingContext(renderingContext) .build() @@ -71,6 +87,7 @@ public static class Builder { private final List queryExpressions = new ArrayList<>(); private OrderByModel orderByModel; private PagingModel pagingModel; + private StatementConfiguration statementConfiguration; public Builder withQueryExpression(QueryExpressionModel queryExpression) { this.queryExpressions.add(queryExpression); @@ -92,6 +109,11 @@ public Builder withPagingModel(PagingModel pagingModel) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public SelectModel build() { return new SelectModel(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseDSL.java b/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseDSL.java index 4e070b98d..674012d3c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseDSL.java @@ -29,7 +29,7 @@ import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionDSL; public class SearchedCaseDSL implements ElseDSL { - private final List whenConditions = new ArrayList<>(); + private final List whenConditions = new ArrayList<>(); private BasicColumn elseValue; public WhenDSL when(BindableColumn column, VisitableCondition condition, @@ -85,8 +85,11 @@ private WhenDSL(SqlCriterion sqlCriterion) { @Override public SearchedCaseDSL then(BasicColumn column) { - whenConditions.add(new SearchedCaseModel.SearchedWhenCondition(getInitialCriterion(), subCriteria, - column)); + whenConditions.add(new SearchedCaseWhenCondition.Builder() + .withInitialCriterion(getInitialCriterion()) + .withSubCriteria(subCriteria) + .withThenValue(column) + .build()); return SearchedCaseDSL.this; } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseModel.java b/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseModel.java index 50ca830ad..65d3f949f 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseModel.java @@ -17,21 +17,17 @@ import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import org.mybatis.dynamic.sql.AndOrCriteriaGroup; import org.mybatis.dynamic.sql.BasicColumn; -import org.mybatis.dynamic.sql.SqlCriterion; -import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionModel; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.select.render.SearchedCaseRenderer; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.Validator; public class SearchedCaseModel implements BasicColumn { - private final List whenConditions; + private final List whenConditions; private final BasicColumn elseValue; private final String alias; @@ -42,7 +38,7 @@ private SearchedCaseModel(Builder builder) { Validator.assertNotEmpty(whenConditions, "ERROR.40"); //$NON-NLS-1$ } - public Stream whenConditions() { + public Stream whenConditions() { return whenConditions.stream(); } @@ -68,27 +64,12 @@ public FragmentAndParameters render(RenderingContext renderingContext) { return new SearchedCaseRenderer(this, renderingContext).render(); } - public static class SearchedWhenCondition extends AbstractBooleanExpressionModel { - - private final BasicColumn thenValue; - - public BasicColumn thenValue() { - return thenValue; - } - - public SearchedWhenCondition(SqlCriterion initialCriterion, List subCriteria, - BasicColumn thenValue) { - super(initialCriterion, subCriteria); - this.thenValue = Objects.requireNonNull(thenValue); - } - } - public static class Builder { - private final List whenConditions = new ArrayList<>(); + private final List whenConditions = new ArrayList<>(); private BasicColumn elseValue; private String alias; - public Builder withWhenConditions(List whenConditions) { + public Builder withWhenConditions(List whenConditions) { this.whenConditions.addAll(whenConditions); return this; } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseWhenCondition.java b/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseWhenCondition.java new file mode 100644 index 000000000..e6d57e32c --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/select/caseexpression/SearchedCaseWhenCondition.java @@ -0,0 +1,52 @@ +/* + * Copyright 2016-2024 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.mybatis.dynamic.sql.select.caseexpression; + +import java.util.Objects; + +import org.mybatis.dynamic.sql.BasicColumn; +import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionModel; + +public class SearchedCaseWhenCondition extends AbstractBooleanExpressionModel { + private final BasicColumn thenValue; + + public BasicColumn thenValue() { + return thenValue; + } + + private SearchedCaseWhenCondition(Builder builder) { + super(builder); + thenValue = Objects.requireNonNull(builder.thenValue); + } + + public static class Builder extends AbstractBuilder { + private BasicColumn thenValue; + + public Builder withThenValue(BasicColumn thenValue) { + this.thenValue = thenValue; + return this; + } + + public SearchedCaseWhenCondition build() { + return new SearchedCaseWhenCondition(this); + } + + @Override + protected Builder getThis() { + return this; + } + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/HavingRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/HavingRenderer.java index f99d48410..0c8292a9e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/HavingRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/HavingRenderer.java @@ -18,7 +18,7 @@ import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionRenderer; import org.mybatis.dynamic.sql.select.HavingModel; -public class HavingRenderer extends AbstractBooleanExpressionRenderer { +public class HavingRenderer extends AbstractBooleanExpressionRenderer { private HavingRenderer(Builder builder) { super("having", builder); //$NON-NLS-1$ } @@ -27,7 +27,7 @@ public static Builder withHavingModel(HavingModel havingModel) { return new Builder(havingModel); } - public static class Builder extends AbstractBuilder { + public static class Builder extends AbstractBuilder { public Builder(HavingModel havingModel) { super(havingModel); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/MultiSelectRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/MultiSelectRenderer.java index c20a41d0e..4d064822d 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/MultiSelectRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/MultiSelectRenderer.java @@ -21,6 +21,7 @@ import org.mybatis.dynamic.sql.common.OrderByModel; import org.mybatis.dynamic.sql.common.OrderByRenderer; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.MultiSelectModel; @@ -36,7 +37,8 @@ public class MultiSelectRenderer { private MultiSelectRenderer(Builder builder) { renderingContext = RenderingContext - .withRenderingStrategy(Objects.requireNonNull(builder.renderingStrategy)) + .withRenderingStrategy(builder.renderingStrategy) + .withStatementConfiguration(builder.statementConfiguration) .build(); multiSelectModel = Objects.requireNonNull(builder.multiSelectModel); } @@ -62,10 +64,7 @@ private SelectStatementProvider toSelectStatementProvider(FragmentCollector frag } private FragmentAndParameters renderSelect(SelectModel selectModel) { - SelectStatementProvider selectStatement = SelectRenderer.withSelectModel(selectModel) - .withRenderingContext(renderingContext) - .build() - .render(); + SelectStatementProvider selectStatement = selectModel.render(renderingContext); return FragmentAndParameters .withFragment("(" + selectStatement.getSelectStatement() + ")") //$NON-NLS-1$ //$NON-NLS-2$ @@ -74,10 +73,7 @@ private FragmentAndParameters renderSelect(SelectModel selectModel) { } private FragmentAndParameters renderSelect(UnionQuery unionQuery) { - SelectStatementProvider selectStatement = SelectRenderer.withSelectModel(unionQuery.selectModel()) - .withRenderingContext(renderingContext) - .build() - .render(); + SelectStatementProvider selectStatement = unionQuery.selectModel().render(renderingContext); return FragmentAndParameters.withFragment( unionQuery.connector() + " (" + selectStatement.getSelectStatement() + ")") //$NON-NLS-1$ //$NON-NLS-2$ @@ -108,6 +104,7 @@ private FragmentAndParameters renderPagingModel(PagingModel pagingModel) { public static class Builder { private RenderingStrategy renderingStrategy; private MultiSelectModel multiSelectModel; + private StatementConfiguration statementConfiguration; public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) { this.renderingStrategy = renderingStrategy; @@ -119,6 +116,11 @@ public Builder withMultiSelectModel(MultiSelectModel multiSelectModel) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public MultiSelectRenderer build() { return new MultiSelectRenderer(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/QueryExpressionRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/QueryExpressionRenderer.java index dd52e64a4..b5d6a69ce 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/QueryExpressionRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/QueryExpressionRenderer.java @@ -32,8 +32,7 @@ import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.FragmentCollector; import org.mybatis.dynamic.sql.util.StringUtilities; -import org.mybatis.dynamic.sql.where.WhereModel; -import org.mybatis.dynamic.sql.where.render.WhereRenderer; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class QueryExpressionRenderer { private final QueryExpressionModel queryExpression; @@ -167,11 +166,8 @@ private Optional calculateWhereClause() { return queryExpression.whereModel().flatMap(this::renderWhereClause); } - private Optional renderWhereClause(WhereModel whereModel) { - return WhereRenderer.withWhereModel(whereModel) - .withRenderingContext(renderingContext) - .build() - .render(); + private Optional renderWhereClause(EmbeddedWhereModel whereModel) { + return whereModel.render(renderingContext); } private Optional calculateGroupByClause() { diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/SearchedCaseRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/SearchedCaseRenderer.java index 050a87204..d273c4c87 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/SearchedCaseRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/SearchedCaseRenderer.java @@ -24,6 +24,7 @@ import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.select.caseexpression.SearchedCaseModel; +import org.mybatis.dynamic.sql.select.caseexpression.SearchedCaseWhenCondition; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.FragmentCollector; import org.mybatis.dynamic.sql.util.Messages; @@ -56,12 +57,12 @@ private FragmentAndParameters renderWhenConditions() { .toFragmentAndParameters(Collectors.joining(" ")); //$NON-NLS-1$ } - private FragmentAndParameters renderWhenCondition(SearchedCaseModel.SearchedWhenCondition whenCondition) { + private FragmentAndParameters renderWhenCondition(SearchedCaseWhenCondition whenCondition) { return Stream.of(renderWhen(whenCondition), renderThen(whenCondition)).collect(FragmentCollector.collect()) .toFragmentAndParameters(Collectors.joining(" ")); //$NON-NLS-1$ } - private FragmentAndParameters renderWhen(SearchedCaseModel.SearchedWhenCondition whenCondition) { + private FragmentAndParameters renderWhen(SearchedCaseWhenCondition whenCondition) { SearchedCaseWhenConditionRenderer renderer = new SearchedCaseWhenConditionRenderer.Builder(whenCondition) .withRenderingContext(renderingContext) .build(); @@ -70,7 +71,7 @@ private FragmentAndParameters renderWhen(SearchedCaseModel.SearchedWhenCondition .orElseThrow(() -> new InvalidSqlException(Messages.getString("ERROR.39"))); //$NON-NLS-1$ } - private FragmentAndParameters renderThen(SearchedCaseModel.SearchedWhenCondition whenCondition) { + private FragmentAndParameters renderThen(SearchedCaseWhenCondition whenCondition) { return whenCondition.thenValue().render(renderingContext).mapFragment(f -> "then " + f); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/SearchedCaseWhenConditionRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/SearchedCaseWhenConditionRenderer.java index cb487b2d4..b7d9bfff3 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/SearchedCaseWhenConditionRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/SearchedCaseWhenConditionRenderer.java @@ -16,17 +16,17 @@ package org.mybatis.dynamic.sql.select.render; import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionRenderer; -import org.mybatis.dynamic.sql.select.caseexpression.SearchedCaseModel.SearchedWhenCondition; +import org.mybatis.dynamic.sql.select.caseexpression.SearchedCaseWhenCondition; -public class SearchedCaseWhenConditionRenderer extends AbstractBooleanExpressionRenderer { +public class SearchedCaseWhenConditionRenderer extends AbstractBooleanExpressionRenderer { protected SearchedCaseWhenConditionRenderer(Builder builder) { super("when", builder); } public static class Builder - extends AbstractBooleanExpressionRenderer.AbstractBuilder { + extends AbstractBooleanExpressionRenderer.AbstractBuilder { - protected Builder(SearchedWhenCondition model) { + protected Builder(SearchedCaseWhenCondition model) { super(model); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/TableExpressionRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/TableExpressionRenderer.java index 8bdb5a937..ccb9b1d84 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/TableExpressionRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/TableExpressionRenderer.java @@ -39,11 +39,7 @@ public FragmentAndParameters visit(SqlTable table) { @Override public FragmentAndParameters visit(SubQuery subQuery) { - SelectStatementProvider selectStatement = new SelectRenderer.Builder() - .withSelectModel(subQuery.selectModel()) - .withRenderingContext(renderingContext) - .build() - .render(); + SelectStatementProvider selectStatement = subQuery.selectModel().render(renderingContext); String fragment = "(" + selectStatement.getSelectStatement() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ diff --git a/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java b/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java index 55ddc6eff..8cf7259a3 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java @@ -45,7 +45,7 @@ import org.mybatis.dynamic.sql.util.ValueWhenPresentMapping; import org.mybatis.dynamic.sql.where.AbstractWhereFinisher; import org.mybatis.dynamic.sql.where.AbstractWhereStarter; -import org.mybatis.dynamic.sql.where.WhereModel; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class UpdateDSL extends AbstractWhereStarter.UpdateWhereBuilder, UpdateDSL> implements Buildable { @@ -104,6 +104,7 @@ public R build() { .withLimit(limit) .withOrderByModel(orderByModel) .withWhereModel(whereBuilder == null ? null : whereBuilder.buildWhereModel()) + .withStatementConfiguration(statementConfiguration) .build(); return adapterFunction.apply(updateModel); @@ -191,7 +192,7 @@ public UpdateDSL equalToWhenPresent(Supplier valueSupplier) { public class UpdateWhereBuilder extends AbstractWhereFinisher implements Buildable { private UpdateWhereBuilder() { - super(statementConfiguration); + super(UpdateDSL.this); } public UpdateDSL limit(long limit) { @@ -218,7 +219,7 @@ protected UpdateWhereBuilder getThis() { return this; } - protected WhereModel buildWhereModel() { + protected EmbeddedWhereModel buildWhereModel() { return buildModel(); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java b/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java index 5267375d7..a6d23adff 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java @@ -26,20 +26,22 @@ import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.common.CommonBuilder; import org.mybatis.dynamic.sql.common.OrderByModel; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.update.render.UpdateRenderer; import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider; import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.Validator; -import org.mybatis.dynamic.sql.where.WhereModel; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class UpdateModel { private final SqlTable table; private final String tableAlias; - private final WhereModel whereModel; + private final EmbeddedWhereModel whereModel; private final List columnMappings; private final Long limit; private final OrderByModel orderByModel; + private final StatementConfiguration statementConfiguration; private UpdateModel(Builder builder) { table = Objects.requireNonNull(builder.table()); @@ -49,6 +51,7 @@ private UpdateModel(Builder builder) { limit = builder.limit(); orderByModel = builder.orderByModel(); Validator.assertNotEmpty(columnMappings, "ERROR.17"); //$NON-NLS-1$ + statementConfiguration = Objects.requireNonNull(builder.statementConfiguration()); } public SqlTable table() { @@ -59,7 +62,7 @@ public Optional tableAlias() { return Optional.ofNullable(tableAlias); } - public Optional whereModel() { + public Optional whereModel() { return Optional.ofNullable(whereModel); } @@ -79,6 +82,7 @@ public Optional orderByModel() { public UpdateStatementProvider render(RenderingStrategy renderingStrategy) { return UpdateRenderer.withUpdateModel(this) .withRenderingStrategy(renderingStrategy) + .withStatementConfiguration(statementConfiguration) .build() .render(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java index 2334df0b8..a48f1ae1b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java @@ -20,7 +20,6 @@ import org.mybatis.dynamic.sql.render.RenderedParameterInfo; import org.mybatis.dynamic.sql.render.RenderingContext; -import org.mybatis.dynamic.sql.select.render.SelectRenderer; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.ColumnToColumnMapping; @@ -29,6 +28,7 @@ import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.SelectMapping; import org.mybatis.dynamic.sql.util.StringConstantMapping; +import org.mybatis.dynamic.sql.util.StringUtilities; import org.mybatis.dynamic.sql.util.UpdateMappingVisitor; import org.mybatis.dynamic.sql.util.ValueMapping; import org.mybatis.dynamic.sql.util.ValueOrNullMapping; @@ -58,9 +58,8 @@ public Optional visit(ConstantMapping mapping) { @Override public Optional visit(StringConstantMapping mapping) { String fragment = mapping.mapColumn(renderingContext::aliasedColumnName) - + " = '" //$NON-NLS-1$ - + mapping.constant() - + "'"; //$NON-NLS-1$ + + " = " //$NON-NLS-1$ + + StringUtilities.formatConstantForSQL(mapping.constant()); return FragmentAndParameters.withFragment(fragment) .buildOptional(); @@ -85,11 +84,7 @@ public Optional visit(ValueWhenPresentMapping mapp @Override public Optional visit(SelectMapping mapping) { - SelectStatementProvider selectStatement = SelectRenderer.withSelectModel(mapping.selectModel()) - .withRenderingContext(renderingContext) - .build() - .render(); - + SelectStatementProvider selectStatement = mapping.selectModel().render(renderingContext); String fragment = mapping.mapColumn(renderingContext::aliasedColumnName) + " = (" //$NON-NLS-1$ + selectStatement.getSelectStatement() diff --git a/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java b/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java index faecffce3..3c9c5d73e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java @@ -22,6 +22,7 @@ import org.mybatis.dynamic.sql.common.OrderByModel; import org.mybatis.dynamic.sql.common.OrderByRenderer; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderedParameterInfo; import org.mybatis.dynamic.sql.render.RenderingContext; @@ -31,8 +32,7 @@ import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.FragmentCollector; import org.mybatis.dynamic.sql.util.Validator; -import org.mybatis.dynamic.sql.where.WhereModel; -import org.mybatis.dynamic.sql.where.render.WhereRenderer; +import org.mybatis.dynamic.sql.where.EmbeddedWhereModel; public class UpdateRenderer { private final UpdateModel updateModel; @@ -46,6 +46,7 @@ private UpdateRenderer(Builder builder) { renderingContext = RenderingContext .withRenderingStrategy(Objects.requireNonNull(builder.renderingStrategy)) .withTableAliasCalculator(tableAliasCalculator) + .withStatementConfiguration(builder.statementConfiguration) .build(); } @@ -100,11 +101,8 @@ private Optional calculateWhereClause() { return updateModel.whereModel().flatMap(this::renderWhereClause); } - private Optional renderWhereClause(WhereModel whereModel) { - return WhereRenderer.withWhereModel(whereModel) - .withRenderingContext(renderingContext) - .build() - .render(); + private Optional renderWhereClause(EmbeddedWhereModel whereModel) { + return whereModel.render(renderingContext); } private Optional calculateLimitClause() { @@ -134,6 +132,7 @@ public static Builder withUpdateModel(UpdateModel updateModel) { public static class Builder { private UpdateModel updateModel; private RenderingStrategy renderingStrategy; + private StatementConfiguration statementConfiguration; public Builder withUpdateModel(UpdateModel updateModel) { this.updateModel = updateModel; @@ -145,6 +144,11 @@ public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) { return this; } + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + public UpdateRenderer build() { return new UpdateRenderer(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java b/src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java index 0606c0e7c..3ef16be5e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/StringUtilities.java @@ -52,4 +52,10 @@ static String toCamelCase(String inputString) { return sb.toString(); } + + static String formatConstantForSQL(String in) { + String escaped = in.replace("'", "''"); //$NON-NLS-1$ //$NON-NLS-2$ + return "'" + escaped + "'"; //$NON-NLS-1$ //$NON-NLS-2$ + + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/AbstractWhereFinisher.java b/src/main/java/org/mybatis/dynamic/sql/where/AbstractWhereFinisher.java index dd501d315..edad1cccc 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/AbstractWhereFinisher.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/AbstractWhereFinisher.java @@ -27,7 +27,11 @@ public abstract class AbstractWhereFinisher> extends AbstractBooleanExpressionDSL implements ConfigurableStatement { - private final StatementConfiguration statementConfiguration; + private final ConfigurableStatement parentStatement; + + protected AbstractWhereFinisher(ConfigurableStatement parentStatement) { + this.parentStatement = Objects.requireNonNull(parentStatement); + } void initialize(SqlCriterion sqlCriterion) { setInitialCriterion(sqlCriterion, StatementType.WHERE); @@ -38,17 +42,16 @@ void initialize(SqlCriterion sqlCriterion, List subCriteria) super.subCriteria.addAll(subCriteria); } - protected AbstractWhereFinisher(StatementConfiguration statementConfiguration) { - this.statementConfiguration = Objects.requireNonNull(statementConfiguration); - } - @Override public T configureStatement(Consumer consumer) { - consumer.accept(statementConfiguration); + parentStatement.configureStatement(consumer); return getThis(); } - protected WhereModel buildModel() { - return new WhereModel(getInitialCriterion(), subCriteria, statementConfiguration); + protected EmbeddedWhereModel buildModel() { + return new EmbeddedWhereModel.Builder() + .withInitialCriterion(getInitialCriterion()) + .withSubCriteria(subCriteria) + .build(); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/EmbeddedWhereModel.java b/src/main/java/org/mybatis/dynamic/sql/where/EmbeddedWhereModel.java new file mode 100644 index 000000000..acede2590 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/where/EmbeddedWhereModel.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016-2024 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.mybatis.dynamic.sql.where; + +import java.util.Optional; + +import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionModel; +import org.mybatis.dynamic.sql.render.RenderingContext; +import org.mybatis.dynamic.sql.util.FragmentAndParameters; +import org.mybatis.dynamic.sql.where.render.WhereRenderer; + +public class EmbeddedWhereModel extends AbstractBooleanExpressionModel { + private EmbeddedWhereModel(Builder builder) { + super(builder); + } + + public Optional render(RenderingContext renderingContext) { + return WhereRenderer.withWhereModel(this) + .withRenderingContext(renderingContext) + .build() + .render(); + } + + public static class Builder extends AbstractBuilder { + public EmbeddedWhereModel build() { + return new EmbeddedWhereModel(this); + } + + @Override + protected Builder getThis() { + return this; + } + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/where/WhereDSL.java b/src/main/java/org/mybatis/dynamic/sql/where/WhereDSL.java index 00082056f..0eb23f638 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/WhereDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/WhereDSL.java @@ -21,6 +21,11 @@ import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.util.Buildable; +/** + * DSL for standalone where clauses. + * + *

This can also be used to create reusable where clauses for different statements. + */ public class WhereDSL extends AbstractWhereStarter { private final StatementConfiguration statementConfiguration = new StatementConfiguration(); private final StandaloneWhereFinisher whereBuilder = new StandaloneWhereFinisher(); @@ -39,7 +44,7 @@ public WhereDSL configureStatement(Consumer consumer) { public class StandaloneWhereFinisher extends AbstractWhereFinisher implements Buildable { private StandaloneWhereFinisher() { - super(statementConfiguration); + super(WhereDSL.this); } @Override @@ -50,7 +55,11 @@ protected StandaloneWhereFinisher getThis() { @NotNull @Override public WhereModel build() { - return buildModel(); + return new WhereModel.Builder() + .withInitialCriterion(getInitialCriterion()) + .withSubCriteria(subCriteria) + .withStatementConfiguration(statementConfiguration) + .build(); } public WhereApplier toWhereApplier() { diff --git a/src/main/java/org/mybatis/dynamic/sql/where/WhereModel.java b/src/main/java/org/mybatis/dynamic/sql/where/WhereModel.java index 2d216cec9..8db761223 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/WhereModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/WhereModel.java @@ -15,12 +15,9 @@ */ package org.mybatis.dynamic.sql.where; -import java.util.List; import java.util.Objects; import java.util.Optional; -import org.mybatis.dynamic.sql.AndOrCriteriaGroup; -import org.mybatis.dynamic.sql.SqlCriterion; import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionModel; import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.RenderingContext; @@ -33,14 +30,9 @@ public class WhereModel extends AbstractBooleanExpressionModel { private final StatementConfiguration statementConfiguration; - public WhereModel(SqlCriterion initialCriterion, List subCriteria, - StatementConfiguration statementConfiguration) { - super(initialCriterion, subCriteria); - this.statementConfiguration = Objects.requireNonNull(statementConfiguration); - } - - public boolean isNonRenderingClauseAllowed() { - return statementConfiguration.isNonRenderingWhereClauseAllowed(); + private WhereModel(Builder builder) { + super(builder); + statementConfiguration = Objects.requireNonNull(builder.statementConfiguration); } /** @@ -52,7 +44,8 @@ public boolean isNonRenderingClauseAllowed() { * @return rendered where clause */ public Optional render(RenderingStrategy renderingStrategy) { - RenderingContext renderingContext = RenderingContext.withRenderingStrategy(renderingStrategy).build(); + RenderingContext renderingContext = RenderingContext.withRenderingStrategy(renderingStrategy) + .withStatementConfiguration(statementConfiguration).build(); return render(renderingContext); } @@ -62,6 +55,7 @@ public Optional render(RenderingStrategy renderingStrategy, RenderingContext renderingContext = RenderingContext .withRenderingStrategy(renderingStrategy) .withTableAliasCalculator(tableAliasCalculator) + .withStatementConfiguration(statementConfiguration) .build(); return render(renderingContext); @@ -71,6 +65,7 @@ public Optional render(RenderingStrategy renderingStrategy, RenderingContext renderingContext = RenderingContext .withRenderingStrategy(renderingStrategy) .withParameterName(parameterName) + .withStatementConfiguration(statementConfiguration) .build(); return render(renderingContext); @@ -82,6 +77,7 @@ public Optional render(RenderingStrategy renderingStrategy, .withRenderingStrategy(renderingStrategy) .withTableAliasCalculator(tableAliasCalculator) .withParameterName(parameterName) + .withStatementConfiguration(statementConfiguration) .build(); return render(renderingContext); @@ -100,4 +96,22 @@ private WhereClauseProvider toWhereClauseProvider(FragmentAndParameters fragment .withParameters(fragmentAndParameters.parameters()) .build(); } + + public static class Builder extends AbstractBuilder { + private StatementConfiguration statementConfiguration; + + public Builder withStatementConfiguration(StatementConfiguration statementConfiguration) { + this.statementConfiguration = statementConfiguration; + return this; + } + + public WhereModel build() { + return new WhereModel(this); + } + + @Override + protected Builder getThis() { + return this; + } + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/render/CriterionRenderer.java b/src/main/java/org/mybatis/dynamic/sql/where/render/CriterionRenderer.java index 4be08cc2d..6e3e13254 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/render/CriterionRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/render/CriterionRenderer.java @@ -30,7 +30,6 @@ import org.mybatis.dynamic.sql.SqlCriterion; import org.mybatis.dynamic.sql.SqlCriterionVisitor; import org.mybatis.dynamic.sql.render.RenderingContext; -import org.mybatis.dynamic.sql.select.render.SelectRenderer; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.FragmentCollector; @@ -121,11 +120,7 @@ private Optional renderColumnAndCondition(ColumnAndCo private FragmentAndParameters renderExists(ExistsCriterion criterion) { ExistsPredicate existsPredicate = criterion.existsPredicate(); - SelectStatementProvider selectStatement = SelectRenderer - .withSelectModel(existsPredicate.selectModelBuilder().build()) - .withRenderingContext(renderingContext) - .build() - .render(); + SelectStatementProvider selectStatement = existsPredicate.selectModelBuilder().build().render(renderingContext); String fragment = existsPredicate.operator() + " (" //$NON-NLS-1$ diff --git a/src/main/java/org/mybatis/dynamic/sql/where/render/DefaultConditionVisitor.java b/src/main/java/org/mybatis/dynamic/sql/where/render/DefaultConditionVisitor.java index 63ded9299..d9cb35283 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/render/DefaultConditionVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/render/DefaultConditionVisitor.java @@ -30,7 +30,6 @@ import org.mybatis.dynamic.sql.ConditionVisitor; import org.mybatis.dynamic.sql.render.RenderedParameterInfo; import org.mybatis.dynamic.sql.render.RenderingContext; -import org.mybatis.dynamic.sql.select.render.SelectRenderer; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.FragmentCollector; @@ -94,10 +93,7 @@ public FragmentAndParameters visit(AbstractTwoValueCondition condition) { @Override public FragmentAndParameters visit(AbstractSubselectCondition condition) { - SelectStatementProvider selectStatement = SelectRenderer.withSelectModel(condition.selectModel()) - .withRenderingContext(renderingContext) - .build() - .render(); + SelectStatementProvider selectStatement = condition.selectModel().render(renderingContext); String finalFragment = condition.operator() + " (" //$NON-NLS-1$ diff --git a/src/main/java/org/mybatis/dynamic/sql/where/render/WhereRenderer.java b/src/main/java/org/mybatis/dynamic/sql/where/render/WhereRenderer.java index 03e8f7eb5..244dcfa1b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/render/WhereRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/render/WhereRenderer.java @@ -17,12 +17,12 @@ import java.util.Optional; +import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionModel; import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionRenderer; import org.mybatis.dynamic.sql.exception.NonRenderingWhereClauseException; import org.mybatis.dynamic.sql.util.FragmentAndParameters; -import org.mybatis.dynamic.sql.where.WhereModel; -public class WhereRenderer extends AbstractBooleanExpressionRenderer { +public class WhereRenderer extends AbstractBooleanExpressionRenderer { private WhereRenderer(Builder builder) { super("where", builder); //$NON-NLS-1$ } @@ -31,19 +31,19 @@ private WhereRenderer(Builder builder) { public Optional render() { Optional whereClause = super.render(); - if (whereClause.isPresent() || model.isNonRenderingClauseAllowed()) { + if (whereClause.isPresent() || renderingContext.isNonRenderingClauseAllowed()) { return whereClause; } else { throw new NonRenderingWhereClauseException(); } } - public static Builder withWhereModel(WhereModel whereModel) { + public static Builder withWhereModel(AbstractBooleanExpressionModel whereModel) { return new Builder(whereModel); } - public static class Builder extends AbstractBuilder { - public Builder(WhereModel whereModel) { + public static class Builder extends AbstractBuilder { + public Builder(AbstractBooleanExpressionModel whereModel) { super(whereModel); } diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinMultiSelectBuilder.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinMultiSelectBuilder.kt index 199b044de..440232397 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinMultiSelectBuilder.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinMultiSelectBuilder.kt @@ -18,6 +18,7 @@ package org.mybatis.dynamic.sql.util.kotlin import org.mybatis.dynamic.sql.BasicColumn import org.mybatis.dynamic.sql.SortSpecification import org.mybatis.dynamic.sql.SqlBuilder +import org.mybatis.dynamic.sql.configuration.StatementConfiguration import org.mybatis.dynamic.sql.select.MultiSelectDSL import org.mybatis.dynamic.sql.select.MultiSelectModel import org.mybatis.dynamic.sql.util.Buildable @@ -74,6 +75,10 @@ class KotlinMultiSelectBuilder: Buildable { getDsl().fetchFirst(fetchFirstRows).rowsOnly() } + fun configureStatement(c: StatementConfiguration.() -> Unit) { + getDsl().configureStatement(c) + } + override fun build(): MultiSelectModel = getDsl().build() diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinSubQueryBuilders.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinSubQueryBuilders.kt index 90f8c8f80..cf5a12d8a 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinSubQueryBuilders.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinSubQueryBuilders.kt @@ -19,6 +19,7 @@ import org.mybatis.dynamic.sql.BasicColumn import org.mybatis.dynamic.sql.SqlBuilder import org.mybatis.dynamic.sql.SqlColumn import org.mybatis.dynamic.sql.SqlTable +import org.mybatis.dynamic.sql.configuration.StatementConfiguration import org.mybatis.dynamic.sql.insert.InsertSelectModel import org.mybatis.dynamic.sql.select.SelectModel import org.mybatis.dynamic.sql.util.Buildable @@ -63,6 +64,7 @@ typealias InsertSelectCompleter = KotlinInsertSelectSubQueryBuilder.() -> Unit class KotlinInsertSelectSubQueryBuilder : KotlinBaseSubQueryBuilder(), Buildable { private var columnList: List>? = null private var table: SqlTable? = null + private var statementConfigurator: (StatementConfiguration.() -> Unit)? = null fun into(table: SqlTable) { this.table = table @@ -74,18 +76,24 @@ class KotlinInsertSelectSubQueryBuilder : KotlinBaseSubQueryBuilder(), Buildable this.columnList = columnList } + fun configureStatement(c: StatementConfiguration.() -> Unit) { + statementConfigurator = c + } + override fun build(): InsertSelectModel { assertNotNull(table, "ERROR.29") //$NON-NLS-1$ - return if (columnList == null) { + val dsl = if (columnList == null) { SqlBuilder.insertInto(table) .withSelectStatement { buildSelectModel() } - .build() } else { SqlBuilder.insertInto(table) .withColumnList(columnList) .withSelectStatement { buildSelectModel() } - .build() } + + statementConfigurator?.let { dsl.configureStatement(it) } + + return dsl.build() } } diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/CaseDSLs.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/CaseDSLs.kt index e3f244f8e..98653f434 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/CaseDSLs.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/CaseDSLs.kt @@ -19,7 +19,7 @@ import org.mybatis.dynamic.sql.BasicColumn import org.mybatis.dynamic.sql.VisitableCondition import org.mybatis.dynamic.sql.select.caseexpression.BasicWhenCondition import org.mybatis.dynamic.sql.select.caseexpression.ConditionBasedWhenCondition -import org.mybatis.dynamic.sql.select.caseexpression.SearchedCaseModel.SearchedWhenCondition +import org.mybatis.dynamic.sql.select.caseexpression.SearchedCaseWhenCondition import org.mybatis.dynamic.sql.select.caseexpression.SimpleCaseWhenCondition import org.mybatis.dynamic.sql.util.kotlin.GroupingCriteriaCollector import org.mybatis.dynamic.sql.util.kotlin.assertNull @@ -30,12 +30,16 @@ class KSearchedCaseDSL : KElseDSL { assertNull(field, "ERROR.42") //$NON-NLS-1$ field = value } - internal val whenConditions = mutableListOf() + internal val whenConditions = mutableListOf() - fun `when`(dslCompleter: SearchedCaseCriteriaCollector.() -> Unit) { - val dsl = SearchedCaseCriteriaCollector().apply(dslCompleter) - whenConditions.add(SearchedWhenCondition(dsl.initialCriterion, dsl.subCriteria, dsl.thenValue)) - } + fun `when`(dslCompleter: SearchedCaseCriteriaCollector.() -> Unit) = + SearchedCaseCriteriaCollector().apply(dslCompleter).run { + whenConditions.add(SearchedCaseWhenCondition.Builder().withInitialCriterion(initialCriterion) + .withSubCriteria(subCriteria) + .withThenValue(thenValue) + .build()) + + } override fun `else`(column: BasicColumn) { this.elseValue = column diff --git a/src/site/markdown/docs/configuration.md b/src/site/markdown/docs/configuration.md index e98fba5f8..f413d4150 100644 --- a/src/site/markdown/docs/configuration.md +++ b/src/site/markdown/docs/configuration.md @@ -56,3 +56,32 @@ val deleteStatement = deleteFrom(person) { } ``` +## Configuration Scope with Select Statements + +Select statements can stand alone, or they can be embedded within other statements. For example, the library supports +writing insert statements with an embedded select, or select statements that contain other select statements for sub +queries. The select DSLs (both Java and Kotlin) appear to allow you to specify statement configuration on embedded +select statements, but this is not supported in point of fact. Statement configuration must ALWAYS be specified on the +outermost statement. Any configuration specified on embedded select statements will be ignored. We realize this could be +confusing! But we've made this decision hoping to minimize code duplication and maximize consistency. + +So the best practice is to ALWAYS specify the statement configuration as the LAST call to the DSL before calling +`build`, or before ending a Kotlin lambda. + +The following Kotlin code snippet shows this in action... + +```kotlin +val insertStatement = insertSelect { + into(person) + select(id, firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + where { id isGreaterThanOrEqualToWhenPresent null } + // the following will be ignored in favor of the enclosing statement configuration... + configureStatement { isNonRenderingWhereClauseAllowed = false } + } + configureStatement { isNonRenderingWhereClauseAllowed = true } +} +``` + +The inner `configureStatement` call will be ignored in this case, only the `configureStatement` call scoped to the +insert statement itself will be in effect. diff --git a/src/site/markdown/docs/extending.md b/src/site/markdown/docs/extending.md index f0827391a..ae04f434a 100644 --- a/src/site/markdown/docs/extending.md +++ b/src/site/markdown/docs/extending.md @@ -1,6 +1,6 @@ # Extending MyBatis Dynamic SQL -The library has been designed for extension from the very start of the design. We do not believe that the library +The library has been designed for extension from the very beginning. We do not believe that the library covers all possible uses, and we wanted to make it possible to add functionality that suits the needs of different projects. @@ -11,10 +11,18 @@ The SELECT support is the most complex part of the library, and also the part of extended. There are two main interfaces involved with extending the SELECT support. Picking which interface to implement is dependent on how you want to use your extension. -| Interface | Purpose | -|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------| -| `org.mybatis.dynamic.sql.BasicColumn` | Use this interface if you want to add capabilities to a SELECT list or a GROUP BY expression. For example, creating a calculated column. | -| `org.mybatis.dynamic.sql.BindableColumn` | Use this interface if you want to add capabilities to a WHERE clause. For example, creating a custom condition. | +| Interface | Purpose | +|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------| +| `org.mybatis.dynamic.sql.BasicColumn` | Use this interface if you want to add capabilities to a SELECT list or a GROUP BY expression. For example, using a database function. | +| `org.mybatis.dynamic.sql.BindableColumn` | Use this interface if you want to add capabilities to a WHERE clause. For example, creating a custom condition. | + +Rendering is the process of generating an appropriate SQL fragment to implement the function or calculated column. +The library will call a method `render(RenderingContext)` in your implementation. This method should return an +instance of `FragmentAndParameters` containing your desired SQL fragment and any bind parameters needed. Bind +parameter markers can be calculated by calling the `RenderingContext.calculateParameterInfo()` method. That method will +return a properly formatted bind marker for the SQL string, and a matching Map key you should use in your parameter map. +In general, you do not need to worry about adding spacing, commas, etc. before or after your fragment - the library +will properly format the final statement from all the different fragments. See the following sections for examples. @@ -166,6 +174,37 @@ public class Upper extends AbstractUniTypeFunction { } ``` +Note that `FragmentAndParameters` has a utility method that can simplify the implementation if you do not need to +add any new parameters to the resulting fragment. For example, the UPPER function can be simplified as follows: + +```java +import org.mybatis.dynamic.sql.render.RenderingContext; +import org.mybatis.dynamic.sql.select.function.AbstractUniTypeFunction; +import org.mybatis.dynamic.sql.util.FragmentAndParameters; + +public class Upper extends AbstractUniTypeFunction { + + private Upper(BindableColumn column) { + super(column); + } + + @Override + public FragmentAndParameters render(RenderingContext renderingContext) { + return = column.render(renderingContext).mapFragment(f -> "upper(" + f + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + protected Upper copy() { + return new Upper(column); + } + + public static Upper of(BindableColumn column) { + return new Upper(column); + } +} +``` + + ### OperatorFunction Example The following function implements the concatenate operator. Note that the operator can be applied to list of columns of diff --git a/src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java b/src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java index 22855da4b..c6113c842 100644 --- a/src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.common.OrderByModel; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.exception.DynamicSqlException; import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.insert.BatchInsertModel; @@ -243,6 +244,7 @@ void testInvalidPagingModel() { RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) + .withStatementConfiguration(new StatementConfiguration()) .build(); assertThat(pagingModel).isPresent(); diff --git a/src/test/java/org/mybatis/dynamic/sql/mybatis3/CriterionRendererTest.java b/src/test/java/org/mybatis/dynamic/sql/mybatis3/CriterionRendererTest.java index 29161000f..ef82f3322 100644 --- a/src/test/java/org/mybatis/dynamic/sql/mybatis3/CriterionRendererTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/mybatis3/CriterionRendererTest.java @@ -27,6 +27,7 @@ import org.mybatis.dynamic.sql.SqlBuilder; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategies; @@ -45,7 +46,8 @@ void testAliasWithIgnore() { .withCondition(condition) .build(); - RenderingContext renderingContext =RenderingContext.withRenderingStrategy(RenderingStrategies.MYBATIS3).build(); + RenderingContext renderingContext =RenderingContext.withRenderingStrategy(RenderingStrategies.MYBATIS3) + .withStatementConfiguration(new StatementConfiguration()).build(); CriterionRenderer renderer = new CriterionRenderer(renderingContext); @@ -70,6 +72,7 @@ void testAliasWithoutIgnore() { RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) .withTableAliasCalculator(ExplicitTableAliasCalculator.of(tableAliases)) + .withStatementConfiguration(new StatementConfiguration()) .build(); CriterionRenderer renderer = new CriterionRenderer(renderingContext); @@ -97,6 +100,7 @@ void testTypeHandler() { RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) + .withStatementConfiguration(new StatementConfiguration()) .build(); CriterionRenderer renderer = new CriterionRenderer(renderingContext); @@ -122,6 +126,7 @@ void testTypeHandlerAndAlias() { RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) .withTableAliasCalculator(ExplicitTableAliasCalculator.of(tableAliases)) + .withStatementConfiguration(new StatementConfiguration()) .build(); CriterionRenderer renderer = new CriterionRenderer(renderingContext); diff --git a/src/test/java/org/mybatis/dynamic/sql/select/HavingModelTest.java b/src/test/java/org/mybatis/dynamic/sql/select/HavingModelTest.java index b56611341..47d015da2 100644 --- a/src/test/java/org/mybatis/dynamic/sql/select/HavingModelTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/select/HavingModelTest.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.dynamic.sql.select.render.HavingRenderer; @@ -65,6 +66,7 @@ void testComplexHaving() { private Optional renderHavingModel(HavingModel havingModel) { RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.SPRING_NAMED_PARAMETER) + .withStatementConfiguration(new StatementConfiguration()) .build(); return HavingRenderer.withHavingModel(havingModel) diff --git a/src/test/java/org/mybatis/dynamic/sql/where/render/CriterionRendererTest.java b/src/test/java/org/mybatis/dynamic/sql/where/render/CriterionRendererTest.java index a706d3d7d..39d297fbd 100644 --- a/src/test/java/org/mybatis/dynamic/sql/where/render/CriterionRendererTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/where/render/CriterionRendererTest.java @@ -26,6 +26,7 @@ import org.mybatis.dynamic.sql.ColumnAndConditionCriterion; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.configuration.StatementConfiguration; import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategies; @@ -46,6 +47,7 @@ void testAliasWithIgnore() { RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) + .withStatementConfiguration(new StatementConfiguration()) .build(); CriterionRenderer renderer = new CriterionRenderer(renderingContext); @@ -72,6 +74,7 @@ void testAliasWithoutIgnore() { RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) .withTableAliasCalculator(ExplicitTableAliasCalculator.of(tableAliases)) + .withStatementConfiguration(new StatementConfiguration()) .build(); CriterionRenderer renderer = new CriterionRenderer(renderingContext); diff --git a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt index f3d69db30..e09d91210 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt @@ -28,10 +28,12 @@ import examples.kotlin.mybatis3.canonical.PersonDynamicSqlSupport.person import org.apache.ibatis.session.ExecutorType import org.apache.ibatis.session.SqlSessionFactory import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance.Lifecycle +import org.mybatis.dynamic.sql.exception.NonRenderingWhereClauseException import org.mybatis.dynamic.sql.util.kotlin.elements.add import org.mybatis.dynamic.sql.util.kotlin.elements.constant import org.mybatis.dynamic.sql.util.kotlin.elements.isIn @@ -824,4 +826,83 @@ class PersonMapperTest { } } } + + @Test + fun testMultiSelectWithNonRenderingWhereClauseDisAllowed() { + assertThatExceptionOfType(NonRenderingWhereClauseException::class.java).isThrownBy { + multiSelect { + select(id.`as`("A_ID"), firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + where { id isLessThanOrEqualTo 2 } + orderBy(id) + limit(1) + } + union { + select(id.`as`("A_ID"), firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + where { id isGreaterThanOrEqualToWhenPresent null } + orderBy(id.descending()) + limit(1) + } + } + orderBy(sortColumn("A_ID")) + limit(2) + offset(1) + } + } + } + + @Test + fun testMultiSelectWithNonRenderingWhereClauseAllowed() { + val selectStatement = multiSelect { + select(id, firstName) { + from(person) + where { id isLessThanOrEqualTo 2 } + } + union { + select(id, firstName) { + from(person) + where { id isGreaterThanOrEqualToWhenPresent null } + // following should be ignored in favor of the statement configuration... + configureStatement { isNonRenderingWhereClauseAllowed = false } + } + } + configureStatement { isNonRenderingWhereClauseAllowed = true } + } + + val expected = "(select id, first_name from Person where id <= #{parameters.p1,jdbcType=INTEGER}) " + + "union (select id, first_name from Person)" + assertThat(selectStatement.selectStatement).isEqualTo(expected) + } + + @Test + fun testInsertSelectWithNonRenderingWhereClauseDisAllowed() { + assertThatExceptionOfType(NonRenderingWhereClauseException::class.java).isThrownBy { + insertSelect { + into(person) + select(id, firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + where { id isGreaterThanOrEqualToWhenPresent null } + } + } + } + } + + @Test + fun testInsertSelectWithNonRenderingWhereClauseAllowed() { + val insertStatement = insertSelect { + into(person) + select(id, firstName, lastName, birthDate, employed, occupation, addressId) { + from(person) + where { id isGreaterThanOrEqualToWhenPresent null } + // following should be ignored in favor of the statement configuration... + configureStatement { isNonRenderingWhereClauseAllowed = false } + } + configureStatement { isNonRenderingWhereClauseAllowed = true } + } + + val expected = "insert into Person " + + "select id, first_name, last_name, birth_date, employed, occupation, address_id from Person" + assertThat(insertStatement.insertStatement).isEqualTo(expected) + } }