Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* Copyright 2016-2022 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.common;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.jetbrains.annotations.NotNull;
import org.mybatis.dynamic.sql.AndOrCriteriaGroup;
import org.mybatis.dynamic.sql.BindableColumn;
import org.mybatis.dynamic.sql.ColumnAndConditionCriterion;
import org.mybatis.dynamic.sql.CriteriaGroup;
import org.mybatis.dynamic.sql.ExistsCriterion;
import org.mybatis.dynamic.sql.ExistsPredicate;
import org.mybatis.dynamic.sql.SqlCriterion;
import org.mybatis.dynamic.sql.VisitableCondition;

public abstract class AbstractBooleanExpressionDSL<T extends AbstractBooleanExpressionDSL<T>> {
protected SqlCriterion initialCriterion; // WARNING - may be null!
protected final List<AndOrCriteriaGroup> subCriteria = new ArrayList<>();

@NotNull
public <S> T and(BindableColumn<S> column, VisitableCondition<S> condition,
AndOrCriteriaGroup... subCriteria) {
return and(column, condition, Arrays.asList(subCriteria));
}

@NotNull
public <S> T and(BindableColumn<S> column, VisitableCondition<S> condition,
List<AndOrCriteriaGroup> subCriteria) {
addSubCriteria("and", buildCriterion(column, condition), subCriteria); //$NON-NLS-1$
return getThis();
}

@NotNull
public T and(ExistsPredicate existsPredicate, AndOrCriteriaGroup... subCriteria) {
return and(existsPredicate, Arrays.asList(subCriteria));
}

@NotNull
public T and(ExistsPredicate existsPredicate, List<AndOrCriteriaGroup> subCriteria) {
addSubCriteria("and", buildCriterion(existsPredicate), subCriteria); //$NON-NLS-1$
return getThis();
}

@NotNull
public T and(SqlCriterion initialCriterion, AndOrCriteriaGroup... subCriteria) {
return and(initialCriterion, Arrays.asList(subCriteria));
}

@NotNull
public T and(SqlCriterion initialCriterion, List<AndOrCriteriaGroup> subCriteria) {
addSubCriteria("and", buildCriterion(initialCriterion), subCriteria); //$NON-NLS-1$
return getThis();
}

@NotNull
public T and(List<AndOrCriteriaGroup> criteria) {
addSubCriteria("and", criteria); //$NON-NLS-1$
return getThis();
}

@NotNull
public <S> T or(BindableColumn<S> column, VisitableCondition<S> condition,
AndOrCriteriaGroup... subCriteria) {
return or(column, condition, Arrays.asList(subCriteria));
}

@NotNull
public <S> T or(BindableColumn<S> column, VisitableCondition<S> condition,
List<AndOrCriteriaGroup> subCriteria) {
addSubCriteria("or", buildCriterion(column, condition), subCriteria); //$NON-NLS-1$
return getThis();
}

@NotNull
public T or(ExistsPredicate existsPredicate, AndOrCriteriaGroup... subCriteria) {
return or(existsPredicate, Arrays.asList(subCriteria));
}

@NotNull
public T or(ExistsPredicate existsPredicate, List<AndOrCriteriaGroup> subCriteria) {
addSubCriteria("or", buildCriterion(existsPredicate), subCriteria); //$NON-NLS-1$
return getThis();
}

@NotNull
public T or(SqlCriterion initialCriterion, AndOrCriteriaGroup... subCriteria) {
return or(initialCriterion, Arrays.asList(subCriteria));
}

@NotNull
public T or(SqlCriterion initialCriterion, List<AndOrCriteriaGroup> subCriteria) {
addSubCriteria("or", buildCriterion(initialCriterion), subCriteria); //$NON-NLS-1$
return getThis();
}

@NotNull
public T or(List<AndOrCriteriaGroup> criteria) {
addSubCriteria("or", criteria); //$NON-NLS-1$
return getThis();
}

private <R> SqlCriterion buildCriterion(BindableColumn<R> column, VisitableCondition<R> condition) {
return ColumnAndConditionCriterion.withColumn(column).withCondition(condition).build();
}

private SqlCriterion buildCriterion(ExistsPredicate existsPredicate) {
return new ExistsCriterion.Builder().withExistsPredicate(existsPredicate).build();
}

private SqlCriterion buildCriterion(SqlCriterion initialCriterion) {
return new CriteriaGroup.Builder().withInitialCriterion(initialCriterion).build();
}

private void addSubCriteria(String connector, SqlCriterion initialCriterion,
List<AndOrCriteriaGroup> subCriteria) {
this.subCriteria.add(new AndOrCriteriaGroup.Builder()
.withInitialCriterion(initialCriterion)
.withConnector(connector)
.withSubCriteria(subCriteria)
.build());
}

private void addSubCriteria(String connector, List<AndOrCriteriaGroup> criteria) {
this.subCriteria.add(new AndOrCriteriaGroup.Builder()
.withConnector(connector)
.withSubCriteria(criteria)
.build());
}

protected abstract T getThis();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2016-2022 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.common;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.mybatis.dynamic.sql.AndOrCriteriaGroup;
import org.mybatis.dynamic.sql.SqlCriterion;

public abstract class AbstractBooleanExpressionModel {
private final SqlCriterion initialCriterion;
private final List<AndOrCriteriaGroup> subCriteria = new ArrayList<>();

protected AbstractBooleanExpressionModel(SqlCriterion initialCriterion, List<AndOrCriteriaGroup> subCriteria) {
this.initialCriterion = initialCriterion;
this.subCriteria.addAll(subCriteria);
}

public Optional<SqlCriterion> initialCriterion() {
return Optional.ofNullable(initialCriterion);
}

public List<AndOrCriteriaGroup> subCriteria() {
return Collections.unmodifiableList(subCriteria);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2016-2022 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.common;

import static org.mybatis.dynamic.sql.util.StringUtilities.spaceAfter;

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.util.FragmentAndParameters;
import org.mybatis.dynamic.sql.util.FragmentCollector;
import org.mybatis.dynamic.sql.where.render.CriterionRenderer;
import org.mybatis.dynamic.sql.where.render.RenderedCriterion;

public abstract class AbstractBooleanExpressionRenderer<M extends AbstractBooleanExpressionModel> {
protected final M model;
private final String prefix;
private final CriterionRenderer criterionRenderer;

protected AbstractBooleanExpressionRenderer(String prefix, AbstractBuilder<M, ?> builder) {
model = Objects.requireNonNull(builder.model);
this.prefix = Objects.requireNonNull(prefix);

criterionRenderer = new CriterionRenderer.Builder()
.withSequence(builder.sequence)
.withRenderingStrategy(builder.renderingStrategy)
.withTableAliasCalculator(builder.tableAliasCalculator)
.withParameterName(builder.parameterName)
.build();
}

public Optional<FragmentAndParameters> render() {
return model.initialCriterion()
.map(this::renderWithInitialCriterion)
.orElseGet(this::renderWithoutInitialCriterion)
.map(rc -> FragmentAndParameters.withFragment(rc.fragmentAndParameters().fragment())
.withParameters(rc.fragmentAndParameters().parameters())
.build()
);
}

private Optional<RenderedCriterion> renderWithInitialCriterion(SqlCriterion initialCriterion) {
return criterionRenderer.render(initialCriterion, model.subCriteria(), this::calculateClause);
}

private Optional<RenderedCriterion> renderWithoutInitialCriterion() {
return criterionRenderer.render(model.subCriteria(), this::calculateClause);
}

private String calculateClause(FragmentCollector collector) {
if (collector.hasMultipleFragments()) {
return collector.fragments()
.collect(Collectors.joining(" ", spaceAfter(prefix), "")); //$NON-NLS-1$ //$NON-NLS-2$
} else {
return collector.firstFragment()
.map(this::stripEnclosingParenthesesIfPresent)
.map(this::addPrefix)
.orElse(""); //$NON-NLS-1$
}
}

private String stripEnclosingParenthesesIfPresent(String fragment) {
// The fragment will have surrounding open/close parentheses if there is more than one rendered condition.
// Since there is only a single fragment, we don't need these in the final rendered clause
if (fragment.startsWith("(") && fragment.endsWith(")")) { //$NON-NLS-1$ //$NON-NLS-2$
return fragment.substring(1, fragment.length() - 1);
} else {
return fragment;
}
}

private String addPrefix(String fragment) {
return spaceAfter(prefix) + 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;

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;
return getThis();
}

public B withParameterName(String parameterName) {
this.parameterName = parameterName;
return getThis();
}

protected abstract B getThis();
}
}
28 changes: 28 additions & 0 deletions src/main/java/org/mybatis/dynamic/sql/select/HavingModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2016-2022 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;

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<AndOrCriteriaGroup> subCriteria) {
super(initialCriterion, subCriteria);
}
}
Loading