Skip to content

Commit 7d3b7a9

Browse files
committed
Standalone Having clauses
1 parent d56d07b commit 7d3b7a9

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2016-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.dynamic.sql.select;
17+
18+
import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionDSL;
19+
20+
import java.util.function.Consumer;
21+
22+
@FunctionalInterface
23+
public interface HavingApplier {
24+
25+
void accept(HavingDSL havingDSL);
26+
27+
/**
28+
* Return a composed having applier that performs this operation followed by the after operation.
29+
*
30+
* @param after the operation to perform after this operation
31+
*
32+
* @return a composed having applier that performs this operation followed by the after operation.
33+
*/
34+
default HavingApplier andThen(Consumer<AbstractBooleanExpressionDSL<?>> after) {
35+
return t -> {
36+
accept(t);
37+
after.accept(t);
38+
};
39+
}
40+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2016-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.dynamic.sql.select;
17+
18+
import java.util.Arrays;
19+
import java.util.Collections;
20+
import java.util.List;
21+
22+
import org.mybatis.dynamic.sql.AndOrCriteriaGroup;
23+
import org.mybatis.dynamic.sql.BindableColumn;
24+
import org.mybatis.dynamic.sql.ColumnAndConditionCriterion;
25+
import org.mybatis.dynamic.sql.SqlCriterion;
26+
import org.mybatis.dynamic.sql.VisitableCondition;
27+
import org.mybatis.dynamic.sql.common.AbstractBooleanExpressionDSL;
28+
29+
public class HavingDSL extends AbstractBooleanExpressionDSL<HavingDSL> {
30+
public <T> HavingDSL having(BindableColumn<T> column, VisitableCondition<T> condition,
31+
AndOrCriteriaGroup... subCriteria) {
32+
return having(column, condition, Arrays.asList(subCriteria));
33+
}
34+
35+
public <T> HavingDSL having(BindableColumn<T> column, VisitableCondition<T> condition,
36+
List<AndOrCriteriaGroup> subCriteria) {
37+
return having(ColumnAndConditionCriterion.withColumn(column).withCondition(condition).build(), subCriteria);
38+
}
39+
40+
public HavingDSL having(SqlCriterion initialCriterion, AndOrCriteriaGroup... subCriteria) {
41+
return having(initialCriterion, Arrays.asList(subCriteria));
42+
}
43+
44+
public HavingDSL having(SqlCriterion initialCriterion, List<AndOrCriteriaGroup> subCriteria) {
45+
setInitialCriterion(initialCriterion);
46+
this.subCriteria.addAll(subCriteria);
47+
return this;
48+
}
49+
50+
@Override
51+
protected HavingDSL getThis() {
52+
return this;
53+
}
54+
55+
@Override
56+
protected SqlCriterion getInitialCriterion() {
57+
return super.getInitialCriterion();
58+
}
59+
60+
protected List<AndOrCriteriaGroup> getSubCriteria() {
61+
return Collections.unmodifiableList(subCriteria);
62+
}
63+
}

src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,13 @@ public QueryExpressionHavingBuilder having(SqlCriterion initialCriterion, AndOrC
564564
public QueryExpressionHavingBuilder having(SqlCriterion initialCriterion, List<AndOrCriteriaGroup> subCriteria) {
565565
return QueryExpressionDSL.this.having(initialCriterion, subCriteria);
566566
}
567+
568+
public QueryExpressionDSL<R> applyHaving(HavingApplier havingApplier) {
569+
HavingDSL s = new HavingDSL();
570+
havingApplier.accept(s);
571+
QueryExpressionDSL.this.having(s.getInitialCriterion(), s.getSubCriteria());
572+
return QueryExpressionDSL.this;
573+
}
567574
}
568575

569576
public class UnionBuilder {

src/test/java/examples/groupby/GroupByTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.junit.jupiter.api.BeforeEach;
4040
import org.junit.jupiter.api.Test;
4141
import org.mybatis.dynamic.sql.render.RenderingStrategies;
42+
import org.mybatis.dynamic.sql.select.HavingApplier;
4243
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
4344
import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;
4445

@@ -662,4 +663,54 @@ void testHavingWithUnionAll() {
662663
assertThat(row).containsEntry("LAST_NAME", "Flintstone");
663664
}
664665
}
666+
667+
@Test
668+
void testStandaloneHaving() {
669+
try (SqlSession session = sqlSessionFactory.openSession()) {
670+
CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
671+
672+
SelectStatementProvider selectStatement = select(lastName, count())
673+
.from(person)
674+
.groupBy(lastName)
675+
.applyHaving(commonHaving)
676+
.build()
677+
.render(RenderingStrategies.MYBATIS3);
678+
679+
String expected = "select last_name, count(*) from Person group by last_name " +
680+
"having count(*) = #{parameters.p1}";
681+
assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
682+
683+
List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
684+
assertThat(rows).hasSize(1);
685+
Map<String, Object> row = rows.get(0);
686+
assertThat(row).containsEntry("LAST_NAME", "Rubble");
687+
}
688+
}
689+
690+
@Test
691+
void testComposedHaving() {
692+
try (SqlSession session = sqlSessionFactory.openSession()) {
693+
CommonSelectMapper mapper = session.getMapper(CommonSelectMapper.class);
694+
695+
HavingApplier composedHaving = commonHaving.andThen(d -> d.and(lastName, isEqualTo("Rubble")));
696+
697+
SelectStatementProvider selectStatement = select(lastName, count())
698+
.from(person)
699+
.groupBy(lastName)
700+
.applyHaving(composedHaving)
701+
.build()
702+
.render(RenderingStrategies.MYBATIS3);
703+
704+
String expected = "select last_name, count(*) from Person group by last_name " +
705+
"having count(*) = #{parameters.p1} and last_name = #{parameters.p2,jdbcType=VARCHAR}";
706+
assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
707+
708+
List<Map<String, Object>> rows = mapper.selectManyMappedRows(selectStatement);
709+
assertThat(rows).hasSize(1);
710+
Map<String, Object> row = rows.get(0);
711+
assertThat(row).containsEntry("LAST_NAME", "Rubble");
712+
}
713+
}
714+
715+
private final HavingApplier commonHaving = d -> d.having(count(), isEqualTo(3L));
665716
}

0 commit comments

Comments
 (0)