Skip to content

Commit b641516

Browse files
committed
Implement Property When Present Mapping
1 parent 2979292 commit b641516

File tree

10 files changed

+159
-24
lines changed

10 files changed

+159
-24
lines changed

src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java

Lines changed: 3 additions & 4 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,6 +25,7 @@
2525
import org.mybatis.dynamic.sql.util.ConstantMapping;
2626
import org.mybatis.dynamic.sql.util.NullMapping;
2727
import org.mybatis.dynamic.sql.util.PropertyMapping;
28+
import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping;
2829
import org.mybatis.dynamic.sql.util.StringConstantMapping;
2930

3031
public class InsertDSL<T> {
@@ -78,9 +79,7 @@ public InsertDSL<T> toProperty(String property) {
7879
}
7980

8081
public InsertDSL<T> toPropertyWhenPresent(String property, Supplier<?> valueSupplier) {
81-
if (valueSupplier.get() != null) {
82-
toProperty(property);
83-
}
82+
columnMappings.add(PropertyWhenPresentMapping.of(column, property, valueSupplier));
8483
return InsertDSL.this;
8584
}
8685

src/main/java/org/mybatis/dynamic/sql/insert/render/FieldAndValue.java

Lines changed: 6 additions & 1 deletion
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.
@@ -16,6 +16,7 @@
1616
package org.mybatis.dynamic.sql.insert.render;
1717

1818
import java.util.Objects;
19+
import java.util.Optional;
1920

2021
public class FieldAndValue {
2122
private String fieldName;
@@ -59,5 +60,9 @@ public Builder withValuePhrase(String valuePhrase) {
5960
public FieldAndValue build() {
6061
return new FieldAndValue(this);
6162
}
63+
64+
public Optional<FieldAndValue> buildOptional() {
65+
return Optional.of(build());
66+
}
6267
}
6368
}

src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderer.java

Lines changed: 29 additions & 9 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.
@@ -17,8 +17,11 @@
1717

1818
import static org.mybatis.dynamic.sql.util.StringUtilities.spaceBefore;
1919

20+
import java.util.List;
2021
import java.util.Objects;
22+
import java.util.Optional;
2123
import java.util.function.Function;
24+
import java.util.stream.Collectors;
2225

2326
import org.mybatis.dynamic.sql.insert.InsertModel;
2427
import org.mybatis.dynamic.sql.render.RenderingStrategy;
@@ -36,26 +39,43 @@ private InsertRenderer(Builder<T> builder) {
3639

3740
public InsertStatementProvider<T> render() {
3841
ValuePhraseVisitor visitor = new ValuePhraseVisitor(renderingStrategy);
39-
FieldAndValueCollector collector = model.mapColumnMappings(toFieldAndValue(visitor))
40-
.collect(FieldAndValueCollector.collect());
42+
43+
List<Optional<FieldAndValue>> fieldsAndValues = model.mapColumnMappings(toFieldAndValue(visitor))
44+
.collect(Collectors.toList());
4145

4246
return DefaultInsertStatementProvider.withRecord(model.record())
43-
.withInsertStatement(calculateInsertStatement(collector))
47+
.withInsertStatement(calculateInsertStatement(fieldsAndValues))
4448
.build();
4549
}
4650

47-
private String calculateInsertStatement(FieldAndValueCollector collector) {
51+
private String calculateInsertStatement(List<Optional<FieldAndValue>> fieldsAndValues) {
4852
return "insert into" //$NON-NLS-1$
4953
+ spaceBefore(model.table().tableNameAtRuntime())
50-
+ spaceBefore(collector.columnsPhrase())
51-
+ spaceBefore(collector.valuesPhrase());
54+
+ spaceBefore(calculateColumnsPhrase(fieldsAndValues))
55+
+ spaceBefore(calculateValuesPhrase(fieldsAndValues));
56+
}
57+
58+
private String calculateColumnsPhrase(List<Optional<FieldAndValue>> fieldsAndValues) {
59+
return fieldsAndValues.stream()
60+
.filter(Optional::isPresent)
61+
.map(Optional::get)
62+
.map(FieldAndValue::fieldName)
63+
.collect(Collectors.joining(", ", "(", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
5264
}
5365

54-
private Function<AbstractColumnMapping, FieldAndValue> toFieldAndValue(ValuePhraseVisitor visitor) {
66+
private String calculateValuesPhrase(List<Optional<FieldAndValue>> fieldsAndValues) {
67+
return fieldsAndValues.stream()
68+
.filter(Optional::isPresent)
69+
.map(Optional::get)
70+
.map(FieldAndValue::valuePhrase)
71+
.collect(Collectors.joining(", ", "values (", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
72+
}
73+
74+
private Function<AbstractColumnMapping, Optional<FieldAndValue>> toFieldAndValue(ValuePhraseVisitor visitor) {
5575
return insertMapping -> toFieldAndValue(visitor, insertMapping);
5676
}
5777

58-
private FieldAndValue toFieldAndValue(ValuePhraseVisitor visitor, AbstractColumnMapping insertMapping) {
78+
private Optional<FieldAndValue> toFieldAndValue(ValuePhraseVisitor visitor, AbstractColumnMapping insertMapping) {
5979
return insertMapping.accept(visitor);
6080
}
6181

src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.mybatis.dynamic.sql.insert.render;
1717

18+
import java.util.Optional;
1819
import java.util.function.Function;
1920

2021
import org.mybatis.dynamic.sql.SqlColumn;
@@ -23,9 +24,10 @@
2324
import org.mybatis.dynamic.sql.util.InsertMappingVisitor;
2425
import org.mybatis.dynamic.sql.util.NullMapping;
2526
import org.mybatis.dynamic.sql.util.PropertyMapping;
27+
import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping;
2628
import org.mybatis.dynamic.sql.util.StringConstantMapping;
2729

28-
public class ValuePhraseVisitor extends InsertMappingVisitor<FieldAndValue> {
30+
public class ValuePhraseVisitor extends InsertMappingVisitor<Optional<FieldAndValue>> {
2931

3032
protected RenderingStrategy renderingStrategy;
3133

@@ -34,33 +36,44 @@ public ValuePhraseVisitor(RenderingStrategy renderingStrategy) {
3436
}
3537

3638
@Override
37-
public FieldAndValue visit(NullMapping mapping) {
39+
public Optional<FieldAndValue> visit(NullMapping mapping) {
3840
return FieldAndValue.withFieldName(mapping.mapColumn(SqlColumn::name))
3941
.withValuePhrase("null") //$NON-NLS-1$
40-
.build();
42+
.buildOptional();
4143
}
4244

4345
@Override
44-
public FieldAndValue visit(ConstantMapping mapping) {
46+
public Optional<FieldAndValue> visit(ConstantMapping mapping) {
4547
return FieldAndValue.withFieldName(mapping.mapColumn(SqlColumn::name))
4648
.withValuePhrase(mapping.constant())
47-
.build();
49+
.buildOptional();
4850
}
4951

5052
@Override
51-
public FieldAndValue visit(StringConstantMapping mapping) {
53+
public Optional<FieldAndValue> visit(StringConstantMapping mapping) {
5254
return FieldAndValue.withFieldName(mapping.mapColumn(SqlColumn::name))
5355
.withValuePhrase("'" + mapping.constant() + "'") //$NON-NLS-1$ //$NON-NLS-2$
54-
.build();
56+
.buildOptional();
5557
}
5658

5759
@Override
58-
public FieldAndValue visit(PropertyMapping mapping) {
60+
public Optional<FieldAndValue> visit(PropertyMapping mapping) {
5961
return FieldAndValue.withFieldName(mapping.mapColumn(SqlColumn::name))
6062
.withValuePhrase(mapping.mapColumn(toJdbcPlaceholder(mapping.property())))
61-
.build();
63+
.buildOptional();
6264
}
6365

66+
@Override
67+
public Optional<FieldAndValue> visit(PropertyWhenPresentMapping mapping) {
68+
if (mapping.shouldRender()) {
69+
return FieldAndValue.withFieldName(mapping.mapColumn(SqlColumn::name))
70+
.withValuePhrase(mapping.mapColumn(toJdbcPlaceholder(mapping.property())))
71+
.buildOptional();
72+
} else {
73+
return Optional.empty();
74+
}
75+
}
76+
6477
private Function<SqlColumn<?>, String> toJdbcPlaceholder(String parameterName) {
6578
return column -> column.renderingStrategy().orElse(renderingStrategy)
6679
.getFormattedJdbcPlaceholder(column, "record", parameterName); //$NON-NLS-1$

src/main/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* an insert - so the ColumnToColumnMapping is only supported on update statements.
2323
*
2424
* <p>Rather than implement this interface directly, we recommend implementing one of the derived
25-
* interfaces. The derived interfaces encapsulate the rules about which mappings are applicable to the
25+
* classes. The derived classes encapsulate the rules about which mappings are applicable to the
2626
* different types of statements.
2727
*
2828
* @author Jeff Butler
@@ -44,5 +44,7 @@ public interface ColumnMappingVisitor<T> {
4444

4545
T visit(PropertyMapping mapping);
4646

47+
T visit(PropertyWhenPresentMapping mapping);
48+
4749
T visit(ColumnToColumnMapping columnMapping);
4850
}

src/main/java/org/mybatis/dynamic/sql/util/GeneralInsertMappingVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public final T visit(PropertyMapping mapping) {
2626
throw new UnsupportedOperationException();
2727
}
2828

29+
@Override
30+
public final T visit(PropertyWhenPresentMapping mapping) {
31+
throw new UnsupportedOperationException();
32+
}
33+
2934
@Override
3035
public final T visit(ColumnToColumnMapping columnMapping) {
3136
throw new UnsupportedOperationException();

src/main/java/org/mybatis/dynamic/sql/util/MultiRowInsertMappingVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,9 @@ public final T visit(ColumnToColumnMapping columnMapping) {
3535
public final <R> T visit(ValueWhenPresentMapping<R> mapping) {
3636
throw new UnsupportedOperationException();
3737
}
38+
39+
@Override
40+
public final T visit(PropertyWhenPresentMapping mapping) {
41+
throw new UnsupportedOperationException();
42+
}
3843
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright 2016-2020 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+
* http://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.util;
17+
18+
import java.util.Objects;
19+
import java.util.function.Supplier;
20+
21+
import org.mybatis.dynamic.sql.SqlColumn;
22+
23+
public class PropertyWhenPresentMapping extends AbstractColumnMapping {
24+
private String property;
25+
private Supplier<?> valueSupplier;
26+
27+
private PropertyWhenPresentMapping(SqlColumn<?> column, String property, Supplier<?> valueSupplier) {
28+
super(column);
29+
this.property = Objects.requireNonNull(property);
30+
this.valueSupplier = Objects.requireNonNull(valueSupplier);
31+
}
32+
33+
public String property() {
34+
return property;
35+
}
36+
37+
public boolean shouldRender() {
38+
return valueSupplier.get() != null;
39+
}
40+
41+
@Override
42+
public <R> R accept(ColumnMappingVisitor<R> visitor) {
43+
return visitor.visit(this);
44+
}
45+
46+
public static PropertyWhenPresentMapping of(SqlColumn<?> column, String property, Supplier<?> valueSupplier) {
47+
return new PropertyWhenPresentMapping(column, property, valueSupplier);
48+
}
49+
}

src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@ public abstract class UpdateMappingVisitor<T> implements ColumnMappingVisitor<T>
2020
public final T visit(PropertyMapping mapping) {
2121
throw new UnsupportedOperationException();
2222
}
23+
24+
@Override
25+
public final T visit(PropertyWhenPresentMapping mapping) {
26+
throw new UnsupportedOperationException();
27+
}
2328
}

src/test/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitorTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ void testThatGeneralInsertVisitorErrorsForPropertyMapping() {
5151
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping));
5252
}
5353

54+
@Test
55+
void testThatGeneralInsertVisitorErrorsForPropertyWhenPresentMapping() {
56+
TestTable table = new TestTable();
57+
GeneralInsertVisitor tv = new GeneralInsertVisitor();
58+
PropertyWhenPresentMapping mapping = PropertyWhenPresentMapping.of(table.id, "id", () -> 3);
59+
60+
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping));
61+
}
62+
5463
@Test
5564
void testThatInsertVisitorErrorsForColumnToColumnMapping() {
5665
TestTable table = new TestTable();
@@ -123,6 +132,15 @@ void testThatMultiRowInsertVisitorErrorsForValueWhenPresentMapping() {
123132
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping));
124133
}
125134

135+
@Test
136+
void testThatMultiRowInsertVisitorErrorsForPropertyWhenPresentMapping() {
137+
TestTable table = new TestTable();
138+
MultiRowInsertVisitor tv = new MultiRowInsertVisitor();
139+
PropertyWhenPresentMapping mapping = PropertyWhenPresentMapping.of(table.id, "id", () -> 3);
140+
141+
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping));
142+
}
143+
126144
@Test
127145
void testThatUpdateVisitorErrorsForPropertyMapping() {
128146
TestTable table = new TestTable();
@@ -132,6 +150,15 @@ void testThatUpdateVisitorErrorsForPropertyMapping() {
132150
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping));
133151
}
134152

153+
@Test
154+
void testThatUpdateVisitorErrorsForPropertyWhenPresentMapping() {
155+
TestTable table = new TestTable();
156+
UpdateVisitor tv = new UpdateVisitor();
157+
PropertyWhenPresentMapping mapping = PropertyWhenPresentMapping.of(table.id, "id", () -> 3);
158+
159+
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping));
160+
}
161+
135162
private static class TestTable extends SqlTable {
136163
public SqlColumn<Integer> id;
137164
public SqlColumn<String> description;
@@ -191,6 +218,11 @@ public String visit(StringConstantMapping mapping) {
191218
public String visit(PropertyMapping mapping) {
192219
return "Property Mapping";
193220
}
221+
222+
@Override
223+
public String visit(PropertyWhenPresentMapping mapping) {
224+
return "Property Whn Present Mapping";
225+
}
194226
}
195227

196228
private static class UpdateVisitor extends UpdateMappingVisitor<String> {

0 commit comments

Comments
 (0)