Skip to content

Refactor Condition and Column Rendering #662

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ In order to add this capability, we've modified the join DSL to add type informa
be source code compatible with most uses. There could be an issue if you are joining tables with columns of different
types - which is a rare usage. Please let us know if this causes an undo hardship.

### Other Changes

1. Rendering of conditions and columns was refactored. One benefit of this change is that
it is now easier to support more complex functions - such as the aggregate function `sum(id < 5)` which is the
initial enhancement request that inspired this change. As a result of the changes, one method is deprecated
in the `BasicColumn` object. If you have implemented any custom functions, please note this deprecation and update
your code accordingly. ([#662](https://github.com/mybatis/mybatis-dynamic-sql/pull/662))

## Release 1.5.0 - April 21, 2023

GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/milestone/12?closed=1](https://github.com/mybatis/mybatis-dynamic-sql/milestone/12?closed=1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,22 @@
*/
package org.mybatis.dynamic.sql;

import org.mybatis.dynamic.sql.render.TableAliasCalculator;

public abstract class AbstractColumnComparisonCondition<T> implements VisitableCondition<T> {

protected final BasicColumn column;
protected final BasicColumn rightColumn;

protected AbstractColumnComparisonCondition(BasicColumn rightColumn) {
this.rightColumn = rightColumn;
}

protected AbstractColumnComparisonCondition(BasicColumn column) {
this.column = column;
public BasicColumn rightColumn() {
return rightColumn;
}

@Override
public <R> R accept(ConditionVisitor<T, R> visitor) {
return visitor.visit(this);
}

public String renderCondition(String columnName, TableAliasCalculator tableAliasCalculator) {
return renderCondition(columnName, column.renderWithTableAlias(tableAliasCalculator));
}

protected abstract String renderCondition(String leftColumn, String rightColumn);
public abstract String operator();
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,5 @@ protected <R, S extends AbstractListValueCondition<R>> S mapSupport(Function<? s
*/
public abstract AbstractListValueCondition<T> filter(Predicate<? super T> predicate);

public abstract String renderCondition(String columnName, Stream<String> placeholders);
public abstract String operator();
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ protected <S extends AbstractNoValueCondition<?>> S filterSupport(BooleanSupplie
}
}

public abstract String renderCondition(String columnName);
public abstract String operator();
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,5 @@ protected <R, S extends AbstractSingleValueCondition<R>> S mapSupport(Function<?
*/
public abstract AbstractSingleValueCondition<T> filter(Predicate<? super T> predicate);

public abstract String renderCondition(String columnName, String placeholder);
public abstract String operator();
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ public <R> R accept(ConditionVisitor<T, R> visitor) {
return visitor.visit(this);
}

public abstract String renderCondition(String columnName, String renderedSelectStatement);
public abstract String operator();
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,7 @@ protected <R, S extends AbstractTwoValueCondition<R>> S mapSupport(Function<? su
*/
public abstract AbstractTwoValueCondition<T> filter(Predicate<? super T> predicate);

public abstract String renderCondition(String columnName, String placeholder1, String placeholder2);
public abstract String operator1();

public abstract String operator2();
}
36 changes: 23 additions & 13 deletions src/main/java/org/mybatis/dynamic/sql/BasicColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@

import java.util.Optional;

import org.mybatis.dynamic.sql.exception.DynamicSqlException;
import org.mybatis.dynamic.sql.render.RenderingContext;
import org.mybatis.dynamic.sql.render.TableAliasCalculator;
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
import org.mybatis.dynamic.sql.util.Messages;

/**
* Describes attributes of columns that are necessary for rendering if the column is not expected to
Expand Down Expand Up @@ -46,26 +50,32 @@ public interface BasicColumn {
BasicColumn as(String alias);

/**
* Returns the name of the item aliased with a table name if appropriate.
* For example, "a.foo". This is appropriate for where clauses and order by clauses.
* Returns a rendering of the column.
* The rendered fragment should include the table alias based on the TableAliasCalculator
* in the RenderingContext. The fragment could contain prepared statement parameter
* markers and associated parameter values if desired.
*
* @param tableAliasCalculator the table alias calculator for the current renderer
* @return the item name with the table alias applied
* @param renderingContext the rendering context (strategy, sequence, etc.)
* @return a rendered SQL fragment and, optionally, parameters associated with the fragment
* @since 1.5.1
*/
String renderWithTableAlias(TableAliasCalculator tableAliasCalculator);
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.
return FragmentAndParameters.fromFragment(renderWithTableAlias(renderingContext.tableAliasCalculator()));
}

/**
* Returns the name of the item aliased with a table name and column alias if appropriate.
* For example, "a.foo as bar". This is appropriate for select list clauses.
* Returns the name of the item aliased with a table name if appropriate.
* For example, "a.foo". This is appropriate for where clauses and order by clauses.
*
* @param tableAliasCalculator the table alias calculator for the current renderer
* @return the item name with the table and column aliases applied
* @return the item name with the table alias applied
* @deprecated Please replace this method by overriding the more general "render" method
*/
default String renderWithTableAndColumnAlias(TableAliasCalculator tableAliasCalculator) {
String nameAndTableAlias = renderWithTableAlias(tableAliasCalculator);

return alias().map(a -> nameAndTableAlias + " as " + a) //$NON-NLS-1$
.orElse(nameAndTableAlias);
@Deprecated
default String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
throw new DynamicSqlException(Messages.getString("ERROR.36")); //$NON-NLS-1$
}

/**
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/org/mybatis/dynamic/sql/Constant.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import java.util.Objects;
import java.util.Optional;

import org.mybatis.dynamic.sql.render.TableAliasCalculator;
import org.mybatis.dynamic.sql.render.RenderingContext;
import org.mybatis.dynamic.sql.util.FragmentAndParameters;

public class Constant<T> implements BindableColumn<T> {

Expand All @@ -40,8 +41,8 @@ public Optional<String> alias() {
}

@Override
public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
return value;
public FragmentAndParameters render(RenderingContext renderingContext) {
return FragmentAndParameters.fromFragment(value);
}

@Override
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/org/mybatis/dynamic/sql/DerivedColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
import java.util.Objects;
import java.util.Optional;

import org.mybatis.dynamic.sql.render.TableAliasCalculator;
import org.mybatis.dynamic.sql.render.RenderingContext;
import org.mybatis.dynamic.sql.util.FragmentAndParameters;

/**
* A derived column is a column that is not directly related to a table. This is primarily
Expand Down Expand Up @@ -62,8 +63,9 @@ public Optional<String> typeHandler() {
}

@Override
public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
return tableQualifier == null ? name : tableQualifier + "." + name; //$NON-NLS-1$
public FragmentAndParameters render(RenderingContext renderingContext) {
String fragment = tableQualifier == null ? name : tableQualifier + "." + name; //$NON-NLS-1$
return FragmentAndParameters.fromFragment(fragment);
}

@Override
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,10 @@ static <T> Sum<T> sum(BindableColumn<T> column) {
return Sum.of(column);
}

static <T> Sum<T> sum(BindableColumn<T> column, VisitableCondition<T> condition) {
return Sum.of(column, condition);
}

// constants
static <T> Constant<T> constant(String constant) {
return Constant.of(constant);
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/org/mybatis/dynamic/sql/SqlColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
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;

public class SqlColumn<T> implements BindableColumn<T>, SortSpecification {
Expand Down Expand Up @@ -136,10 +138,12 @@ public String orderByName() {
}

@Override
public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
return tableQualifierFunction.apply(tableAliasCalculator, table)
public FragmentAndParameters render(RenderingContext renderingContext) {
String fragment = tableQualifierFunction.apply(renderingContext.tableAliasCalculator(), table)
.map(this::applyTableAlias)
.orElseGet(this::name);

return FragmentAndParameters.fromFragment(fragment);
}

@Override
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/org/mybatis/dynamic/sql/StringConstant.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import java.util.Objects;
import java.util.Optional;

import org.mybatis.dynamic.sql.render.TableAliasCalculator;
import org.mybatis.dynamic.sql.render.RenderingContext;
import org.mybatis.dynamic.sql.util.FragmentAndParameters;

public class StringConstant implements BindableColumn<String> {

Expand All @@ -40,8 +41,8 @@ public Optional<String> alias() {
}

@Override
public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
return "'" + value + "'"; //$NON-NLS-1$ //$NON-NLS-2$
public FragmentAndParameters render(RenderingContext renderingContext) {
return FragmentAndParameters.fromFragment("'" + value + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}

@Override
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/mybatis/dynamic/sql/VisitableCondition.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,16 @@ default boolean shouldRender() {
* returns false.
*/
default void renderingSkipped() {}

/**
* This method is called during rendering. Its purpose is to allow conditions to change
* the value of the rendered left column. This is primarily used in the case-insensitive conditions
* where we surround the rendered column with "upper(" and ")".
*
* @param renderedLeftColumn the rendered left column
* @return the altered column - by default no change is applied
*/
default String overrideRenderedLeftColumn(String renderedLeftColumn) {
return renderedLeftColumn;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@

import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import org.mybatis.dynamic.sql.SqlCriterion;
import org.mybatis.dynamic.sql.render.RenderingStrategy;
import org.mybatis.dynamic.sql.render.TableAliasCalculator;
import org.mybatis.dynamic.sql.render.RenderingContext;
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
import org.mybatis.dynamic.sql.util.FragmentCollector;
import org.mybatis.dynamic.sql.where.render.CriterionRenderer;
Expand All @@ -40,9 +38,7 @@ protected AbstractBooleanExpressionRenderer(String prefix, AbstractBuilder<M, ?>
this.prefix = Objects.requireNonNull(prefix);

criterionRenderer = new CriterionRenderer.Builder()
.withSequence(builder.sequence)
.withRenderingStrategy(builder.renderingStrategy)
.withTableAliasCalculator(builder.tableAliasCalculator)
.withRenderingContext(Objects.requireNonNull(builder.renderingContext))
.withParameterName(builder.parameterName)
.build();
}
Expand All @@ -67,8 +63,8 @@ private Optional<RenderedCriterion> renderWithoutInitialCriterion() {

private String calculateClause(FragmentCollector collector) {
if (collector.hasMultipleFragments()) {
return collector.fragments()
.collect(Collectors.joining(" ", spaceAfter(prefix), "")); //$NON-NLS-1$ //$NON-NLS-2$
return collector.collectFragments(
Collectors.joining(" ", spaceAfter(prefix), "")); //$NON-NLS-1$ //$NON-NLS-2$
} else {
return collector.firstFragment()
.map(this::stripEnclosingParenthesesIfPresent)
Expand All @@ -93,27 +89,15 @@ private String addPrefix(String fragment) {

public abstract static class AbstractBuilder<M, B extends AbstractBuilder<M, B>> {
private final M model;
private RenderingStrategy renderingStrategy;
private TableAliasCalculator tableAliasCalculator;
private AtomicInteger sequence;
private String parameterName;
private RenderingContext renderingContext;

protected AbstractBuilder(M model) {
this.model = model;
}

public B withRenderingStrategy(RenderingStrategy renderingStrategy) {
this.renderingStrategy = renderingStrategy;
return getThis();
}

public B withTableAliasCalculator(TableAliasCalculator tableAliasCalculator) {
this.tableAliasCalculator = tableAliasCalculator;
return getThis();
}

public B withSequence(AtomicInteger sequence) {
this.sequence = sequence;
public B withRenderingContext(RenderingContext renderingContext) {
this.renderingContext = renderingContext;
return getThis();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class OrderByRenderer {
public FragmentAndParameters render(OrderByModel orderByModel) {
String phrase = orderByModel.mapColumns(this::calculateOrderByPhrase)
.collect(Collectors.joining(", ", "order by ", "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return FragmentAndParameters.withFragment(phrase).build();
return FragmentAndParameters.fromFragment(phrase);
}

private String calculateOrderByPhrase(SortSpecification column) {
Expand Down
Loading