diff --git a/src/main/java/org/mybatis/dynamic/sql/BasicColumn.java b/src/main/java/org/mybatis/dynamic/sql/BasicColumn.java index 1c848ff88..4f194be79 100644 --- a/src/main/java/org/mybatis/dynamic/sql/BasicColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/BasicColumn.java @@ -62,6 +62,7 @@ public interface BasicColumn { default FragmentAndParameters render(RenderingContext renderingContext) { // the default implementation ensures compatibility with prior releases. When the // deprecated renderWithTableAlias method is removed, this function can become purely abstract. + // Also remove the method tableAliasCalculator() from RenderingContext. return FragmentAndParameters.fromFragment(renderWithTableAlias(renderingContext.tableAliasCalculator())); } diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index 59e69b050..1095f1144 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -18,12 +18,10 @@ import java.sql.JDBCType; import java.util.Objects; import java.util.Optional; -import java.util.function.BiFunction; import org.jetbrains.annotations.NotNull; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategy; -import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -37,7 +35,7 @@ public class SqlColumn implements BindableColumn, SortSpecification { protected final String typeHandler; protected final RenderingStrategy renderingStrategy; protected final ParameterTypeConverter parameterTypeConverter; - protected final BiFunction> tableQualifierFunction; + protected final String tableQualifier; protected final Class javaType; private SqlColumn(Builder builder) { @@ -49,7 +47,7 @@ private SqlColumn(Builder builder) { typeHandler = builder.typeHandler; renderingStrategy = builder.renderingStrategy; parameterTypeConverter = builder.parameterTypeConverter; - tableQualifierFunction = Objects.requireNonNull(builder.tableQualifierFunction); + tableQualifier = builder.tableQualifier; javaType = builder.javaType; } @@ -107,7 +105,7 @@ public SqlColumn as(String alias) { */ public SqlColumn qualifiedWith(String tableQualifier) { Builder b = copy(); - b.withTableQualifierFunction((tac, t) -> Optional.of(tableQualifier)); + b.withTableQualifier(tableQualifier); return b.build(); } @@ -117,7 +115,7 @@ public SqlColumn qualifiedWith(String tableQualifier) { * a map based on the column name returned from the database. * *

A camel case string is mixed case, and most databases do not support unquoted mixed case strings - * as identifiers. Therefore the generated alias will be surrounded by double quotes thereby making it a + * as identifiers. Therefore, the generated alias will be surrounded by double quotes thereby making it a * quoted identifier. Most databases will respect quoted mixed case identifiers. * * @return a new column aliased with a camel case version of the column name @@ -139,11 +137,11 @@ public String orderByName() { @Override public FragmentAndParameters render(RenderingContext renderingContext) { - String fragment = tableQualifierFunction.apply(renderingContext.tableAliasCalculator(), table) - .map(this::applyTableAlias) - .orElseGet(this::name); - - return FragmentAndParameters.fromFragment(fragment); + if (tableQualifier == null) { + return FragmentAndParameters.fromFragment(renderingContext.aliasedColumnName(this)); + } else { + return FragmentAndParameters.fromFragment(renderingContext.aliasedColumnName(this, tableQualifier)); + } } @Override @@ -195,14 +193,10 @@ private Builder copy() { .withTypeHandler(this.typeHandler) .withRenderingStrategy(this.renderingStrategy) .withParameterTypeConverter((ParameterTypeConverter) this.parameterTypeConverter) - .withTableQualifierFunction(this.tableQualifierFunction) + .withTableQualifier(this.tableQualifier) .withJavaType((Class) this.javaType); } - private String applyTableAlias(String tableAlias) { - return tableAlias + "." + name(); //$NON-NLS-1$ - } - public static SqlColumn of(String name, SqlTable table) { return new Builder().withName(name) .withTable(table) @@ -225,8 +219,7 @@ public static class Builder { protected String typeHandler; protected RenderingStrategy renderingStrategy; protected ParameterTypeConverter parameterTypeConverter; - protected BiFunction> tableQualifierFunction = - TableAliasCalculator::aliasForColumn; + protected String tableQualifier; protected Class javaType; public Builder withName(String name) { @@ -269,9 +262,8 @@ public Builder withParameterTypeConverter(ParameterTypeConverter parame return this; } - private Builder withTableQualifierFunction( - BiFunction> tableQualifierFunction) { - this.tableQualifierFunction = tableQualifierFunction; + private Builder withTableQualifier(String tableQualifier) { + this.tableQualifier = tableQualifier; return this; } 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 0650f30ec..9e78b763b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/common/AbstractBooleanExpressionRenderer.java @@ -36,11 +36,7 @@ public abstract class AbstractBooleanExpressionRenderer builder) { model = Objects.requireNonNull(builder.model); this.prefix = Objects.requireNonNull(prefix); - - criterionRenderer = new CriterionRenderer.Builder() - .withRenderingContext(Objects.requireNonNull(builder.renderingContext)) - .withParameterName(builder.parameterName) - .build(); + criterionRenderer = new CriterionRenderer(builder.renderingContext); } public Optional render() { @@ -89,7 +85,6 @@ private String addPrefix(String fragment) { public abstract static class AbstractBuilder> { private final M model; - private String parameterName; private RenderingContext renderingContext; protected AbstractBuilder(M model) { @@ -101,11 +96,6 @@ public B withRenderingContext(RenderingContext renderingContext) { return getThis(); } - public B withParameterName(String parameterName) { - this.parameterName = parameterName; - return getThis(); - } - protected abstract B getThis(); } } 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 e1b591ec8..7191b39cf 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 @@ -17,10 +17,8 @@ import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.common.OrderByModel; import org.mybatis.dynamic.sql.common.OrderByRenderer; import org.mybatis.dynamic.sql.delete.DeleteModel; @@ -42,10 +40,9 @@ private DeleteRenderer(Builder builder) { TableAliasCalculator tableAliasCalculator = builder.deleteModel.tableAlias() .map(a -> ExplicitTableAliasCalculator.of(deleteModel.table(), a)) .orElseGet(TableAliasCalculator::empty); - renderingContext = new RenderingContext.Builder() + renderingContext = RenderingContext .withRenderingStrategy(Objects.requireNonNull(builder.renderingStrategy)) .withTableAliasCalculator(tableAliasCalculator) - .withSequence(new AtomicInteger(1)) .build(); } @@ -68,11 +65,7 @@ private DeleteStatementProvider toDeleteStatementProvider(FragmentCollector frag } private FragmentAndParameters calculateDeleteStatementStart() { - SqlTable table = deleteModel.table(); - String tableName = table.tableNameAtRuntime(); - String aliasedTableName = renderingContext.tableAliasCalculator().aliasForTable(table) - .map(a -> tableName + " " + a).orElse(tableName); //$NON-NLS-1$ - + String aliasedTableName = renderingContext.aliasedTableName(deleteModel.table()); return FragmentAndParameters.fromFragment("delete from " + aliasedTableName); //$NON-NLS-1$ } @@ -92,12 +85,10 @@ private Optional calculateLimitClause() { } private FragmentAndParameters renderLimitClause(Long limit) { - String mapKey = renderingContext.nextMapKey(); - String jdbcPlaceholder = renderingContext - .renderingStrategy().getFormattedJdbcPlaceholder(RenderingStrategy.DEFAULT_PARAMETER_PREFIX, mapKey); + RenderingContext.ParameterInfo parameterInfo = renderingContext.calculateParameterInfo(); - return FragmentAndParameters.withFragment("limit " + jdbcPlaceholder) //$NON-NLS-1$ - .withParameter(mapKey, limit) + return FragmentAndParameters.withFragment("limit " + parameterInfo.renderedPlaceHolder()) //$NON-NLS-1$ + .withParameter(parameterInfo.mapKey(), limit) .build(); } 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 3acb2d6b2..5cdee9706 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 @@ -20,6 +20,7 @@ import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.insert.GeneralInsertModel; +import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.util.Messages; @@ -34,7 +35,9 @@ private GeneralInsertRenderer(Builder builder) { } public GeneralInsertStatementProvider render() { - GeneralInsertValuePhraseVisitor visitor = new GeneralInsertValuePhraseVisitor(renderingStrategy); + RenderingContext renderingContext = RenderingContext.withRenderingStrategy(renderingStrategy).build(); + + GeneralInsertValuePhraseVisitor visitor = new GeneralInsertValuePhraseVisitor(renderingContext); FieldAndValueCollector collector = model.mapColumnMappings(m -> m.accept(visitor)) .filter(Optional::isPresent) .map(Optional::get) 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 ca3222c8a..86092cb8c 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 @@ -15,11 +15,10 @@ */ package org.mybatis.dynamic.sql.insert.render; +import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; -import org.mybatis.dynamic.sql.SqlColumn; -import org.mybatis.dynamic.sql.render.RenderingStrategy; +import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.ConstantMapping; import org.mybatis.dynamic.sql.util.GeneralInsertMappingVisitor; @@ -31,11 +30,10 @@ public class GeneralInsertValuePhraseVisitor extends GeneralInsertMappingVisitor> { - private final RenderingStrategy renderingStrategy; - private final AtomicInteger sequence = new AtomicInteger(1); + private final RenderingContext renderingContext; - public GeneralInsertValuePhraseVisitor(RenderingStrategy renderingStrategy) { - this.renderingStrategy = renderingStrategy; + public GeneralInsertValuePhraseVisitor(RenderingContext renderingContext) { + this.renderingContext = Objects.requireNonNull(renderingContext); } @Override @@ -85,18 +83,11 @@ private Optional buildNullFragment(AbstractColumnMap } private Optional buildFragment(AbstractColumnMapping mapping, Object value) { - String mapKey = renderingStrategy.formatParameterMapKey(sequence); - - String jdbcPlaceholder = mapping.mapColumn(c -> calculateJdbcPlaceholder(c, mapKey)); + RenderingContext.ParameterInfo parameterInfo = mapping.mapColumn(renderingContext::calculateParameterInfo); return FieldAndValueAndParameters.withFieldName(mapping.columnName()) - .withValuePhrase(jdbcPlaceholder) - .withParameter(mapKey, value) + .withValuePhrase(parameterInfo.renderedPlaceHolder()) + .withParameter(parameterInfo.mapKey(), value) .buildOptional(); } - - private String calculateJdbcPlaceholder(SqlColumn column, String parameterName) { - return column.renderingStrategy().orElse(renderingStrategy) - .getFormattedJdbcPlaceholder(column, RenderingStrategy.DEFAULT_PARAMETER_PREFIX, parameterName); - } } 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 e5e5a8118..96f8db794 100644 --- a/src/main/java/org/mybatis/dynamic/sql/render/RenderingContext.java +++ b/src/main/java/org/mybatis/dynamic/sql/render/RenderingContext.java @@ -15,41 +15,123 @@ */ package org.mybatis.dynamic.sql.render; +import static org.mybatis.dynamic.sql.util.StringUtilities.spaceBefore; + import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; +import org.mybatis.dynamic.sql.BindableColumn; +import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.SqlTable; + +/** + * This class encapsulates all the supporting items related to rendering, and contains many utility methods + * used during the rendering process. + * + * @since 1.5.1 + * @author Jeff Butler + */ public class RenderingContext { private final AtomicInteger sequence; private final RenderingStrategy renderingStrategy; private final TableAliasCalculator tableAliasCalculator; + private final String configuredParameterName; + private final String calculatedParameterName; private RenderingContext(Builder builder) { - sequence = Objects.requireNonNull(builder.sequence); + this.sequence = builder.sequence == null ? new AtomicInteger(1) : builder.sequence; renderingStrategy = Objects.requireNonNull(builder.renderingStrategy); tableAliasCalculator = Objects.requireNonNull(builder.tableAliasCalculator); + configuredParameterName = builder.parameterName; + if (configuredParameterName == null) { + calculatedParameterName = RenderingStrategy.DEFAULT_PARAMETER_PREFIX; + } else { + calculatedParameterName = + configuredParameterName + "." + RenderingStrategy.DEFAULT_PARAMETER_PREFIX; //$NON-NLS-1$ + } } - public AtomicInteger sequence() { - return sequence; + public TableAliasCalculator tableAliasCalculator() { + // this method can be removed when the renderWithTableAlias method is removed from BasicColumn + return tableAliasCalculator; } - public RenderingStrategy renderingStrategy() { - return renderingStrategy; + private String nextMapKey() { + return renderingStrategy.formatParameterMapKey(sequence); } - public TableAliasCalculator tableAliasCalculator() { - return tableAliasCalculator; + private String renderedPlaceHolder(String mapKey) { + return renderingStrategy.getFormattedJdbcPlaceholder(calculatedParameterName, mapKey); } - public String nextMapKey() { - return renderingStrategy.formatParameterMapKey(sequence); + private String renderedPlaceHolder(String mapKey, BindableColumn column) { + return column.renderingStrategy().orElse(renderingStrategy) + .getFormattedJdbcPlaceholder(column, calculatedParameterName, mapKey); + } + + public ParameterInfo calculateParameterInfo() { + ParameterInfo p = new ParameterInfo(); + p.mapKey = nextMapKey(); + p.renderedPlaceHolder = renderedPlaceHolder(p.mapKey); + return p; + } + + public ParameterInfo calculateParameterInfo(BindableColumn column) { + ParameterInfo p = new ParameterInfo(); + p.mapKey = nextMapKey(); + p.renderedPlaceHolder = renderedPlaceHolder(p.mapKey, column); + return p; + } + + public String aliasedColumnName(SqlColumn column) { + return tableAliasCalculator.aliasForColumn(column.table()) + .map(alias -> aliasedColumnName(column, alias)) + .orElseGet(column::name); + } + + public String aliasedColumnName(SqlColumn column, String explicitAlias) { + return explicitAlias + "." + column.name(); //$NON-NLS-1$ + } + + public String aliasedTableName(SqlTable table) { + return tableAliasCalculator.aliasForTable(table) + .map(a -> table.tableNameAtRuntime() + spaceBefore(a)) + .orElseGet(table::tableNameAtRuntime); + } + + /** + * Crete 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 + * may change during rendering. + * + * @param childTableAliasCalculator the child table alias calculator + * @return a new rendering context whose table alias calculator is composed of the former calculator as parent, and + * the new child calculator + */ + public RenderingContext withChildTableAliasCalculator(TableAliasCalculator childTableAliasCalculator) { + TableAliasCalculator tac = new TableAliasCalculatorWithParent.Builder() + .withParent(tableAliasCalculator) + .withChild(childTableAliasCalculator) + .build(); + + return new Builder() + .withRenderingStrategy(this.renderingStrategy) + .withSequence(this.sequence) + .withParameterName(this.configuredParameterName) + .withTableAliasCalculator(tac) + .build(); + } + + public static Builder withRenderingStrategy(RenderingStrategy renderingStrategy) { + return new Builder().withRenderingStrategy(renderingStrategy); } public static class Builder { private AtomicInteger sequence; private RenderingStrategy renderingStrategy; - private TableAliasCalculator tableAliasCalculator; + private TableAliasCalculator tableAliasCalculator = TableAliasCalculator.empty(); + private String parameterName; public Builder withSequence(AtomicInteger sequence) { this.sequence = sequence; @@ -66,8 +148,26 @@ public Builder withTableAliasCalculator(TableAliasCalculator tableAliasCalculato return this; } + public Builder withParameterName(String parameterName) { + this.parameterName = parameterName; + return this; + } + public RenderingContext build() { return new RenderingContext(this); } } + + public static class ParameterInfo { + private String mapKey; + private String renderedPlaceHolder; + + public String mapKey() { + return mapKey; + } + + public String renderedPlaceHolder() { + return renderedPlaceHolder; + } + } } 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 123db5056..c2ee62794 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java @@ -19,7 +19,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Stream; @@ -28,7 +27,6 @@ import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategy; -import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.select.render.SelectRenderer; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.Messages; @@ -62,11 +60,7 @@ public Optional pagingModel() { @NotNull public SelectStatementProvider render(RenderingStrategy renderingStrategy) { - RenderingContext renderingContext = new RenderingContext.Builder() - .withRenderingStrategy(renderingStrategy) - .withSequence(new AtomicInteger(1)) - .withTableAliasCalculator(TableAliasCalculator.empty()) - .build(); + RenderingContext renderingContext = RenderingContext.withRenderingStrategy(renderingStrategy).build(); return SelectRenderer.withSelectModel(this) .withRenderingContext(renderingContext) .build() diff --git a/src/main/java/org/mybatis/dynamic/sql/select/aggregate/Sum.java b/src/main/java/org/mybatis/dynamic/sql/select/aggregate/Sum.java index 628b166f2..96f100015 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/aggregate/Sum.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/aggregate/Sum.java @@ -17,9 +17,11 @@ import org.mybatis.dynamic.sql.BindableColumn; import org.mybatis.dynamic.sql.VisitableCondition; +import org.mybatis.dynamic.sql.exception.DynamicSqlException; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.select.function.AbstractUniTypeFunction; import org.mybatis.dynamic.sql.util.FragmentAndParameters; +import org.mybatis.dynamic.sql.util.Messages; import org.mybatis.dynamic.sql.where.render.DefaultConditionVisitor; public class Sum extends AbstractUniTypeFunction> { @@ -52,6 +54,10 @@ private FragmentAndParameters renderWithoutCondition(RenderingContext renderingC } private FragmentAndParameters renderWithCondition(RenderingContext renderingContext) { + if (!condition.shouldRender()) { + throw new DynamicSqlException(Messages.getString("ERROR.37", "sum")); //$NON-NLS-1$ //$NON-NLS-2$ + } + DefaultConditionVisitor visitor = new DefaultConditionVisitor.Builder() .withColumn(column) .withRenderingContext(renderingContext) diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/FetchFirstPagingModelRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/FetchFirstPagingModelRenderer.java index 892732ed6..19a50eaa9 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/FetchFirstPagingModelRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/FetchFirstPagingModelRenderer.java @@ -17,7 +17,6 @@ import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.render.RenderingContext; -import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.PagingModel; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.InternalError; @@ -51,35 +50,30 @@ private FragmentAndParameters renderFetchFirstRowsOnly() { } private FragmentAndParameters renderFetchFirstRowsOnly(Long fetchFirstRows) { - String mapKey = renderingContext.nextMapKey(); + RenderingContext.ParameterInfo parameterInfo = renderingContext.calculateParameterInfo(); return FragmentAndParameters - .withFragment("fetch first " + renderPlaceholder(mapKey) //$NON-NLS-1$ + .withFragment("fetch first " + parameterInfo.renderedPlaceHolder() //$NON-NLS-1$ + " rows only") //$NON-NLS-1$ - .withParameter(mapKey, fetchFirstRows) + .withParameter(parameterInfo.mapKey(), fetchFirstRows) .build(); } private FragmentAndParameters renderOffsetOnly(Long offset) { - String mapKey = renderingContext.nextMapKey(); - return FragmentAndParameters.withFragment("offset " + renderPlaceholder(mapKey) //$NON-NLS-1$ + RenderingContext.ParameterInfo parameterInfo = renderingContext.calculateParameterInfo(); + return FragmentAndParameters.withFragment("offset " + parameterInfo.renderedPlaceHolder() //$NON-NLS-1$ + " rows") //$NON-NLS-1$ - .withParameter(mapKey, offset) + .withParameter(parameterInfo.mapKey(), offset) .build(); } private FragmentAndParameters renderOffsetAndFetchFirstRows(Long offset, Long fetchFirstRows) { - String mapKey1 = renderingContext.nextMapKey(); - String mapKey2 = renderingContext.nextMapKey(); - return FragmentAndParameters.withFragment("offset " + renderPlaceholder(mapKey1) //$NON-NLS-1$ - + " rows fetch first " + renderPlaceholder(mapKey2) //$NON-NLS-1$ + RenderingContext.ParameterInfo parameterInfo1 = renderingContext.calculateParameterInfo(); + RenderingContext.ParameterInfo parameterInfo2 = renderingContext.calculateParameterInfo(); + return FragmentAndParameters.withFragment("offset " + parameterInfo1.renderedPlaceHolder() //$NON-NLS-1$ + + " rows fetch first " + parameterInfo2.renderedPlaceHolder() //$NON-NLS-1$ + " rows only") //$NON-NLS-1$ - .withParameter(mapKey1, offset) - .withParameter(mapKey2, fetchFirstRows) + .withParameter(parameterInfo1.mapKey(), offset) + .withParameter(parameterInfo2.mapKey(), fetchFirstRows) .build(); } - - private String renderPlaceholder(String parameterName) { - return renderingContext.renderingStrategy() - .getFormattedJdbcPlaceholder(RenderingStrategy.DEFAULT_PARAMETER_PREFIX, parameterName); - } } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/JoinConditionRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/JoinConditionRenderer.java index 5f76bc1e4..2b95cd669 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/JoinConditionRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/JoinConditionRenderer.java @@ -19,10 +19,8 @@ import java.util.Objects; -import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.BindableColumn; import org.mybatis.dynamic.sql.render.RenderingContext; -import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.join.ColumnBasedJoinCondition; import org.mybatis.dynamic.sql.select.join.JoinConditionVisitor; import org.mybatis.dynamic.sql.select.join.TypedJoinCondition; @@ -39,29 +37,23 @@ private JoinConditionRenderer(Builder builder) { @Override public FragmentAndParameters visit(TypedJoinCondition condition) { - String mapKey = renderingContext.nextMapKey(); + RenderingContext.ParameterInfo parameterInfo = renderingContext.calculateParameterInfo(leftColumn); - String placeHolder = leftColumn.renderingStrategy().orElse(renderingContext.renderingStrategy()) - .getFormattedJdbcPlaceholder(leftColumn, RenderingStrategy.DEFAULT_PARAMETER_PREFIX, mapKey); - - return FragmentAndParameters.withFragment(condition.operator() + spaceBefore(placeHolder)) - .withParameter(mapKey, condition.value()) + return FragmentAndParameters + .withFragment(condition.operator() + spaceBefore(parameterInfo.renderedPlaceHolder())) + .withParameter(parameterInfo.mapKey(), condition.value()) .build(); } @Override public FragmentAndParameters visit(ColumnBasedJoinCondition condition) { - FragmentAndParameters renderedColumn = applyTableAlias(condition.rightColumn()); + FragmentAndParameters renderedColumn = condition.rightColumn().render(renderingContext); return FragmentAndParameters .withFragment(condition.operator() + spaceBefore(renderedColumn.fragment())) .withParameters(renderedColumn.parameters()) .build(); } - private FragmentAndParameters applyTableAlias(BasicColumn column) { - return column.render(renderingContext); - } - public static class Builder { private BindableColumn leftColumn; private RenderingContext renderingContext; diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/JoinRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/JoinRenderer.java index 90a20d3a7..5904c2a74 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/JoinRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/JoinRenderer.java @@ -20,7 +20,6 @@ import java.util.Objects; import java.util.stream.Collectors; -import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.select.join.JoinCriterion; import org.mybatis.dynamic.sql.select.join.JoinModel; @@ -73,7 +72,7 @@ private FragmentAndParameters renderConditions(JoinSpecification joinSpecificati } private FragmentAndParameters renderCriterion(JoinCriterion joinCriterion) { - FragmentAndParameters renderedColumn = applyTableAlias(joinCriterion.leftColumn()); + FragmentAndParameters renderedColumn = joinCriterion.leftColumn().render(renderingContext); String prefix = joinCriterion.connector() + spaceBefore(renderedColumn.fragment()); @@ -91,10 +90,6 @@ private FragmentAndParameters renderCriterion(JoinCriterion joinCriterion .build(); } - private FragmentAndParameters applyTableAlias(BasicColumn column) { - return column.render(renderingContext); - } - public static Builder withJoinModel(JoinModel joinModel) { return new Builder().withJoinModel(joinModel); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/LimitAndOffsetPagingModelRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/LimitAndOffsetPagingModelRenderer.java index 8c07a83c3..25b32767e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/LimitAndOffsetPagingModelRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/LimitAndOffsetPagingModelRenderer.java @@ -18,7 +18,6 @@ import java.util.Objects; import org.mybatis.dynamic.sql.render.RenderingContext; -import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.PagingModel; import org.mybatis.dynamic.sql.util.FragmentAndParameters; @@ -40,24 +39,19 @@ public FragmentAndParameters render() { } private FragmentAndParameters renderLimitOnly() { - String mapKey = renderingContext.nextMapKey(); - return FragmentAndParameters.withFragment("limit " + renderPlaceholder(mapKey)) //$NON-NLS-1$ - .withParameter(mapKey, limit) + RenderingContext.ParameterInfo parameterInfo = renderingContext.calculateParameterInfo(); + return FragmentAndParameters.withFragment("limit " + parameterInfo.renderedPlaceHolder()) //$NON-NLS-1$ + .withParameter(parameterInfo.mapKey(), limit) .build(); } private FragmentAndParameters renderLimitAndOffset(Long offset) { - String mapKey1 = renderingContext.nextMapKey(); - String mapKey2 = renderingContext.nextMapKey(); - return FragmentAndParameters.withFragment("limit " + renderPlaceholder(mapKey1) //$NON-NLS-1$ - + " offset " + renderPlaceholder(mapKey2)) //$NON-NLS-1$ - .withParameter(mapKey1, limit) - .withParameter(mapKey2, offset) + RenderingContext.ParameterInfo parameterInfo1 = renderingContext.calculateParameterInfo(); + RenderingContext.ParameterInfo parameterInfo2 = renderingContext.calculateParameterInfo(); + return FragmentAndParameters.withFragment("limit " + parameterInfo1.renderedPlaceHolder() //$NON-NLS-1$ + + " offset " + parameterInfo2.renderedPlaceHolder()) //$NON-NLS-1$ + .withParameter(parameterInfo1.mapKey(), limit) + .withParameter(parameterInfo2.mapKey(), offset) .build(); } - - private String renderPlaceholder(String parameterName) { - return renderingContext.renderingStrategy() - .getFormattedJdbcPlaceholder(RenderingStrategy.DEFAULT_PARAMETER_PREFIX, parameterName); - } } 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 90be1f79b..b0cece9d7 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 @@ -17,14 +17,12 @@ import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import org.mybatis.dynamic.sql.common.OrderByModel; import org.mybatis.dynamic.sql.common.OrderByRenderer; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategy; -import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.select.MultiSelectModel; import org.mybatis.dynamic.sql.select.PagingModel; import org.mybatis.dynamic.sql.select.SelectModel; @@ -37,9 +35,7 @@ public class MultiSelectRenderer { private final RenderingContext renderingContext; private MultiSelectRenderer(Builder builder) { - renderingContext = new RenderingContext.Builder() - .withTableAliasCalculator(TableAliasCalculator.empty()) - .withSequence(new AtomicInteger(1)) + renderingContext = RenderingContext .withRenderingStrategy(Objects.requireNonNull(builder.renderingStrategy)) .build(); multiSelectModel = Objects.requireNonNull(builder.multiSelectModel); 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 d424d62b8..8a54794e7 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 @@ -25,7 +25,6 @@ import org.mybatis.dynamic.sql.render.GuaranteedTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.TableAliasCalculator; -import org.mybatis.dynamic.sql.render.TableAliasCalculatorWithParent; import org.mybatis.dynamic.sql.select.GroupByModel; import org.mybatis.dynamic.sql.select.HavingModel; import org.mybatis.dynamic.sql.select.QueryExpressionModel; @@ -43,14 +42,9 @@ public class QueryExpressionRenderer { private QueryExpressionRenderer(Builder builder) { queryExpression = Objects.requireNonNull(builder.queryExpression); - TableAliasCalculator tableAliasCalculator = - calculateTableAliasCalculator(queryExpression, builder.renderingContext.tableAliasCalculator()); + TableAliasCalculator childTableAliasCalculator = calculateChildTableAliasCalculator(queryExpression); - renderingContext = new RenderingContext.Builder() - .withRenderingStrategy(Objects.requireNonNull(builder.renderingContext.renderingStrategy())) - .withSequence(builder.renderingContext.sequence()) - .withTableAliasCalculator(tableAliasCalculator) - .build(); + renderingContext = builder.renderingContext.withChildTableAliasCalculator(childTableAliasCalculator); tableExpressionRenderer = new TableExpressionRenderer.Builder() .withRenderingContext(renderingContext) @@ -78,20 +72,13 @@ private QueryExpressionRenderer(Builder builder) { * * * @param queryExpression the model to render - * @param parentTableAliasCalculator table alias calculator from the parent query * @return a table alias calculator appropriate for this context */ - private TableAliasCalculator calculateTableAliasCalculator(QueryExpressionModel queryExpression, - TableAliasCalculator parentTableAliasCalculator) { - TableAliasCalculator baseTableAliasCalculator = queryExpression.joinModel() + private TableAliasCalculator calculateChildTableAliasCalculator(QueryExpressionModel queryExpression) { + return queryExpression.joinModel() .map(JoinModel::containsSubQueries) .map(this::calculateTableAliasCalculatorWithJoins) .orElseGet(this::explicitTableAliasCalculator); - - return new TableAliasCalculatorWithParent.Builder() - .withParent(parentTableAliasCalculator) - .withChild(baseTableAliasCalculator) - .build(); } private TableAliasCalculator calculateTableAliasCalculatorWithJoins(boolean hasSubQueries) { @@ -151,7 +138,7 @@ private FragmentAndParameters calculateQueryExpressionStart() { } private FragmentAndParameters calculateColumnList() { - FragmentCollector fc = queryExpression.mapColumns(this::applyTableAndColumnAlias) + FragmentCollector fc = queryExpression.mapColumns(this::renderColumnAndAlias) .collect(FragmentCollector.collect()); String s = fc.collectFragments(Collectors.joining(", ")); //$NON-NLS-1$ @@ -161,7 +148,7 @@ private FragmentAndParameters calculateColumnList() { .build(); } - private FragmentAndParameters applyTableAndColumnAlias(BasicColumn selectListItem) { + private FragmentAndParameters renderColumnAndAlias(BasicColumn selectListItem) { FragmentAndParameters renderedColumn = selectListItem.render(renderingContext); String nameAndTableAlias = selectListItem.alias().map(a -> renderedColumn.fragment() + " as " + a) //$NON-NLS-1$ @@ -204,7 +191,7 @@ private Optional calculateGroupByClause() { } private FragmentAndParameters renderGroupBy(GroupByModel groupByModel) { - FragmentCollector fc = groupByModel.mapColumns(this::applyTableAlias) + FragmentCollector fc = groupByModel.mapColumns(this::renderColumn) .collect(FragmentCollector.collect()); String groupBy = fc.collectFragments( Collectors.joining(", ", "group by ", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$) @@ -214,6 +201,10 @@ private FragmentAndParameters renderGroupBy(GroupByModel groupByModel) { .build(); } + private FragmentAndParameters renderColumn(BasicColumn column) { + return column.render(renderingContext); + } + private Optional calculateHavingClause() { return queryExpression.havingModel().flatMap(this::renderHavingClause); } @@ -225,10 +216,6 @@ private Optional renderHavingClause(HavingModel havingMod .render(); } - private FragmentAndParameters applyTableAlias(BasicColumn column) { - return column.render(renderingContext); - } - public static Builder withQueryExpression(QueryExpressionModel model) { return new Builder().withQueryExpression(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 85119a283..2007e6387 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 @@ -34,11 +34,7 @@ private TableExpressionRenderer(Builder builder) { @Override public FragmentAndParameters visit(SqlTable table) { - return FragmentAndParameters.withFragment( - renderingContext.tableAliasCalculator().aliasForTable(table) - .map(a -> table.tableNameAtRuntime() + spaceBefore(a)) - .orElseGet(table::tableNameAtRuntime)) - .build(); + return FragmentAndParameters.fromFragment(renderingContext.aliasedTableName(table)); } @Override 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 935515c92..486b95f3f 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 @@ -17,11 +17,8 @@ import java.util.Objects; import java.util.Optional; -import java.util.function.Function; -import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.render.RenderingContext; -import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.render.SelectRenderer; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.AbstractColumnMapping; @@ -38,34 +35,30 @@ public class SetPhraseVisitor extends UpdateMappingVisitor> { - private final Function, String> aliasedColumnNameFunction; private final RenderingContext renderingContext; public SetPhraseVisitor(RenderingContext renderingContext) { - aliasedColumnNameFunction = c -> renderingContext.tableAliasCalculator().aliasForColumn(c.table()) - .map(alias -> alias + "." + c.name()) //$NON-NLS-1$ - .orElseGet(c::name); - this.renderingContext = Objects.requireNonNull(renderingContext); } @Override public Optional visit(NullMapping mapping) { return FragmentAndParameters - .withFragment(mapping.mapColumn(aliasedColumnNameFunction) + " = null") //$NON-NLS-1$ + .withFragment(mapping.mapColumn(renderingContext::aliasedColumnName) + " = null") //$NON-NLS-1$ .buildOptional(); } @Override public Optional visit(ConstantMapping mapping) { - String fragment = mapping.mapColumn(aliasedColumnNameFunction) + " = " + mapping.constant(); //$NON-NLS-1$ + String fragment = mapping.mapColumn(renderingContext::aliasedColumnName) + + " = " + mapping.constant(); //$NON-NLS-1$ return FragmentAndParameters.withFragment(fragment) .buildOptional(); } @Override public Optional visit(StringConstantMapping mapping) { - String fragment = mapping.mapColumn(aliasedColumnNameFunction) + String fragment = mapping.mapColumn(renderingContext::aliasedColumnName) + " = '" //$NON-NLS-1$ + mapping.constant() + "'"; //$NON-NLS-1$ @@ -84,7 +77,7 @@ public Optional visit(ValueOrNullMapping mapping) return mapping.value() .map(v -> buildFragment(mapping, v)) .orElseGet(() -> FragmentAndParameters - .withFragment(mapping.mapColumn(aliasedColumnNameFunction) + " = null") //$NON-NLS-1$ + .withFragment(mapping.mapColumn(renderingContext::aliasedColumnName) + " = null") //$NON-NLS-1$ .buildOptional() ); } @@ -101,7 +94,7 @@ public Optional visit(SelectMapping mapping) { .build() .render(); - String fragment = mapping.mapColumn(aliasedColumnNameFunction) + String fragment = mapping.mapColumn(renderingContext::aliasedColumnName) + " = (" //$NON-NLS-1$ + selectStatement.getSelectStatement() + ")"; //$NON-NLS-1$ @@ -115,7 +108,7 @@ public Optional visit(SelectMapping mapping) { public Optional visit(ColumnToColumnMapping mapping) { FragmentAndParameters renderedColumn = mapping.rightColumn().render(renderingContext); - String setPhrase = mapping.mapColumn(aliasedColumnNameFunction) + String setPhrase = mapping.mapColumn(renderingContext::aliasedColumnName) + " = " //$NON-NLS-1$ + renderedColumn.fragment(); @@ -125,20 +118,13 @@ public Optional visit(ColumnToColumnMapping mapping) { } private Optional buildFragment(AbstractColumnMapping mapping, T value) { - String mapKey = renderingContext.nextMapKey(); - - String jdbcPlaceholder = mapping.mapColumn(c -> calculateJdbcPlaceholder(c, mapKey)); - String setPhrase = mapping.mapColumn(aliasedColumnNameFunction) + RenderingContext.ParameterInfo parameterInfo = mapping.mapColumn(renderingContext::calculateParameterInfo); + String setPhrase = mapping.mapColumn(renderingContext::aliasedColumnName) + " = " //$NON-NLS-1$ - + jdbcPlaceholder; + + parameterInfo.renderedPlaceHolder(); return FragmentAndParameters.withFragment(setPhrase) - .withParameter(mapKey, value) + .withParameter(parameterInfo.mapKey(), value) .buildOptional(); } - - private String calculateJdbcPlaceholder(SqlColumn column, String parameterName) { - return column.renderingStrategy().orElse(renderingContext.renderingStrategy()) - .getFormattedJdbcPlaceholder(column, RenderingStrategy.DEFAULT_PARAMETER_PREFIX, parameterName); - } } 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 d42f37d0e..d759a20f8 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 @@ -18,10 +18,8 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.common.OrderByModel; import org.mybatis.dynamic.sql.common.OrderByRenderer; import org.mybatis.dynamic.sql.exception.InvalidSqlException; @@ -45,10 +43,9 @@ private UpdateRenderer(Builder builder) { TableAliasCalculator tableAliasCalculator = builder.updateModel.tableAlias() .map(a -> ExplicitTableAliasCalculator.of(updateModel.table(), a)) .orElseGet(TableAliasCalculator::empty); - renderingContext = new RenderingContext.Builder() + renderingContext = RenderingContext .withRenderingStrategy(Objects.requireNonNull(builder.renderingStrategy)) .withTableAliasCalculator(tableAliasCalculator) - .withSequence(new AtomicInteger(1)) .build(); } @@ -72,11 +69,7 @@ private UpdateStatementProvider toUpdateStatementProvider(FragmentCollector frag } private FragmentAndParameters calculateUpdateStatementStart() { - SqlTable table = updateModel.table(); - String tableName = table.tableNameAtRuntime(); - String aliasedTableName = renderingContext.tableAliasCalculator().aliasForTable(table) - .map(a -> tableName + " " + a).orElse(tableName); //$NON-NLS-1$ - + String aliasedTableName = renderingContext.aliasedTableName(updateModel.table()); return FragmentAndParameters.fromFragment("update " + aliasedTableName); //$NON-NLS-1$ } @@ -122,12 +115,10 @@ private Optional calculateLimitClause() { } private FragmentAndParameters renderLimitClause(Long limit) { - String mapKey = renderingContext.nextMapKey(); - String jdbcPlaceholder = renderingContext - .renderingStrategy().getFormattedJdbcPlaceholder(RenderingStrategy.DEFAULT_PARAMETER_PREFIX, mapKey); + RenderingContext.ParameterInfo parameterInfo = renderingContext.calculateParameterInfo(); - return FragmentAndParameters.withFragment("limit " + jdbcPlaceholder) //$NON-NLS-1$ - .withParameter(mapKey, limit) + return FragmentAndParameters.withFragment("limit " + parameterInfo.renderedPlaceHolder()) //$NON-NLS-1$ + .withParameter(parameterInfo.mapKey(), limit) .build(); } 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 4b12c763e..d744cbbdf 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/WhereModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/WhereModel.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; import org.mybatis.dynamic.sql.AndOrCriteriaGroup; import org.mybatis.dynamic.sql.SqlCriterion; @@ -53,60 +52,44 @@ public boolean isNonRenderingClauseAllowed() { * @return rendered where clause */ public Optional render(RenderingStrategy renderingStrategy) { - RenderingContext renderingContext = new RenderingContext.Builder() - .withRenderingStrategy(renderingStrategy) - .withSequence(new AtomicInteger(1)) - .withTableAliasCalculator(TableAliasCalculator.empty()) - .build(); + RenderingContext renderingContext = RenderingContext.withRenderingStrategy(renderingStrategy).build(); - return WhereRenderer.withWhereModel(this) - .withRenderingContext(renderingContext) - .build() - .render() - .map(this::toWhereClauseProvider); + return render(renderingContext); } public Optional render(RenderingStrategy renderingStrategy, TableAliasCalculator tableAliasCalculator) { - RenderingContext renderingContext = new RenderingContext.Builder() + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(renderingStrategy) - .withSequence(new AtomicInteger(1)) .withTableAliasCalculator(tableAliasCalculator) .build(); - return WhereRenderer.withWhereModel(this) - .withRenderingContext(renderingContext) - .build() - .render() - .map(this::toWhereClauseProvider); + return render(renderingContext); } public Optional render(RenderingStrategy renderingStrategy, String parameterName) { - RenderingContext renderingContext = new RenderingContext.Builder() + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(renderingStrategy) - .withSequence(new AtomicInteger(1)) - .withTableAliasCalculator(TableAliasCalculator.empty()) + .withParameterName(parameterName) .build(); - return WhereRenderer.withWhereModel(this) - .withRenderingContext(renderingContext) - .withParameterName(parameterName) - .build() - .render() - .map(this::toWhereClauseProvider); + return render(renderingContext); } public Optional render(RenderingStrategy renderingStrategy, TableAliasCalculator tableAliasCalculator, String parameterName) { - RenderingContext renderingContext = new RenderingContext.Builder() + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(renderingStrategy) - .withSequence(new AtomicInteger(1)) .withTableAliasCalculator(tableAliasCalculator) + .withParameterName(parameterName) .build(); + return render(renderingContext); + } + + private Optional render(RenderingContext renderingContext) { return WhereRenderer.withWhereModel(this) .withRenderingContext(renderingContext) - .withParameterName(parameterName) .build() .render() .map(this::toWhereClauseProvider); 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 0cbe3c6a6..491b10a92 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 @@ -54,12 +54,10 @@ * @author Jeff Butler */ public class CriterionRenderer implements SqlCriterionVisitor> { - private final String parameterName; private final RenderingContext renderingContext; - private CriterionRenderer(Builder builder) { - parameterName = builder.parameterName; - renderingContext = Objects.requireNonNull(builder.renderingContext); + public CriterionRenderer(RenderingContext renderingContext) { + this.renderingContext = Objects.requireNonNull(renderingContext); } @Override @@ -179,7 +177,6 @@ private Optional calculateRenderedCriterion(List FragmentAndParameters renderCondition(ColumnAndConditionCriterion criterion) { DefaultConditionVisitor visitor = DefaultConditionVisitor.withColumn(criterion.column()) .withRenderingContext(renderingContext) - .withParameterName(parameterName) .build(); return criterion.condition().accept(visitor); } @@ -248,23 +245,4 @@ private String calculateNotFragment(FragmentCollector collector) { return collector.firstFragment().map(s -> "not " + s).orElse(""); //$NON-NLS-1$ //$NON-NLS-2$ } } - - public static class Builder { - private String parameterName; - private RenderingContext renderingContext; - - public Builder withRenderingContext(RenderingContext renderingContext) { - this.renderingContext = renderingContext; - return this; - } - - public Builder withParameterName(String parameterName) { - this.parameterName = parameterName; - return this; - } - - public CriterionRenderer build() { - return new CriterionRenderer(this); - } - } } 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 9564c8c7f..9c5b277d4 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 @@ -29,7 +29,6 @@ import org.mybatis.dynamic.sql.BindableColumn; import org.mybatis.dynamic.sql.ConditionVisitor; import org.mybatis.dynamic.sql.render.RenderingContext; -import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.render.SelectRenderer; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.FragmentAndParameters; @@ -38,12 +37,10 @@ public class DefaultConditionVisitor implements ConditionVisitor { private final BindableColumn column; - private final String parameterPrefix; private final RenderingContext renderingContext; private DefaultConditionVisitor(Builder builder) { column = Objects.requireNonNull(builder.column); - parameterPrefix = Objects.requireNonNull(builder.parameterPrefix); renderingContext = Objects.requireNonNull(builder.renderingContext); } @@ -78,13 +75,13 @@ public FragmentAndParameters visit(AbstractNoValueCondition condition) { @Override public FragmentAndParameters visit(AbstractSingleValueCondition condition) { FragmentAndParameters renderedLeftColumn = column.render(renderingContext); - String mapKey = renderingContext.nextMapKey(); + RenderingContext.ParameterInfo parameterInfo = renderingContext.calculateParameterInfo(column); String finalFragment = condition.overrideRenderedLeftColumn(renderedLeftColumn.fragment()) + spaceBefore(condition.operator()) - + spaceBefore(getFormattedJdbcPlaceholder(mapKey)); + + spaceBefore(parameterInfo.renderedPlaceHolder()); return FragmentAndParameters.withFragment(finalFragment) - .withParameter(mapKey, convertValue(condition.value())) + .withParameter(parameterInfo.mapKey(), convertValue(condition.value())) .withParameters(renderedLeftColumn.parameters()) .build(); } @@ -92,18 +89,18 @@ public FragmentAndParameters visit(AbstractSingleValueCondition condition) { @Override public FragmentAndParameters visit(AbstractTwoValueCondition condition) { FragmentAndParameters renderedLeftColumn = column.render(renderingContext); - String mapKey1 = renderingContext.nextMapKey(); - String mapKey2 = renderingContext.nextMapKey(); + RenderingContext.ParameterInfo parameterInfo1 = renderingContext.calculateParameterInfo(column); + RenderingContext.ParameterInfo parameterInfo2 = renderingContext.calculateParameterInfo(column); String finalFragment = condition.overrideRenderedLeftColumn(renderedLeftColumn.fragment()) + spaceBefore(condition.operator1()) - + spaceBefore(getFormattedJdbcPlaceholder(mapKey1)) + + spaceBefore(parameterInfo1.renderedPlaceHolder()) + spaceBefore(condition.operator2()) - + spaceBefore(getFormattedJdbcPlaceholder(mapKey2)); + + spaceBefore(parameterInfo2.renderedPlaceHolder()); return FragmentAndParameters.withFragment(finalFragment) - .withParameter(mapKey1, convertValue(condition.value1())) - .withParameter(mapKey2, convertValue(condition.value2())) + .withParameter(parameterInfo1.mapKey(), convertValue(condition.value1())) + .withParameter(parameterInfo2.mapKey(), convertValue(condition.value2())) .withParameters(renderedLeftColumn.parameters()) .build(); } @@ -146,25 +143,18 @@ private Object convertValue(T value) { } private FragmentAndParameters toFragmentAndParameters(T value) { - String mapKey = renderingContext.nextMapKey(); - - return FragmentAndParameters.withFragment(getFormattedJdbcPlaceholder(mapKey)) - .withParameter(mapKey, convertValue(value)) + RenderingContext.ParameterInfo parameterInfo = renderingContext.calculateParameterInfo(column); + return FragmentAndParameters.withFragment(parameterInfo.renderedPlaceHolder()) + .withParameter(parameterInfo.mapKey(), convertValue(value)) .build(); } - private String getFormattedJdbcPlaceholder(String mapKey) { - return column.renderingStrategy().orElse(renderingContext.renderingStrategy()) - .getFormattedJdbcPlaceholder(column, parameterPrefix, mapKey); - } - public static Builder withColumn(BindableColumn column) { return new Builder().withColumn(column); } public static class Builder { private BindableColumn column; - private String parameterPrefix = RenderingStrategy.DEFAULT_PARAMETER_PREFIX; private RenderingContext renderingContext; public Builder withColumn(BindableColumn column) { @@ -177,13 +167,6 @@ public Builder withRenderingContext(RenderingContext renderingContext) { return this; } - public Builder withParameterName(String parameterName) { - if (parameterName != null) { - parameterPrefix = parameterName + "." + RenderingStrategy.DEFAULT_PARAMETER_PREFIX; //$NON-NLS-1$ - } - return this; - } - public DefaultConditionVisitor build() { return new DefaultConditionVisitor<>(this); } diff --git a/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties b/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties index 5077820de..ee7cb72fa 100644 --- a/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties +++ b/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties @@ -53,4 +53,5 @@ ERROR.33=Calling "select" or "selectDistinct" more than once is not allowed. Add ERROR.34=You must specify "select" or "selectDistinct" before any other clauses in a multi-select statement ERROR.35=Multi-select statements must have at least one "union" or "union all" expression ERROR.36=You must either implement the "render" or "renderWithTableAlias" method in a column or function +ERROR.37=The "{0}" function does not support conditions that fail to render INTERNAL.ERROR=Internal Error {0} diff --git a/src/test/java/issues/gh655/Gh655Test.java b/src/test/java/issues/gh655/Gh655Test.java index ec6539354..74385d97f 100644 --- a/src/test/java/issues/gh655/Gh655Test.java +++ b/src/test/java/issues/gh655/Gh655Test.java @@ -17,9 +17,11 @@ import static examples.mariadb.ItemsDynamicSQLSupport.id; import static examples.mariadb.ItemsDynamicSQLSupport.items; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mybatis.dynamic.sql.SqlBuilder.add; import static org.mybatis.dynamic.sql.SqlBuilder.constant; import static org.assertj.core.api.Assertions.assertThat; +import static org.mybatis.dynamic.sql.SqlBuilder.isEqualToWhenPresent; import static org.mybatis.dynamic.sql.SqlBuilder.isGreaterThan; import static org.mybatis.dynamic.sql.SqlBuilder.select; import static org.mybatis.dynamic.sql.SqlBuilder.sum; @@ -34,7 +36,9 @@ import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mybatis.dynamic.sql.exception.DynamicSqlException; import org.mybatis.dynamic.sql.render.RenderingStrategies; +import org.mybatis.dynamic.sql.select.SelectModel; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper; import org.testcontainers.containers.MariaDBContainer; @@ -81,6 +85,17 @@ void sumTest() { } } + @Test + void sumWithOptionalTest() { + SelectModel selectModel = select(sum(id, isEqualToWhenPresent((Integer) null)).as("numrows")) + .from(items) + .build(); + + assertThatExceptionOfType(DynamicSqlException.class) + .isThrownBy(() -> selectModel.render(RenderingStrategies.MYBATIS3)) + .withMessage("The \"sum\" function does not support conditions that fail to render"); + } + @Test void sumAndAddTest() { try (SqlSession session = sqlSessionFactory.openSession()) { diff --git a/src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java b/src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java index 81ffb0ab8..933116b84 100644 --- a/src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.MissingResourceException; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.exception.DynamicSqlException; @@ -241,10 +240,8 @@ void testMissingMessage() { void testInvalidPagingModel() { PagingModel pagingModel = new PagingModel.Builder().build(); - RenderingContext renderingContext = new RenderingContext.Builder() - .withTableAliasCalculator(TableAliasCalculator.empty()) + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) - .withSequence(new AtomicInteger(1)) .build(); PagingModelRenderer renderer = new PagingModelRenderer.Builder() 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 ca2ac4622..f9b3f7519 100644 --- a/src/test/java/org/mybatis/dynamic/sql/mybatis3/CriterionRendererTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/mybatis3/CriterionRendererTest.java @@ -21,7 +21,6 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.ColumnAndConditionCriterion; @@ -31,7 +30,6 @@ import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategies; -import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.where.condition.IsEqualTo; import org.mybatis.dynamic.sql.where.render.CriterionRenderer; @@ -47,15 +45,9 @@ void testAliasWithIgnore() { .withCondition(condition) .build(); - RenderingContext renderingContext = new RenderingContext.Builder() - .withSequence(new AtomicInteger(1)) - .withRenderingStrategy(RenderingStrategies.MYBATIS3) - .withTableAliasCalculator(TableAliasCalculator.empty()) - .build(); + RenderingContext renderingContext =RenderingContext.withRenderingStrategy(RenderingStrategies.MYBATIS3).build(); - CriterionRenderer renderer = new CriterionRenderer.Builder() - .withRenderingContext(renderingContext) - .build(); + CriterionRenderer renderer = new CriterionRenderer(renderingContext); assertThat(criterion.accept(renderer)).hasValueSatisfying(rc -> { FragmentAndParameters fp = rc.fragmentAndParametersWithConnector(); @@ -75,15 +67,12 @@ void testAliasWithoutIgnore() { Map tableAliases = new HashMap<>(); tableAliases.put(table, "a"); - RenderingContext renderingContext = new RenderingContext.Builder() - .withSequence(new AtomicInteger(1)) + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) .withTableAliasCalculator(ExplicitTableAliasCalculator.of(tableAliases)) .build(); - CriterionRenderer renderer = new CriterionRenderer.Builder() - .withRenderingContext(renderingContext) - .build(); + CriterionRenderer renderer = new CriterionRenderer(renderingContext); assertThat(criterion.accept(renderer)).hasValueSatisfying(rc -> { FragmentAndParameters fp = rc.fragmentAndParametersWithConnector(); @@ -106,15 +95,11 @@ void testTypeHandler() { .withCondition(condition) .build(); - RenderingContext renderingContext = new RenderingContext.Builder() - .withSequence(new AtomicInteger(1)) + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) - .withTableAliasCalculator(TableAliasCalculator.empty()) .build(); - CriterionRenderer renderer = new CriterionRenderer.Builder() - .withRenderingContext(renderingContext) - .build(); + CriterionRenderer renderer = new CriterionRenderer(renderingContext); assertThat(criterion.accept(renderer)).hasValueSatisfying(rc -> { FragmentAndParameters fp = rc.fragmentAndParametersWithConnector(); @@ -134,15 +119,12 @@ void testTypeHandlerAndAlias() { Map tableAliases = new HashMap<>(); tableAliases.put(table, "a"); - RenderingContext renderingContext = new RenderingContext.Builder() - .withSequence(new AtomicInteger(1)) + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) .withTableAliasCalculator(ExplicitTableAliasCalculator.of(tableAliases)) .build(); - CriterionRenderer renderer = new CriterionRenderer.Builder() - .withRenderingContext(renderingContext) - .build(); + CriterionRenderer renderer = new CriterionRenderer(renderingContext); assertThat(criterion.accept(renderer)).hasValueSatisfying(rc -> { FragmentAndParameters fp = rc.fragmentAndParametersWithConnector(); 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 b5879b576..6d8c46de9 100644 --- a/src/test/java/org/mybatis/dynamic/sql/select/HavingModelTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/select/HavingModelTest.java @@ -20,14 +20,12 @@ import java.sql.JDBCType; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategies; -import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.select.render.HavingRenderer; import org.mybatis.dynamic.sql.util.FragmentAndParameters; @@ -65,10 +63,8 @@ void testComplexHaving() { } private Optional renderHavingModel(HavingModel havingModel) { - RenderingContext renderingContext = new RenderingContext.Builder() + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.SPRING_NAMED_PARAMETER) - .withSequence(new AtomicInteger(1)) - .withTableAliasCalculator(TableAliasCalculator.empty()) .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 b568ec27d..d3a1baaac 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 @@ -21,7 +21,6 @@ import java.sql.JDBCType; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.ColumnAndConditionCriterion; @@ -30,7 +29,6 @@ import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.render.RenderingStrategies; -import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.where.condition.IsEqualTo; @@ -46,15 +44,11 @@ void testAliasWithIgnore() { .withCondition(condition) .build(); - RenderingContext renderingContext = new RenderingContext.Builder() + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) - .withSequence(new AtomicInteger(1)) - .withTableAliasCalculator(TableAliasCalculator.empty()) .build(); - CriterionRenderer renderer = new CriterionRenderer.Builder() - .withRenderingContext(renderingContext) - .build(); + CriterionRenderer renderer = new CriterionRenderer(renderingContext); assertThat(criterion.accept(renderer)).hasValueSatisfying(rc -> { FragmentAndParameters fp = rc.fragmentAndParameters(); @@ -75,15 +69,12 @@ void testAliasWithoutIgnore() { Map tableAliases = new HashMap<>(); tableAliases.put(table, "a"); - RenderingContext renderingContext = new RenderingContext.Builder() + RenderingContext renderingContext = RenderingContext .withRenderingStrategy(RenderingStrategies.MYBATIS3) - .withSequence(new AtomicInteger(1)) .withTableAliasCalculator(ExplicitTableAliasCalculator.of(tableAliases)) .build(); - CriterionRenderer renderer = new CriterionRenderer.Builder() - .withRenderingContext(renderingContext) - .build(); + CriterionRenderer renderer = new CriterionRenderer(renderingContext); assertThat(criterion.accept(renderer)).hasValueSatisfying(rc -> { FragmentAndParameters fp = rc.fragmentAndParameters();