Skip to content

Commit 12e1ca8

Browse files
committed
Merge branch 'master' into implement-empty-callback
# Conflicts: # src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java # src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java # src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java # src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java
2 parents 129552d + 66cda22 commit 12e1ca8

File tree

6 files changed

+166
-51
lines changed

6 files changed

+166
-51
lines changed

src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,52 +15,48 @@
1515
*/
1616
package org.mybatis.dynamic.sql;
1717

18-
import java.util.ArrayList;
1918
import java.util.Collection;
2019
import java.util.Objects;
2120
import java.util.function.Function;
2221
import java.util.function.UnaryOperator;
22+
import java.util.stream.Collectors;
2323
import java.util.stream.Stream;
2424

25-
public abstract class AbstractListValueCondition<T, S extends AbstractListValueCondition<T, S>> implements VisitableCondition<T> {
25+
public abstract class AbstractListValueCondition<T> implements VisitableCondition<T> {
2626
protected final Collection<T> values;
2727
protected final UnaryOperator<Stream<T>> valueStreamTransformer;
28-
protected final Callback callback;
28+
protected boolean renderWhenEmpty = false;
2929

3030
protected AbstractListValueCondition(Collection<T> values) {
31-
this(values, UnaryOperator.identity(), () -> {});
31+
this(values, UnaryOperator.identity());
3232
}
3333

3434
protected AbstractListValueCondition(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer) {
35-
this(values, valueStreamTransformer, () -> {});
36-
}
37-
38-
protected AbstractListValueCondition(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer,
39-
Callback callback) {
40-
this.values = new ArrayList<>(Objects.requireNonNull(values));
4135
this.valueStreamTransformer = Objects.requireNonNull(valueStreamTransformer);
42-
this.callback = Objects.requireNonNull(callback);
36+
this.values = valueStreamTransformer.apply(Objects.requireNonNull(values).stream())
37+
.collect(Collectors.toList());
4338
}
4439

4540
public final <R> Stream<R> mapValues(Function<T, R> mapper) {
46-
return valueStreamTransformer.apply(values.stream()).map(mapper);
41+
return values.stream().map(mapper);
4742
}
4843

4944
@Override
5045
public boolean shouldRender() {
51-
if (values.isEmpty()) {
52-
callback.call();
53-
return false;
54-
} else {
55-
return true;
56-
}
46+
return !values.isEmpty() || renderWhenEmpty;
5747
}
5848

49+
/**
50+
* Use with caution - this could cause the library to render invalid SQL like "where column in ()".
51+
*/
52+
protected void forceRenderingWhenEmpty() {
53+
renderWhenEmpty = true;
54+
}
55+
5956
@Override
6057
public <R> R accept(ConditionVisitor<T, R> visitor) {
6158
return visitor.visit(this);
6259
}
6360

64-
public abstract S withListEmptyCallback(Callback callback);
6561
public abstract String renderCondition(String columnName, Stream<String> placeholders);
6662
}

src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,21 +23,15 @@
2323
import java.util.stream.Stream;
2424

2525
import org.mybatis.dynamic.sql.AbstractListValueCondition;
26-
import org.mybatis.dynamic.sql.Callback;
2726

28-
public class IsIn<T> extends AbstractListValueCondition<T, IsIn<T>> {
27+
public class IsIn<T> extends AbstractListValueCondition<T> {
2928

30-
protected IsIn(Collection<T> values) {
31-
super(values);
32-
}
33-
34-
protected IsIn(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer, Callback callback) {
35-
super(values, valueStreamTransformer, callback);
29+
protected IsIn(Collection<T> values, UnaryOperator<Stream<T>> valueStreamTransformer) {
30+
super(values, valueStreamTransformer);
3631
}
3732

38-
@Override
39-
public IsIn<T> withListEmptyCallback(Callback callback) {
40-
return new IsIn<>(values, valueStreamTransformer, callback);
33+
protected IsIn(Collection<T> values) {
34+
super(values);
4135
}
4236

4337
@Override
@@ -57,7 +51,9 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
5751
* @return new condition with the specified transformer
5852
*/
5953
public IsIn<T> then(UnaryOperator<Stream<T>> valueStreamTransformer) {
60-
return new IsIn<>(values, valueStreamTransformer, callback);
54+
IsIn<T> answer = new IsIn<>(values, valueStreamTransformer);
55+
answer.renderWhenEmpty = renderWhenEmpty;
56+
return answer;
6157
}
6258

6359
public static <T> IsIn<T> of(Collection<T> values) {

src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,23 +21,16 @@
2121
import java.util.stream.Stream;
2222

2323
import org.mybatis.dynamic.sql.AbstractListValueCondition;
24-
import org.mybatis.dynamic.sql.Callback;
2524
import org.mybatis.dynamic.sql.util.StringUtilities;
2625

27-
public class IsInCaseInsensitive extends AbstractListValueCondition<String, IsInCaseInsensitive> {
26+
public class IsInCaseInsensitive extends AbstractListValueCondition<String> {
2827

29-
protected IsInCaseInsensitive(Collection<String> values, UnaryOperator<Stream<String>> valueStreamTransformer) {
30-
super(values, valueStreamTransformer);
31-
}
32-
33-
protected IsInCaseInsensitive(Collection<String> values, UnaryOperator<Stream<String>> valueStreamTransformer,
34-
Callback callback) {
35-
super(values, valueStreamTransformer, callback);
28+
protected IsInCaseInsensitive(Collection<String> values) {
29+
super(values, s -> s.map(StringUtilities::safelyUpperCase));
3630
}
3731

38-
@Override
39-
public IsInCaseInsensitive withListEmptyCallback(Callback callback) {
40-
return new IsInCaseInsensitive(values, valueStreamTransformer, callback);
32+
protected IsInCaseInsensitive(Collection<String> values, UnaryOperator<Stream<String>> valueStreamTransformer) {
33+
super(values, StringUtilities.upperCaseAfter(valueStreamTransformer));
4134
}
4235

4336
@Override
@@ -46,7 +39,23 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
4639
placeholders.collect(Collectors.joining(",", "in (", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
4740
}
4841

42+
/**
43+
* This method allows you to modify the condition's values before they are placed into the parameter map.
44+
* For example, you could filter nulls, or trim strings, etc. This process will run before final rendering of SQL.
45+
* If you filter values out of the stream, then final condition will not reference those values. If you filter all
46+
* values out of the stream, then the condition will not render.
47+
*
48+
* @param valueStreamTransformer a UnaryOperator that will transform the value stream before
49+
* the values are placed in the parameter map
50+
* @return new condition with the specified transformer
51+
*/
52+
public IsInCaseInsensitive then(UnaryOperator<Stream<String>> valueStreamTransformer) {
53+
IsInCaseInsensitive answer = new IsInCaseInsensitive(values, valueStreamTransformer);
54+
answer.renderWhenEmpty = renderWhenEmpty;
55+
return answer;
56+
}
57+
4958
public static IsInCaseInsensitive of(Collection<String> values) {
50-
return new IsInCaseInsensitive(values, s -> s.map(StringUtilities::safelyUpperCase));
59+
return new IsInCaseInsensitive(values);
5160
}
5261
}

src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,7 +52,9 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
5252
* @return new condition with the specified transformer
5353
*/
5454
public IsNotIn<T> then(UnaryOperator<Stream<T>> valueStreamTransformer) {
55-
return new IsNotIn<>(values, valueStreamTransformer);
55+
IsNotIn<T> answer = new IsNotIn<>(values, valueStreamTransformer);
56+
answer.renderWhenEmpty = renderWhenEmpty;
57+
return answer;
5658
}
5759

5860
public static <T> IsNotIn<T> of(Collection<T> values) {

src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,8 +25,12 @@
2525

2626
public class IsNotInCaseInsensitive extends AbstractListValueCondition<String> {
2727

28+
protected IsNotInCaseInsensitive(Collection<String> values) {
29+
super(values, s -> s.map(StringUtilities::safelyUpperCase));
30+
}
31+
2832
protected IsNotInCaseInsensitive(Collection<String> values, UnaryOperator<Stream<String>> valueStreamTransformer) {
29-
super(values, valueStreamTransformer);
33+
super(values, StringUtilities.upperCaseAfter(valueStreamTransformer));
3034
}
3135

3236
@Override
@@ -36,7 +40,23 @@ public String renderCondition(String columnName, Stream<String> placeholders) {
3640
Collectors.joining(",", "not in (", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
3741
}
3842

43+
/**
44+
* This method allows you to modify the condition's values before they are placed into the parameter map.
45+
* For example, you could filter nulls, or trim strings, etc. This process will run before final rendering of SQL.
46+
* If you filter values out of the stream, then final condition will not reference those values. If you filter all
47+
* values out of the stream, then the condition will not render.
48+
*
49+
* @param valueStreamTransformer a UnaryOperator that will transform the value stream before
50+
* the values are placed in the parameter map
51+
* @return new condition with the specified transformer
52+
*/
53+
public IsNotInCaseInsensitive then(UnaryOperator<Stream<String>> valueStreamTransformer) {
54+
IsNotInCaseInsensitive answer = new IsNotInCaseInsensitive(values, valueStreamTransformer);
55+
answer.renderWhenEmpty = renderWhenEmpty;
56+
return answer;
57+
}
58+
3959
public static IsNotInCaseInsensitive of(Collection<String> values) {
40-
return new IsNotInCaseInsensitive(values, s -> s.map(StringUtilities::safelyUpperCase));
60+
return new IsNotInCaseInsensitive(values);
4161
}
4262
}

src/test/java/examples/animal/data/AnimalDataTest.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
import java.sql.Connection;
2828
import java.sql.DriverManager;
2929
import java.util.ArrayList;
30+
import java.util.Arrays;
3031
import java.util.Collection;
3132
import java.util.Collections;
3233
import java.util.List;
3334
import java.util.Map;
35+
import java.util.Objects;
3436

3537
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
3638
import org.apache.ibatis.jdbc.ScriptRunner;
@@ -57,6 +59,7 @@
5759
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
5860
import org.mybatis.dynamic.sql.util.mybatis3.MyBatis3Utils;
5961
import org.mybatis.dynamic.sql.where.condition.IsIn;
62+
import org.mybatis.dynamic.sql.where.condition.IsNotIn;
6063
import org.mybatis.dynamic.sql.where.render.WhereClauseProvider;
6164

6265
class AnimalDataTest {
@@ -569,6 +572,47 @@ void testInCondition() {
569572
}
570573
}
571574

575+
@Test
576+
void testInConditionWithEventuallyEmptyList() {
577+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
578+
AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
579+
580+
SelectStatementProvider selectStatement = select(id, animalName, bodyWeight, brainWeight)
581+
.from(animalData)
582+
.where(id, isIn(null, 22, null).then(s -> s.filter(Objects::nonNull).filter(i -> i != 22)))
583+
.build()
584+
.render(RenderingStrategies.MYBATIS3);
585+
586+
assertThat(selectStatement.getSelectStatement())
587+
.isEqualTo("select id, animal_name, body_weight, brain_weight from AnimalData");
588+
List<AnimalData> animals = mapper.selectMany(selectStatement);
589+
assertThat(animals).hasSize(65);
590+
}
591+
}
592+
593+
@Test
594+
void testInConditionWithEventuallyEmptyListForceRendering() {
595+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
596+
AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
597+
598+
List<Integer> inValues = new ArrayList<>();
599+
inValues.add(null);
600+
inValues.add(22);
601+
inValues.add(null);
602+
603+
SelectStatementProvider selectStatement = select(id, animalName, bodyWeight, brainWeight)
604+
.from(animalData)
605+
.where(id, IsInRequired.isIn(inValues).then(s -> s.filter(Objects::nonNull).filter(i -> i != 22)))
606+
.build()
607+
.render(RenderingStrategies.MYBATIS3);
608+
609+
assertThat(selectStatement.getSelectStatement())
610+
.isEqualTo("select id, animal_name, body_weight, brain_weight from AnimalData where id in ()");
611+
612+
assertThatExceptionOfType(PersistenceException.class).isThrownBy(() -> mapper.selectMany(selectStatement));
613+
}
614+
}
615+
572616
@Test
573617
void testInConditionWithEmptyList() {
574618
assertThatExceptionOfType(RuntimeException.class).describedAs("Fred").isThrownBy(() ->
@@ -650,6 +694,54 @@ void testNotInCaseSensitiveConditionWithNull() {
650694
}
651695
}
652696

697+
@Test
698+
void testNotInConditionWithEventuallyEmptyList() {
699+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
700+
AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
701+
702+
SelectStatementProvider selectStatement = select(id, animalName, bodyWeight, brainWeight)
703+
.from(animalData)
704+
.where(id, isNotIn(null, 22, null).then(s -> s.filter(Objects::nonNull).filter(i -> i != 22)))
705+
.build()
706+
.render(RenderingStrategies.MYBATIS3);
707+
708+
assertThat(selectStatement.getSelectStatement())
709+
.isEqualTo("select id, animal_name, body_weight, brain_weight from AnimalData");
710+
List<AnimalData> animals = mapper.selectMany(selectStatement);
711+
assertThat(animals).hasSize(65);
712+
}
713+
}
714+
715+
@Test
716+
void testNotInConditionWithEventuallyEmptyListForceRendering() {
717+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
718+
AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
719+
720+
SelectStatementProvider selectStatement = select(id, animalName, bodyWeight, brainWeight)
721+
.from(animalData)
722+
.where(id, IsNotInRequired.isNotIn(null, 22, null)
723+
.then(s -> s.filter(Objects::nonNull).filter(i -> i != 22)))
724+
.build()
725+
.render(RenderingStrategies.MYBATIS3);
726+
727+
assertThat(selectStatement.getSelectStatement())
728+
.isEqualTo("select id, animal_name, body_weight, brain_weight from AnimalData where id not in ()");
729+
730+
assertThatExceptionOfType(PersistenceException.class).isThrownBy(() -> mapper.selectMany(selectStatement));
731+
}
732+
}
733+
734+
public static class IsNotInRequired<T> extends IsNotIn<T> {
735+
protected IsNotInRequired(Collection<T> values) {
736+
super(values);
737+
forceRenderingWhenEmpty();
738+
}
739+
740+
@SafeVarargs
741+
public static <T> IsNotInRequired<T> isNotIn(T...values) {
742+
return new IsNotInRequired<>(Arrays.asList(values));
743+
}
744+
}
653745
@Test
654746
void testLikeCondition() {
655747
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {

0 commit comments

Comments
 (0)