diff --git a/CHANGELOG.md b/CHANGELOG.md index c1bb7a6bb..170972c99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ This log will detail notable changes to MyBatis Dynamic SQL. Full details are available on the GitHub milestone pages. +## Release 1.1.5 - Unreleased + +GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.1.5+](https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.1.5+) + +### Added + +- Added a general insert statement that does not require a separate record class to hold values for the insert. ([#201](https://github.com/mybatis/mybatis-dynamic-sql/issues/201)) + ## Release 1.1.4 - November 23, 2019 GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.1.4+](https://github.com/mybatis/mybatis-dynamic-sql/issues?q=milestone%3A1.1.4+) diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java index eb4cbdfd5..e1b248c4c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -17,11 +17,13 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Objects; import java.util.function.Supplier; import org.mybatis.dynamic.sql.delete.DeleteDSL; import org.mybatis.dynamic.sql.delete.DeleteModel; import org.mybatis.dynamic.sql.insert.BatchInsertDSL; +import org.mybatis.dynamic.sql.insert.GeneralInsertDSL; import org.mybatis.dynamic.sql.insert.InsertDSL; import org.mybatis.dynamic.sql.insert.InsertSelectDSL; import org.mybatis.dynamic.sql.insert.MultiRowInsertDSL; @@ -133,8 +135,8 @@ static MultiRowInsertDSL.IntoGatherer insertMultiple(Collection record return MultiRowInsertDSL.insert(records); } - static InsertSelectDSL.InsertColumnGatherer insertInto(SqlTable table) { - return InsertSelectDSL.insertInto(table); + static InsertIntoNextStep insertInto(SqlTable table) { + return new InsertIntoNextStep(table); } static FromGatherer select(BasicColumn...selectList) { @@ -636,4 +638,28 @@ static IsNotInCaseInsensitiveWhenPresent isNotInCaseInsensitiveWhenPresent(Colle static SortSpecification sortColumn(String name) { return SimpleSortSpecification.of(name); } + + class InsertIntoNextStep { + + private SqlTable table; + + private InsertIntoNextStep(SqlTable table) { + this.table = Objects.requireNonNull(table); + } + + public InsertSelectDSL withSelectStatement(Buildable selectModelBuilder) { + return InsertSelectDSL.insertInto(table) + .withSelectStatement(selectModelBuilder); + } + + public InsertSelectDSL.SelectGatherer withColumnList(SqlColumn...columns) { + return InsertSelectDSL.insertInto(table) + .withColumnList(columns); + } + + public GeneralInsertDSL.SetClauseFinisher set(SqlColumn column) { + return GeneralInsertDSL.insertInto(table) + .set(column); + } + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/AbstractMultiRowInsertModel.java b/src/main/java/org/mybatis/dynamic/sql/insert/AbstractMultiRowInsertModel.java index 270b92609..10168e63b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/AbstractMultiRowInsertModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/AbstractMultiRowInsertModel.java @@ -24,12 +24,12 @@ import java.util.stream.Stream; import org.mybatis.dynamic.sql.SqlTable; -import org.mybatis.dynamic.sql.util.InsertMapping; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; public abstract class AbstractMultiRowInsertModel { private SqlTable table; private List records; - private List columnMappings; + private List columnMappings; protected AbstractMultiRowInsertModel(AbstractBuilder builder) { table = Objects.requireNonNull(builder.table); @@ -37,7 +37,7 @@ protected AbstractMultiRowInsertModel(AbstractBuilder builder) { columnMappings = Objects.requireNonNull(builder.columnMappings); } - public Stream mapColumnMappings(Function mapper) { + public Stream mapColumnMappings(Function mapper) { return columnMappings.stream().map(mapper); } @@ -56,7 +56,7 @@ public int recordCount() { public abstract static class AbstractBuilder> { private SqlTable table; private List records = new ArrayList<>(); - private List columnMappings = new ArrayList<>(); + private List columnMappings = new ArrayList<>(); public S withTable(SqlTable table) { this.table = table; @@ -68,7 +68,7 @@ public S withRecords(Collection records) { return getThis(); } - public S withColumnMappings(List columnMappings) { + public S withColumnMappings(List columnMappings) { this.columnMappings.addAll(columnMappings); return getThis(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java index 1622856a3..b797fe2ba 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java @@ -22,8 +22,8 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.ConstantMapping; -import org.mybatis.dynamic.sql.util.InsertMapping; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.StringConstantMapping; @@ -32,7 +32,7 @@ public class BatchInsertDSL { private Collection records; private SqlTable table; - private List columnMappings = new ArrayList<>(); + private List columnMappings = new ArrayList<>(); private BatchInsertDSL(Collection records, SqlTable table) { this.records = records; diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertDSL.java new file mode 100644 index 000000000..7ca92b2db --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertDSL.java @@ -0,0 +1,97 @@ +/** + * Copyright 2016-2020 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 + * + * http://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.insert; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; +import org.mybatis.dynamic.sql.util.ConstantMapping; +import org.mybatis.dynamic.sql.util.NullMapping; +import org.mybatis.dynamic.sql.util.StringConstantMapping; +import org.mybatis.dynamic.sql.util.ValueMapping; + +public class GeneralInsertDSL { + private List insertMappings = new ArrayList<>(); + private SqlTable table; + + private GeneralInsertDSL(SqlTable table) { + this.table = Objects.requireNonNull(table); + } + + public SetClauseFinisher set(SqlColumn column) { + return new SetClauseFinisher<>(column); + } + + public GeneralInsertModel build() { + return new GeneralInsertModel.Builder() + .withTable(table) + .withInsertMappings(insertMappings) + .build(); + } + + public static GeneralInsertDSL insertInto(SqlTable table) { + return new GeneralInsertDSL(table); + } + + public class SetClauseFinisher { + + private SqlColumn column; + + public SetClauseFinisher(SqlColumn column) { + this.column = column; + } + + public GeneralInsertDSL toNull() { + insertMappings.add(NullMapping.of(column)); + return GeneralInsertDSL.this; + } + + public GeneralInsertDSL toConstant(String constant) { + insertMappings.add(ConstantMapping.of(column, constant)); + return GeneralInsertDSL.this; + } + + public GeneralInsertDSL toStringConstant(String constant) { + insertMappings.add(StringConstantMapping.of(column, constant)); + return GeneralInsertDSL.this; + } + + public GeneralInsertDSL toValue(T value) { + return toValue(() -> value); + } + + public GeneralInsertDSL toValue(Supplier valueSupplier) { + insertMappings.add(ValueMapping.of(column, valueSupplier)); + return GeneralInsertDSL.this; + } + + public GeneralInsertDSL toValueWhenPresent(T value) { + return toValueWhenPresent(() -> value); + } + + public GeneralInsertDSL toValueWhenPresent(Supplier valueSupplier) { + if (valueSupplier.get() != null) { + insertMappings.add(ValueMapping.of(column, valueSupplier)); + } + return GeneralInsertDSL.this; + } + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertModel.java b/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertModel.java new file mode 100644 index 000000000..e319516e5 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/insert/GeneralInsertModel.java @@ -0,0 +1,73 @@ +/** + * Copyright 2016-2019 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 + * + * http://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.insert; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; + +import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertRenderer; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; +import org.mybatis.dynamic.sql.render.RenderingStrategy; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; + +public class GeneralInsertModel { + + private SqlTable table; + private List insertMappings; + + private GeneralInsertModel(Builder builder) { + table = Objects.requireNonNull(builder.table); + insertMappings = builder.insertMappings; + } + + public Stream mapColumnMappings(Function mapper) { + return insertMappings.stream().map(mapper); + } + + public SqlTable table() { + return table; + } + + public GeneralInsertStatementProvider render(RenderingStrategy renderingStrategy) { + return GeneralInsertRenderer.withInsertModel(this) + .withRenderingStrategy(renderingStrategy) + .build() + .render(); + } + + public static class Builder { + private SqlTable table; + private List insertMappings = new ArrayList<>(); + + public Builder withTable(SqlTable table) { + this.table = table; + return this; + } + + public Builder withInsertMappings(List insertMappings) { + this.insertMappings.addAll(insertMappings); + return this; + } + + public GeneralInsertModel build() { + return new GeneralInsertModel(this); + } + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java index f8f329cae..2bb655344 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 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. @@ -21,8 +21,8 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.ConstantMapping; -import org.mybatis.dynamic.sql.util.InsertMapping; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.StringConstantMapping; @@ -31,7 +31,7 @@ public class InsertDSL { private T record; private SqlTable table; - private List columnMappings = new ArrayList<>(); + private List columnMappings = new ArrayList<>(); private InsertDSL(T record, SqlTable table) { this.record = record; diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/InsertModel.java b/src/main/java/org/mybatis/dynamic/sql/insert/InsertModel.java index 7f4c9bca8..502150e93 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/InsertModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/InsertModel.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 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. @@ -25,12 +25,12 @@ import org.mybatis.dynamic.sql.insert.render.InsertRenderer; import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategy; -import org.mybatis.dynamic.sql.util.InsertMapping; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; public class InsertModel { private SqlTable table; private T record; - private List columnMappings; + private List columnMappings; private InsertModel(Builder builder) { table = Objects.requireNonNull(builder.table); @@ -38,7 +38,7 @@ record = Objects.requireNonNull(builder.record); columnMappings = Objects.requireNonNull(builder.columnMappings); } - public Stream mapColumnMappings(Function mapper) { + public Stream mapColumnMappings(Function mapper) { return columnMappings.stream().map(mapper); } @@ -64,7 +64,7 @@ public static Builder withRecord(T record) { public static class Builder { private SqlTable table; private T record; - private List columnMappings = new ArrayList<>(); + private List columnMappings = new ArrayList<>(); public Builder withTable(SqlTable table) { this.table = table; @@ -76,7 +76,7 @@ public Builder withRecord(T record) { return this; } - public Builder withColumnMappings(List columnMappings) { + public Builder withColumnMappings(List columnMappings) { this.columnMappings.addAll(columnMappings); return this; } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/MultiRowInsertDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/MultiRowInsertDSL.java index 8b80bd913..1600c0b28 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/MultiRowInsertDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/MultiRowInsertDSL.java @@ -22,8 +22,8 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.ConstantMapping; -import org.mybatis.dynamic.sql.util.InsertMapping; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.StringConstantMapping; @@ -32,7 +32,7 @@ public class MultiRowInsertDSL { private Collection records; private SqlTable table; - private List columnMappings = new ArrayList<>(); + private List columnMappings = new ArrayList<>(); private MultiRowInsertDSL(Collection records, SqlTable table) { this.records = records; diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/DefaultInsertSelectStatementProvider.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/DefaultGeneralInsertStatementProvider.java similarity index 79% rename from src/main/java/org/mybatis/dynamic/sql/insert/render/DefaultInsertSelectStatementProvider.java rename to src/main/java/org/mybatis/dynamic/sql/insert/render/DefaultGeneralInsertStatementProvider.java index f647a8160..d86719e6c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/DefaultInsertSelectStatementProvider.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/DefaultGeneralInsertStatementProvider.java @@ -19,25 +19,26 @@ import java.util.Map; import java.util.Objects; -public class DefaultInsertSelectStatementProvider implements InsertSelectStatementProvider { +public class DefaultGeneralInsertStatementProvider + implements GeneralInsertStatementProvider, InsertSelectStatementProvider { private String insertStatement; - private Map parameters; - - private DefaultInsertSelectStatementProvider(Builder builder) { + private Map parameters = new HashMap<>(); + + private DefaultGeneralInsertStatementProvider(Builder builder) { insertStatement = Objects.requireNonNull(builder.insertStatement); - parameters = Objects.requireNonNull(builder.parameters); - } - - @Override - public String getInsertStatement() { - return insertStatement; + parameters.putAll(builder.parameters); } - + @Override public Map getParameters() { return parameters; } + @Override + public String getInsertStatement() { + return insertStatement; + } + public static Builder withInsertStatement(String insertStatement) { return new Builder().withInsertStatement(insertStatement); } @@ -50,14 +51,14 @@ public Builder withInsertStatement(String insertStatement) { this.insertStatement = insertStatement; return this; } - + public Builder withParameters(Map parameters) { this.parameters.putAll(parameters); return this; } - public DefaultInsertSelectStatementProvider build() { - return new DefaultInsertSelectStatementProvider(this); + public DefaultGeneralInsertStatementProvider build() { + return new DefaultGeneralInsertStatementProvider(this); } } } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/FieldAndValueAndParameters.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/FieldAndValueAndParameters.java new file mode 100644 index 000000000..9f03bf900 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/FieldAndValueAndParameters.java @@ -0,0 +1,73 @@ +/** + * Copyright 2016-2020 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 + * + * http://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.insert.render; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class FieldAndValueAndParameters { + private String fieldName; + private String valuePhrase; + private Map parameters; + + private FieldAndValueAndParameters(Builder builder) { + fieldName = Objects.requireNonNull(builder.fieldName); + valuePhrase = Objects.requireNonNull(builder.valuePhrase); + parameters = builder.parameters; + } + + public String fieldName() { + return fieldName; + } + + public String valuePhrase() { + return valuePhrase; + } + + public Map parameters() { + return parameters; + } + + public static Builder withFieldName(String fieldName) { + return new Builder().withFieldName(fieldName); + } + + public static class Builder { + private String fieldName; + private String valuePhrase; + private Map parameters = new HashMap<>(); + + public Builder withFieldName(String fieldName) { + this.fieldName = fieldName; + return this; + } + + public Builder withValuePhrase(String valuePhrase) { + this.valuePhrase = valuePhrase; + return this; + } + + public Builder withParameter(String key, Object value) { + parameters.put(key, value); + return this; + } + + public FieldAndValueAndParameters build() { + return new FieldAndValueAndParameters(this); + } + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/FieldAndValueAndParametersCollector.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/FieldAndValueAndParametersCollector.java new file mode 100644 index 000000000..e77671f63 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/FieldAndValueAndParametersCollector.java @@ -0,0 +1,66 @@ +/** + * Copyright 2016-2020 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 + * + * http://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.insert.render; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +public class FieldAndValueAndParametersCollector { + + private List fieldsAndValue = new ArrayList<>(); + + public FieldAndValueAndParametersCollector() { + super(); + } + + public void add(FieldAndValueAndParameters fieldAndValue) { + fieldsAndValue.add(fieldAndValue); + } + + public FieldAndValueAndParametersCollector merge(FieldAndValueAndParametersCollector other) { + fieldsAndValue.addAll(other.fieldsAndValue); + return this; + } + + public String columnsPhrase() { + return fieldsAndValue.stream() + .map(FieldAndValueAndParameters::fieldName) + .collect(Collectors.joining(", ", "(", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public String valuesPhrase() { + return fieldsAndValue.stream() + .map(FieldAndValueAndParameters::valuePhrase) + .collect(Collectors.joining(", ", "values (", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public Map parameters() { + return fieldsAndValue.stream() + .map(FieldAndValueAndParameters::parameters) + .collect(HashMap::new, HashMap::putAll, HashMap::putAll); + } + + public static Collector collect() { + return Collector.of(FieldAndValueAndParametersCollector::new, + FieldAndValueAndParametersCollector::add, + FieldAndValueAndParametersCollector::merge); + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertRenderer.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertRenderer.java new file mode 100644 index 000000000..84a2d844d --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertRenderer.java @@ -0,0 +1,86 @@ +/** + * Copyright 2016-2020 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 + * + * http://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.insert.render; + +import static org.mybatis.dynamic.sql.util.StringUtilities.spaceBefore; + +import java.util.Objects; +import java.util.function.Function; + +import org.mybatis.dynamic.sql.insert.GeneralInsertModel; +import org.mybatis.dynamic.sql.render.RenderingStrategy; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; + +public class GeneralInsertRenderer { + + private GeneralInsertModel model; + private RenderingStrategy renderingStrategy; + + private GeneralInsertRenderer(Builder builder) { + model = Objects.requireNonNull(builder.model); + renderingStrategy = Objects.requireNonNull(builder.renderingStrategy); + } + + public GeneralInsertStatementProvider render() { + GeneralInsertValuePhraseVisitor visitor = new GeneralInsertValuePhraseVisitor(renderingStrategy); + FieldAndValueAndParametersCollector collector = model.mapColumnMappings(toFieldAndValue(visitor)) + .collect(FieldAndValueAndParametersCollector.collect()); + + return DefaultGeneralInsertStatementProvider.withInsertStatement(calculateInsertStatement(collector)) + .withParameters(collector.parameters()) + .build(); + } + + private String calculateInsertStatement(FieldAndValueAndParametersCollector collector) { + return "insert into" //$NON-NLS-1$ + + spaceBefore(model.table().tableNameAtRuntime()) + + spaceBefore(collector.columnsPhrase()) + + spaceBefore(collector.valuesPhrase()); + } + + private Function toFieldAndValue( + GeneralInsertValuePhraseVisitor visitor) { + return insertMapping -> toFieldAndValue(visitor, insertMapping); + } + + private FieldAndValueAndParameters toFieldAndValue(GeneralInsertValuePhraseVisitor visitor, + AbstractColumnMapping insertMapping) { + return insertMapping.accept(visitor); + } + + public static Builder withInsertModel(GeneralInsertModel model) { + return new Builder().withInsertModel(model); + } + + public static class Builder { + private GeneralInsertModel model; + private RenderingStrategy renderingStrategy; + + public Builder withInsertModel(GeneralInsertModel model) { + this.model = model; + return this; + } + + public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) { + this.renderingStrategy = renderingStrategy; + return this; + } + + public GeneralInsertRenderer build() { + return new GeneralInsertRenderer(this); + } + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/util/InsertMapping.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertStatementProvider.java similarity index 77% rename from src/main/java/org/mybatis/dynamic/sql/util/InsertMapping.java rename to src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertStatementProvider.java index 4252111b8..b3b73bc91 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/InsertMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertStatementProvider.java @@ -13,9 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mybatis.dynamic.sql.util; +package org.mybatis.dynamic.sql.insert.render; -@FunctionalInterface -public interface InsertMapping { - R accept(InsertMappingVisitor visitor); +import java.util.Map; + +public interface GeneralInsertStatementProvider { + Map getParameters(); + + String getInsertStatement(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertValuePhraseVisitor.java new file mode 100644 index 000000000..2845e4696 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/GeneralInsertValuePhraseVisitor.java @@ -0,0 +1,75 @@ +/** + * Copyright 2016-2020 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 + * + * http://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.insert.render; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.render.RenderingStrategy; +import org.mybatis.dynamic.sql.util.ConstantMapping; +import org.mybatis.dynamic.sql.util.GeneralInsertMappingVisitor; +import org.mybatis.dynamic.sql.util.NullMapping; +import org.mybatis.dynamic.sql.util.StringConstantMapping; +import org.mybatis.dynamic.sql.util.ValueMapping; + +public class GeneralInsertValuePhraseVisitor implements GeneralInsertMappingVisitor { + + protected RenderingStrategy renderingStrategy; + private AtomicInteger sequence = new AtomicInteger(1); + + public GeneralInsertValuePhraseVisitor(RenderingStrategy renderingStrategy) { + this.renderingStrategy = renderingStrategy; + } + + @Override + public FieldAndValueAndParameters visit(NullMapping mapping) { + return FieldAndValueAndParameters.withFieldName(mapping.mapColumn(SqlColumn::name)) + .withValuePhrase("null") //$NON-NLS-1$ + .build(); + } + + @Override + public FieldAndValueAndParameters visit(ConstantMapping mapping) { + return FieldAndValueAndParameters.withFieldName(mapping.mapColumn(SqlColumn::name)) + .withValuePhrase(mapping.constant()) + .build(); + } + + @Override + public FieldAndValueAndParameters visit(StringConstantMapping mapping) { + return FieldAndValueAndParameters.withFieldName(mapping.mapColumn(SqlColumn::name)) + .withValuePhrase("'" + mapping.constant() + "'") //$NON-NLS-1$ //$NON-NLS-2$ + .build(); + } + + @Override + public FieldAndValueAndParameters visit(ValueMapping mapping) { + String mapKey = RenderingStrategy.formatParameterMapKey(sequence); + + String jdbcPlaceholder = mapping.mapColumn(toJdbcPlaceholder(mapKey)); + + return FieldAndValueAndParameters.withFieldName(mapping.mapColumn(SqlColumn::name)) + .withValuePhrase(jdbcPlaceholder) + .withParameter(mapKey, mapping.value()) + .build(); + } + + private Function, String> toJdbcPlaceholder(String parameterName) { + return column -> renderingStrategy + .getFormattedJdbcPlaceholder(column, RenderingStrategy.DEFAULT_PARAMETER_PREFIX, parameterName); + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderer.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderer.java index 99d34f3a4..5b858c8b0 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderer.java @@ -22,7 +22,7 @@ import org.mybatis.dynamic.sql.insert.InsertModel; import org.mybatis.dynamic.sql.render.RenderingStrategy; -import org.mybatis.dynamic.sql.util.InsertMapping; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; public class InsertRenderer { @@ -51,11 +51,11 @@ private String calculateInsertStatement(FieldAndValueCollector collector) { + spaceBefore(collector.valuesPhrase()); } - private Function toFieldAndValue(ValuePhraseVisitor visitor) { + private Function toFieldAndValue(ValuePhraseVisitor visitor) { return insertMapping -> toFieldAndValue(visitor, insertMapping); } - private FieldAndValue toFieldAndValue(ValuePhraseVisitor visitor, InsertMapping insertMapping) { + private FieldAndValue toFieldAndValue(ValuePhraseVisitor visitor, AbstractColumnMapping insertMapping) { return insertMapping.accept(visitor); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java index 9c6c07cd8..6a65e2c03 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertSelectRenderer.java @@ -40,7 +40,7 @@ private InsertSelectRenderer(Builder builder) { public InsertSelectStatementProvider render() { SelectStatementProvider selectStatement = model.selectModel().render(renderingStrategy); - return DefaultInsertSelectStatementProvider.withInsertStatement(calculateInsertStatement(selectStatement)) + return DefaultGeneralInsertStatementProvider.withInsertStatement(calculateInsertStatement(selectStatement)) .withParameters(selectStatement.getParameters()) .build(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowRenderingUtilities.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowRenderingUtilities.java index 489b48d4d..bbb98cf46 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowRenderingUtilities.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowRenderingUtilities.java @@ -17,17 +17,17 @@ import java.util.function.Function; -import org.mybatis.dynamic.sql.util.InsertMapping; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; public class MultiRowRenderingUtilities { private MultiRowRenderingUtilities() {} - public static Function toFieldAndValue(ValuePhraseVisitor visitor) { + public static Function toFieldAndValue(ValuePhraseVisitor visitor) { return insertMapping -> MultiRowRenderingUtilities.toFieldAndValue(visitor, insertMapping); } - public static FieldAndValue toFieldAndValue(ValuePhraseVisitor visitor, InsertMapping insertMapping) { + public static FieldAndValue toFieldAndValue(ValuePhraseVisitor visitor, AbstractColumnMapping insertMapping) { return insertMapping.accept(visitor); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java index 0e7235178..d654bcbc3 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. diff --git a/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java b/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java index 5216d1147..1d95f8b50 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java @@ -30,13 +30,13 @@ import org.mybatis.dynamic.sql.VisitableCondition; import org.mybatis.dynamic.sql.select.SelectModel; import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.Buildable; -import org.mybatis.dynamic.sql.util.ColumnMapping; +import org.mybatis.dynamic.sql.util.ColumnToColumnMapping; import org.mybatis.dynamic.sql.util.ConstantMapping; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.SelectMapping; import org.mybatis.dynamic.sql.util.StringConstantMapping; -import org.mybatis.dynamic.sql.util.UpdateMapping; import org.mybatis.dynamic.sql.util.ValueMapping; import org.mybatis.dynamic.sql.util.mybatis3.MyBatis3Utils; import org.mybatis.dynamic.sql.where.AbstractWhereDSL; @@ -46,7 +46,7 @@ public class UpdateDSL implements Buildable { private Function adapterFunction; - private List columnMappings = new ArrayList<>(); + private List columnMappings = new ArrayList<>(); private SqlTable table; private UpdateWhereBuilder whereBuilder = new UpdateWhereBuilder(); @@ -150,7 +150,7 @@ public UpdateDSL equalTo(Buildable buildable) { } public UpdateDSL equalTo(BasicColumn rightColumn) { - columnMappings.add(ColumnMapping.of(column, rightColumn)); + columnMappings.add(ColumnToColumnMapping.of(column, rightColumn)); return UpdateDSL.this; } diff --git a/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java b/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java index d006da8ca..f4f5e7124 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/UpdateModel.java @@ -27,13 +27,13 @@ import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.update.render.UpdateRenderer; import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider; -import org.mybatis.dynamic.sql.util.UpdateMapping; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.where.WhereModel; public class UpdateModel { private SqlTable table; private WhereModel whereModel; - private List columnMappings; + private List columnMappings; private UpdateModel(Builder builder) { table = Objects.requireNonNull(builder.table); @@ -49,7 +49,7 @@ public Optional whereModel() { return Optional.ofNullable(whereModel); } - public Stream mapColumnMappings(Function mapper) { + public Stream mapColumnMappings(Function mapper) { return columnMappings.stream().map(mapper); } @@ -68,14 +68,14 @@ public static Builder withTable(SqlTable table) { public static class Builder { private SqlTable table; private WhereModel whereModel; - private List columnMappings = new ArrayList<>(); + private List columnMappings = new ArrayList<>(); public Builder withTable(SqlTable table) { this.table = table; return this; } - public Builder withColumnMappings(List columnMappings) { + public Builder withColumnMappings(List columnMappings) { this.columnMappings.addAll(columnMappings); return this; } diff --git a/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java index abb3f4d69..e16fe41e1 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -24,7 +24,7 @@ import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.select.render.SelectRenderer; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; -import org.mybatis.dynamic.sql.util.ColumnMapping; +import org.mybatis.dynamic.sql.util.ColumnToColumnMapping; import org.mybatis.dynamic.sql.util.ConstantMapping; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.NullMapping; @@ -100,7 +100,7 @@ public FragmentAndParameters visit(SelectMapping mapping) { } @Override - public FragmentAndParameters visit(ColumnMapping mapping) { + public FragmentAndParameters visit(ColumnToColumnMapping mapping) { String setPhrase = mapping.mapColumn(SqlColumn::name) + " = " //$NON-NLS-1$ + mapping.rightColumn().renderWithTableAlias(TableAliasCalculator.empty()); diff --git a/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java b/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java index 2bf75bb90..9c18e2218 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/render/UpdateRenderer.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -26,9 +26,9 @@ import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.render.TableAliasCalculator; import org.mybatis.dynamic.sql.update.UpdateModel; +import org.mybatis.dynamic.sql.util.AbstractColumnMapping; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.FragmentCollector; -import org.mybatis.dynamic.sql.util.UpdateMapping; import org.mybatis.dynamic.sql.where.WhereModel; import org.mybatis.dynamic.sql.where.render.WhereClauseProvider; import org.mybatis.dynamic.sql.where.render.WhereRenderer; @@ -98,11 +98,12 @@ private Optional renderWhereClause(WhereModel whereModel) { .render(); } - private Function toFragmentAndParameters(SetPhraseVisitor visitor) { + private Function toFragmentAndParameters(SetPhraseVisitor visitor) { return updateMapping -> toFragmentAndParameters(visitor, updateMapping); } - private FragmentAndParameters toFragmentAndParameters(SetPhraseVisitor visitor, UpdateMapping updateMapping) { + private FragmentAndParameters toFragmentAndParameters(SetPhraseVisitor visitor, + AbstractColumnMapping updateMapping) { return updateMapping.accept(visitor); } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/AbstractColumnMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/AbstractColumnMapping.java index 1714e9d7e..17bcf7454 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/AbstractColumnMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/AbstractColumnMapping.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 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. @@ -30,5 +30,6 @@ protected AbstractColumnMapping(SqlColumn column) { public R mapColumn(Function, R> mapper) { return mapper.apply(column); } + + public abstract R accept(ColumnMappingVisitor visitor); } - \ No newline at end of file diff --git a/src/main/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitor.java b/src/main/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitor.java new file mode 100644 index 000000000..0235d3b90 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitor.java @@ -0,0 +1,46 @@ +/** + * Copyright 2016-2020 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 + * + * http://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.util; + +/** + * Visitor for all column mappings. Various column mappings are used by insert and update + * statements. Only the null and constant mappings are supported by all statements. Other mappings + * may or may not be supported. For example, it makes no sense to map a column to another column in + * an insert - so the ColumnToColumnMapping is only supported on update statements. + * + *

Rather than implement this interface directly, we recommend implementing one of the derived + * interfaces. The derived interfaces encapsulate the rules about which mappings are applicable to the + * different types of statements. + * + * @author Jeff Butler + * + * @param The type of object created by the visitor + */ +public interface ColumnMappingVisitor { + T visit(NullMapping mapping); + + T visit(ConstantMapping mapping); + + T visit(StringConstantMapping mapping); + + T visit(ValueMapping mapping); + + T visit(SelectMapping mapping); + + T visit(PropertyMapping mapping); + + T visit(ColumnToColumnMapping columnMapping); +} diff --git a/src/main/java/org/mybatis/dynamic/sql/util/ColumnMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/ColumnToColumnMapping.java similarity index 69% rename from src/main/java/org/mybatis/dynamic/sql/util/ColumnMapping.java rename to src/main/java/org/mybatis/dynamic/sql/util/ColumnToColumnMapping.java index fbe668c7d..c7649a446 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/ColumnMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/ColumnToColumnMapping.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 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. @@ -18,11 +18,11 @@ import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.SqlColumn; -public class ColumnMapping extends AbstractColumnMapping implements UpdateMapping { +public class ColumnToColumnMapping extends AbstractColumnMapping { private BasicColumn rightColumn; - private ColumnMapping(SqlColumn column, BasicColumn rightColumn) { + private ColumnToColumnMapping(SqlColumn column, BasicColumn rightColumn) { super(column); this.rightColumn = rightColumn; } @@ -32,11 +32,11 @@ public BasicColumn rightColumn() { } @Override - public R accept(UpdateMappingVisitor visitor) { + public R accept(ColumnMappingVisitor visitor) { return visitor.visit(this); } - public static ColumnMapping of(SqlColumn column, BasicColumn rightColumn) { - return new ColumnMapping(column, rightColumn); + public static ColumnToColumnMapping of(SqlColumn column, BasicColumn rightColumn) { + return new ColumnToColumnMapping(column, rightColumn); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/ConstantMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/ConstantMapping.java index 6e4b89f4c..f2942454c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/ConstantMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/ConstantMapping.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 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. @@ -24,7 +24,7 @@ * @author Jeff Butler * */ -public class ConstantMapping extends AbstractColumnMapping implements InsertMapping, UpdateMapping { +public class ConstantMapping extends AbstractColumnMapping { private String constant; private ConstantMapping(SqlColumn column) { @@ -42,12 +42,7 @@ public static ConstantMapping of(SqlColumn column, String constant) { } @Override - public R accept(UpdateMappingVisitor visitor) { - return visitor.visit(this); - } - - @Override - public R accept(InsertMappingVisitor visitor) { + public R accept(ColumnMappingVisitor visitor) { return visitor.visit(this); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/UpdateMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/GeneralInsertMappingVisitor.java similarity index 55% rename from src/main/java/org/mybatis/dynamic/sql/util/UpdateMapping.java rename to src/main/java/org/mybatis/dynamic/sql/util/GeneralInsertMappingVisitor.java index 66dfaa2da..be9f2a552 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/UpdateMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/GeneralInsertMappingVisitor.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -15,7 +15,19 @@ */ package org.mybatis.dynamic.sql.util; -@FunctionalInterface -public interface UpdateMapping { - R accept(UpdateMappingVisitor visitor); +public interface GeneralInsertMappingVisitor extends ColumnMappingVisitor { + @Override + default T visit(SelectMapping mapping) { + throw new UnsupportedOperationException(); + } + + @Override + default T visit(PropertyMapping mapping) { + throw new UnsupportedOperationException(); + } + + @Override + default T visit(ColumnToColumnMapping columnMapping) { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/InsertMappingVisitor.java b/src/main/java/org/mybatis/dynamic/sql/util/InsertMappingVisitor.java index d0d919a98..861d54391 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/InsertMappingVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/InsertMappingVisitor.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2020 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. @@ -15,12 +15,19 @@ */ package org.mybatis.dynamic.sql.util; -public interface InsertMappingVisitor { - T visit(NullMapping mapping); +public interface InsertMappingVisitor extends ColumnMappingVisitor { + @Override + default T visit(ValueMapping mapping) { + throw new UnsupportedOperationException(); + } + + @Override + default T visit(SelectMapping mapping) { + throw new UnsupportedOperationException(); + } - T visit(ConstantMapping mapping); - - T visit(StringConstantMapping mapping); - - T visit(PropertyMapping mapping); + @Override + default T visit(ColumnToColumnMapping columnMapping) { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/NullMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/NullMapping.java index 87426396d..c47041fa9 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/NullMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/NullMapping.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 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. @@ -17,7 +17,7 @@ import org.mybatis.dynamic.sql.SqlColumn; -public class NullMapping extends AbstractColumnMapping implements InsertMapping, UpdateMapping { +public class NullMapping extends AbstractColumnMapping { private NullMapping(SqlColumn column) { super(column); } @@ -27,12 +27,7 @@ public static NullMapping of(SqlColumn column) { } @Override - public R accept(UpdateMappingVisitor visitor) { - return visitor.visit(this); - } - - @Override - public R accept(InsertMappingVisitor visitor) { + public R accept(ColumnMappingVisitor visitor) { return visitor.visit(this); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/PropertyMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/PropertyMapping.java index a97f35274..9410fad55 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/PropertyMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/PropertyMapping.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 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. @@ -17,7 +17,7 @@ import org.mybatis.dynamic.sql.SqlColumn; -public class PropertyMapping extends AbstractColumnMapping implements InsertMapping { +public class PropertyMapping extends AbstractColumnMapping { private String property; private PropertyMapping(SqlColumn column) { @@ -29,7 +29,7 @@ public String property() { } @Override - public S accept(InsertMappingVisitor visitor) { + public R accept(ColumnMappingVisitor visitor) { return visitor.visit(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/SelectMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/SelectMapping.java index 304540873..7e43caf9a 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/SelectMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/SelectMapping.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 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. @@ -18,7 +18,7 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.select.SelectModel; -public class SelectMapping extends AbstractColumnMapping implements UpdateMapping { +public class SelectMapping extends AbstractColumnMapping { private SelectModel selectModel; @@ -32,7 +32,7 @@ public SelectModel selectModel() { } @Override - public R accept(UpdateMappingVisitor visitor) { + public R accept(ColumnMappingVisitor visitor) { return visitor.visit(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/SqlProviderAdapter.java b/src/main/java/org/mybatis/dynamic/sql/util/SqlProviderAdapter.java index e645334d5..4f35ca29b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/SqlProviderAdapter.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/SqlProviderAdapter.java @@ -16,6 +16,7 @@ package org.mybatis.dynamic.sql.util; import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertSelectStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider; @@ -34,6 +35,10 @@ public String delete(DeleteStatementProvider deleteStatement) { return deleteStatement.getDeleteStatement(); } + public String generalInsert(GeneralInsertStatementProvider insertStatement) { + return insertStatement.getInsertStatement(); + } + public String insert(InsertStatementProvider insertStatement) { return insertStatement.getInsertStatement(); } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/StringConstantMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/StringConstantMapping.java index f213fdaa2..32904bb53 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/StringConstantMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/StringConstantMapping.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 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. @@ -24,7 +24,7 @@ * @author Jeff Butler * */ -public class StringConstantMapping extends AbstractColumnMapping implements InsertMapping, UpdateMapping { +public class StringConstantMapping extends AbstractColumnMapping { private String constant; private StringConstantMapping(SqlColumn column) { @@ -42,12 +42,7 @@ public static StringConstantMapping of(SqlColumn column, String constant) { } @Override - public R accept(UpdateMappingVisitor visitor) { - return visitor.visit(this); - } - - @Override - public R accept(InsertMappingVisitor visitor) { + public R accept(ColumnMappingVisitor visitor) { return visitor.visit(this); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java b/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java index 18f43602d..c40ab48e3 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2020 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. @@ -15,16 +15,9 @@ */ package org.mybatis.dynamic.sql.util; -public interface UpdateMappingVisitor { - T visit(NullMapping mapping); - - T visit(ConstantMapping mapping); - - T visit(StringConstantMapping mapping); - - T visit(ValueMapping mapping); - - T visit(SelectMapping mapping); - - T visit(ColumnMapping columnMapping); +public interface UpdateMappingVisitor extends ColumnMappingVisitor { + @Override + default T visit(PropertyMapping mapping) { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/ValueMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/ValueMapping.java index 5b206eb01..b8c7934c0 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/ValueMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/ValueMapping.java @@ -19,7 +19,7 @@ import org.mybatis.dynamic.sql.SqlColumn; -public class ValueMapping extends AbstractColumnMapping implements UpdateMapping { +public class ValueMapping extends AbstractColumnMapping { private Supplier valueSupplier; @@ -33,7 +33,7 @@ public T value() { } @Override - public R accept(UpdateMappingVisitor visitor) { + public R accept(ColumnMappingVisitor visitor) { return visitor.visit(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/mybatis3/MyBatis3Utils.java b/src/main/java/org/mybatis/dynamic/sql/util/mybatis3/MyBatis3Utils.java index 0eb5223b5..c41834391 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/mybatis3/MyBatis3Utils.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/mybatis3/MyBatis3Utils.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -27,8 +27,10 @@ import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.delete.DeleteDSLCompleter; import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; +import org.mybatis.dynamic.sql.insert.GeneralInsertDSL; import org.mybatis.dynamic.sql.insert.InsertDSL; import org.mybatis.dynamic.sql.insert.MultiRowInsertDSL; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategies; @@ -93,6 +95,18 @@ public static int insert(ToIntFunction> mapper, R return mapper.applyAsInt(insert(record, table, completer)); } + public static GeneralInsertStatementProvider insert(SqlTable table, + UnaryOperator completer) { + return completer.apply(GeneralInsertDSL.insertInto(table)) + .build() + .render(RenderingStrategies.MYBATIS3); + } + + public static int insert(ToIntFunction mapper, + SqlTable table, UnaryOperator completer) { + return mapper.applyAsInt(insert(table, completer)); + } + public static MultiRowInsertStatementProvider insertMultiple(Collection records, SqlTable table, UnaryOperator> completer) { return completer.apply(SqlBuilder.insertMultiple(records).into(table)) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinUpdateBuilder.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinUpdateBuilder.kt index 8a10346d9..836e20cc3 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinUpdateBuilder.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinUpdateBuilder.kt @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -16,6 +16,7 @@ package org.mybatis.dynamic.sql.util.kotlin import org.mybatis.dynamic.sql.SqlColumn +import org.mybatis.dynamic.sql.insert.GeneralInsertDSL import org.mybatis.dynamic.sql.insert.InsertDSL import org.mybatis.dynamic.sql.insert.MultiRowInsertDSL import org.mybatis.dynamic.sql.update.UpdateDSL @@ -23,6 +24,8 @@ import org.mybatis.dynamic.sql.update.UpdateModel import org.mybatis.dynamic.sql.util.Buildable // insert completers are here because sonar doesn't see them as covered if they are in a file by themselves +typealias GeneralInsertCompleter = GeneralInsertDSL.() -> GeneralInsertDSL + typealias InsertCompleter = InsertDSL.() -> InsertDSL typealias MultiRowInsertCompleter = MultiRowInsertDSL.() -> MultiRowInsertDSL @@ -30,7 +33,7 @@ typealias MultiRowInsertCompleter = MultiRowInsertDSL.() -> MultiRowInsert typealias UpdateCompleter = KotlinUpdateBuilder.() -> Buildable class KotlinUpdateBuilder(private val dsl: UpdateDSL) : - KotlinBaseBuilder.UpdateWhereBuilder, KotlinUpdateBuilder>() { + KotlinBaseBuilder.UpdateWhereBuilder, KotlinUpdateBuilder>() { fun set(column: SqlColumn): UpdateDSL.SetClauseFinisher = dsl.set(column) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/MapperSupportFunctions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/MapperSupportFunctions.kt index 9832234b3..56a42f970 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/MapperSupportFunctions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/MapperSupportFunctions.kt @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -19,6 +19,7 @@ import org.mybatis.dynamic.sql.BasicColumn import org.mybatis.dynamic.sql.SqlBuilder import org.mybatis.dynamic.sql.SqlTable import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider import org.mybatis.dynamic.sql.select.QueryExpressionDSL @@ -36,22 +37,53 @@ fun deleteFrom(mapper: (DeleteStatementProvider) -> Int, table: SqlTable, comple fun insert(mapper: (InsertStatementProvider) -> Int, record: T, table: SqlTable, completer: InsertCompleter) = mapper(SqlBuilder.insert(record).into(table, completer)) -fun insertMultiple(mapper: (MultiRowInsertStatementProvider) -> Int, records: Collection, table: SqlTable, completer: MultiRowInsertCompleter) = +fun insertInto(mapper: (GeneralInsertStatementProvider) -> Int, table: SqlTable, completer: GeneralInsertCompleter) = + mapper(insertInto(table, completer)) + +fun insertMultiple( + mapper: (MultiRowInsertStatementProvider) -> Int, + records: Collection, + table: SqlTable, + completer: MultiRowInsertCompleter +) = mapper(SqlBuilder.insertMultiple(records).into(table, completer)) -fun selectDistinct(mapper: (SelectStatementProvider) -> List, selectList: List, table: SqlTable, completer: SelectCompleter) = +fun selectDistinct( + mapper: (SelectStatementProvider) -> List, + selectList: List, + table: SqlTable, + completer: SelectCompleter +) = mapper(SqlBuilder.selectDistinct(selectList).from(table, completer)) -fun selectList(mapper: (SelectStatementProvider) -> List, selectList: List, table: SqlTable, completer: SelectCompleter) = +fun selectList( + mapper: (SelectStatementProvider) -> List, + selectList: List, + table: SqlTable, + completer: SelectCompleter +) = mapper(SqlBuilder.select(selectList).from(table, completer)) -fun selectList(mapper: (SelectStatementProvider) -> List, start: QueryExpressionDSL, completer: SelectCompleter) = +fun selectList( + mapper: (SelectStatementProvider) -> List, + start: QueryExpressionDSL, + completer: SelectCompleter +) = mapper(select(start, completer)) -fun selectOne(mapper: (SelectStatementProvider) -> T?, selectList: List, table: SqlTable, completer: SelectCompleter) = +fun selectOne( + mapper: (SelectStatementProvider) -> T?, + selectList: List, + table: SqlTable, + completer: SelectCompleter +) = mapper(SqlBuilder.select(selectList).from(table, completer)) -fun selectOne(mapper: (SelectStatementProvider) -> T?, start: QueryExpressionDSL, completer: SelectCompleter) = +fun selectOne( + mapper: (SelectStatementProvider) -> T?, + start: QueryExpressionDSL, + completer: SelectCompleter +) = mapper(select(start, completer)) fun update(mapper: (UpdateStatementProvider) -> Int, table: SqlTable, completer: UpdateCompleter) = diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/ProviderBuilderFunctions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/ProviderBuilderFunctions.kt index e6bc19aa9..8d803d8e5 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/ProviderBuilderFunctions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/mybatis3/ProviderBuilderFunctions.kt @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -18,8 +18,10 @@ package org.mybatis.dynamic.sql.util.kotlin.mybatis3 import org.mybatis.dynamic.sql.SqlBuilder import org.mybatis.dynamic.sql.SqlTable import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider +import org.mybatis.dynamic.sql.insert.GeneralInsertDSL import org.mybatis.dynamic.sql.insert.InsertDSL import org.mybatis.dynamic.sql.insert.MultiRowInsertDSL +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider import org.mybatis.dynamic.sql.render.RenderingStrategies @@ -42,18 +44,28 @@ fun deleteFrom(table: SqlTable, completer: DeleteCompleter): DeleteStatementProv } fun InsertDSL.IntoGatherer.into(table: SqlTable, completer: InsertCompleter): InsertStatementProvider = - completer(into(table)).build().render(RenderingStrategies.MYBATIS3) + completer(into(table)).build().render(RenderingStrategies.MYBATIS3) -fun MultiRowInsertDSL.IntoGatherer.into(table: SqlTable, completer: MultiRowInsertCompleter): MultiRowInsertStatementProvider = - completer(into(table)).build().render(RenderingStrategies.MYBATIS3) +fun MultiRowInsertDSL.IntoGatherer.into( + table: SqlTable, + completer: MultiRowInsertCompleter +): MultiRowInsertStatementProvider = + completer(into(table)).build().render(RenderingStrategies.MYBATIS3) -fun QueryExpressionDSL.FromGatherer.from(table: SqlTable, completer: SelectCompleter): SelectStatementProvider { +fun QueryExpressionDSL.FromGatherer.from( + table: SqlTable, + completer: SelectCompleter +): SelectStatementProvider { val builder = KotlinQueryBuilder(from(table)) completer(builder) return builder.build().render(RenderingStrategies.MYBATIS3) } -fun QueryExpressionDSL.FromGatherer.from(table: SqlTable, alias: String, completer: SelectCompleter): SelectStatementProvider { +fun QueryExpressionDSL.FromGatherer.from( + table: SqlTable, + alias: String, + completer: SelectCompleter +): SelectStatementProvider { val builder = KotlinQueryBuilder(from(table, alias)) completer(builder) return builder.build().render(RenderingStrategies.MYBATIS3) @@ -70,3 +82,6 @@ fun update(table: SqlTable, completer: UpdateCompleter): UpdateStatementProvider completer(builder) return builder.build().render(RenderingStrategies.MYBATIS3) } + +fun insertInto(table: SqlTable, completer: GeneralInsertCompleter): GeneralInsertStatementProvider = + completer(GeneralInsertDSL.insertInto(table)).build().render(RenderingStrategies.MYBATIS3) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt index eebd52318..8f5ced06f 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -19,6 +19,7 @@ import org.mybatis.dynamic.sql.BasicColumn import org.mybatis.dynamic.sql.SqlBuilder import org.mybatis.dynamic.sql.SqlTable import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider import org.mybatis.dynamic.sql.select.render.SelectStatementProvider import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider @@ -45,6 +46,12 @@ fun NamedParameterJdbcTemplate.insert(insertStatement: InsertStatementProvid fun NamedParameterJdbcTemplate.insert(record: T, table: SqlTable, completer: InsertCompleter) = insert(SqlBuilder.insert(record).into(table, completer)) +fun NamedParameterJdbcTemplate.insert(insertStatement: GeneralInsertStatementProvider) = + update(insertStatement.insertStatement, insertStatement.parameters) + +fun NamedParameterJdbcTemplate.insertInto(table: SqlTable, completer: GeneralInsertCompleter) = + insert(org.mybatis.dynamic.sql.util.kotlin.spring.insertInto(table, completer)) + fun NamedParameterJdbcTemplate.select(vararg selectList: BasicColumn) = SelectListFromGatherer(selectList.toList(), this) @@ -54,10 +61,16 @@ fun NamedParameterJdbcTemplate.selectDistinct(vararg selectList: BasicColumn) = fun NamedParameterJdbcTemplate.selectOne(vararg selectList: BasicColumn) = SelectOneFromGatherer(selectList.toList(), this) -fun NamedParameterJdbcTemplate.selectList(selectStatement: SelectStatementProvider, rowMapper: (rs: ResultSet, rowNum: Int) -> T): List = +fun NamedParameterJdbcTemplate.selectList( + selectStatement: SelectStatementProvider, + rowMapper: (rs: ResultSet, rowNum: Int) -> T +): List = query(selectStatement.selectStatement, selectStatement.parameters, rowMapper) -fun NamedParameterJdbcTemplate.selectOne(selectStatement: SelectStatementProvider, rowMapper: (rs: ResultSet, rowNum: Int) -> T): T? = +fun NamedParameterJdbcTemplate.selectOne( + selectStatement: SelectStatementProvider, + rowMapper: (rs: ResultSet, rowNum: Int) -> T +): T? = queryForObject(selectStatement.selectStatement, selectStatement.parameters, rowMapper) fun NamedParameterJdbcTemplate.update(updateStatement: UpdateStatementProvider) = @@ -67,7 +80,10 @@ fun NamedParameterJdbcTemplate.update(table: SqlTable, completer: UpdateComplete update(org.mybatis.dynamic.sql.util.kotlin.spring.update(table, completer)) // support classes for select DSL -class SelectListFromGatherer(private val selectList: List, private val template: NamedParameterJdbcTemplate) { +class SelectListFromGatherer( + private val selectList: List, + private val template: NamedParameterJdbcTemplate +) { fun from(table: SqlTable, completer: SelectCompleter) = SelectListMapperGatherer(SqlBuilder.select(selectList).from(table, completer), template) @@ -75,7 +91,10 @@ class SelectListFromGatherer(private val selectList: List, private SelectListMapperGatherer(SqlBuilder.select(selectList).from(table, alias, completer), template) } -class SelectDistinctFromGatherer(private val selectList: List, private val template: NamedParameterJdbcTemplate) { +class SelectDistinctFromGatherer( + private val selectList: List, + private val template: NamedParameterJdbcTemplate +) { fun from(table: SqlTable, completer: SelectCompleter) = SelectListMapperGatherer(SqlBuilder.selectDistinct(selectList).from(table, completer), template) @@ -83,7 +102,10 @@ class SelectDistinctFromGatherer(private val selectList: List, priv SelectListMapperGatherer(SqlBuilder.selectDistinct(selectList).from(table, alias, completer), template) } -class SelectOneFromGatherer(private val selectList: List, private val template: NamedParameterJdbcTemplate) { +class SelectOneFromGatherer( + private val selectList: List, + private val template: NamedParameterJdbcTemplate +) { fun from(table: SqlTable, completer: SelectCompleter) = SelectOneMapperGatherer(SqlBuilder.select(selectList).from(table, completer), template) @@ -91,12 +113,18 @@ class SelectOneFromGatherer(private val selectList: List, private v SelectOneMapperGatherer(SqlBuilder.select(selectList).from(table, alias, completer), template) } -class SelectListMapperGatherer(private val selectStatement: SelectStatementProvider, private val template: NamedParameterJdbcTemplate) { +class SelectListMapperGatherer( + private val selectStatement: SelectStatementProvider, + private val template: NamedParameterJdbcTemplate +) { fun withRowMapper(rowMapper: (rs: ResultSet, rowNum: Int) -> T) = template.selectList(selectStatement, rowMapper) } -class SelectOneMapperGatherer(private val selectStatement: SelectStatementProvider, private val template: NamedParameterJdbcTemplate) { +class SelectOneMapperGatherer( + private val selectStatement: SelectStatementProvider, + private val template: NamedParameterJdbcTemplate +) { fun withRowMapper(rowMapper: (rs: ResultSet, rowNum: Int) -> T) = template.selectOne(selectStatement, rowMapper) } diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/ProviderBuilderFunctions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/ProviderBuilderFunctions.kt index e3a032135..016568ee1 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/ProviderBuilderFunctions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/ProviderBuilderFunctions.kt @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -18,7 +18,9 @@ package org.mybatis.dynamic.sql.util.kotlin.spring import org.mybatis.dynamic.sql.SqlBuilder import org.mybatis.dynamic.sql.SqlTable import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider +import org.mybatis.dynamic.sql.insert.GeneralInsertDSL import org.mybatis.dynamic.sql.insert.InsertDSL +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider import org.mybatis.dynamic.sql.render.RenderingStrategies import org.mybatis.dynamic.sql.select.QueryExpressionDSL @@ -40,15 +42,22 @@ fun deleteFrom(table: SqlTable, completer: DeleteCompleter): DeleteStatementProv } fun InsertDSL.IntoGatherer.into(table: SqlTable, completer: InsertCompleter): InsertStatementProvider = - completer(into(table)).build().render(RenderingStrategies.SPRING_NAMED_PARAMETER) + completer(into(table)).build().render(RenderingStrategies.SPRING_NAMED_PARAMETER) -fun QueryExpressionDSL.FromGatherer.from(table: SqlTable, completer: SelectCompleter): SelectStatementProvider { +fun QueryExpressionDSL.FromGatherer.from( + table: SqlTable, + completer: SelectCompleter +): SelectStatementProvider { val builder = KotlinQueryBuilder(from(table)) completer(builder) return builder.build().render(RenderingStrategies.SPRING_NAMED_PARAMETER) } -fun QueryExpressionDSL.FromGatherer.from(table: SqlTable, alias: String, completer: SelectCompleter): SelectStatementProvider { +fun QueryExpressionDSL.FromGatherer.from( + table: SqlTable, + alias: String, + completer: SelectCompleter +): SelectStatementProvider { val builder = KotlinQueryBuilder(from(table, alias)) completer(builder) return builder.build().render(RenderingStrategies.SPRING_NAMED_PARAMETER) @@ -59,3 +68,6 @@ fun update(table: SqlTable, completer: UpdateCompleter): UpdateStatementProvider completer(builder) return builder.build().render(RenderingStrategies.SPRING_NAMED_PARAMETER) } + +fun insertInto(table: SqlTable, completer: GeneralInsertCompleter): GeneralInsertStatementProvider = + completer(GeneralInsertDSL.insertInto(table)).build().render(RenderingStrategies.SPRING_NAMED_PARAMETER) diff --git a/src/site/markdown/docs/insert.md b/src/site/markdown/docs/insert.md index 669954a28..6e5af734c 100644 --- a/src/site/markdown/docs/insert.md +++ b/src/site/markdown/docs/insert.md @@ -4,7 +4,8 @@ The library will generate a variety of INSERT statements: 1. An insert for a single record 1. An insert for multiple records with a single statement 1. An insert for multiple records with a JDBC batch -2. An insert with a select statement +1. A general insert statement +1. An insert with a select statement ## Single Record Insert A single record insert is a statement that inserts a single record into a table. This statement is configured differently than other statements in the library so that MyBatis' support for generated keys will work properly. To use the statement, you must first create an object that will map to the database row, then map object attributes to fields in the database. For example: @@ -213,6 +214,66 @@ It is important to open a MyBatis session by setting the executor type to BATCH. Notice that the same mapper method that is used to insert a single record is now executed multiple times. The `map` methods are the same with the exception that the `toPropertyWhenPresent` mapping is not supported for batch inserts. +## General Insert Statement +A general insert is used to build arbitrary insert statements. The general insert does not require a separate record object o hold values for the statement - any value can be passed into the statement. This version of the insert is not convienient for retriving generated keys with MyBatis - for that use case we recommend the "single record insert". However the general insert is perfectly acceptible for Spring JDBC template or MyBatis inserts that do not return generated keys. For example + +```java + GeneralInsertStatementProvider insertStatement = insertInto(animalData) + .set(id).toValue(101) + .set(animalName).toStringConstant("Fred") + .set(brainWeight).toConstant("2.2") + .set(bodyWeight).toValue(4.5) + .build() + .render(RenderingStrategies.MYBATIS3); +``` + +Notice the `set` method. It is used to set the value for a database column. There are several different possibilities: + +1. `set(column).toNull()` will insert a null into a column +2. `set(column).toConstant(constant_value)` will insert a constant into a column. The constant_value will be written into the generated insert statement exactly as entered +3. `set(column).toStringConstant(constant_value)` will insert a constant into a column. The constant_value will be written into the generated insert statement surrounded by single quote marks (as an SQL String) +4. `set(column).toValue(value)` will insert a value into a column. The value of the property will be bound to the SQL statement as a prepared statement parameter +5. `set(column).toValueWhenPresent(property, Supplier valueSupplier)` will insert a value into a column if the value is non-null. The value of the property will be bound to the SQL statement as a prepared statement parameter. + +### Annotated Mapper for General Insert Statements +The GeneralInsertStatementProvider object can be used as a parameter to a MyBatis mapper method directly. If you +are using an annotated mapper, the insert method should look like this: + +```java +import org.apache.ibatis.annotations.InsertProvider; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; +import org.mybatis.dynamic.sql.util.SqlProviderAdapter; + +... + @InsertProvider(type=SqlProviderAdapter.class, method="generalInsert") + int generalInsert(GeneralInsertStatementProvider insertStatement); +... + +``` + +### XML Mapper for General Insert Statements +We do not recommend using an XML mapper for insert statements, but if you want to do so the GeneralInsertStatementProvider object can be used as a parameter to a MyBatis mapper method directly. + +If you are using an XML mapper, the insert method should look like this in the Java interface: + +```java +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; + +... + int generalInsert(GeneralInsertStatementProvider insertStatement); +... + +``` + +The XML element should look like this: + +```xml + + ${insertStatement} + +``` + + ## Insert with Select An insert select is an SQL insert statement the inserts the results of a select. For example: diff --git a/src/site/markdown/docs/kotlinMyBatis3.md b/src/site/markdown/docs/kotlinMyBatis3.md index f546d3d23..b77b9ffb8 100644 --- a/src/site/markdown/docs/kotlinMyBatis3.md +++ b/src/site/markdown/docs/kotlinMyBatis3.md @@ -152,7 +152,7 @@ val rows = mapper.delete { allRows() } Insert method support enables the removal of some of the boilerplate code from insert methods in a mapper interfaces. -To use this support, we envision creating several methods - two standard mapper methods, and other extension methods. The standard mapper methods are standard MyBatis Dynamic SQL methods that will execute a delete: +To use this support, we envision creating several methods - both standard mapper methods, and other extension methods. The standard mapper methods are standard MyBatis Dynamic SQL methods that will execute an insert: ```kotlin @Mapper @@ -160,6 +160,9 @@ interface PersonMapper { @InsertProvider(type = SqlProviderAdapter::class, method = "insert") fun insert(insertStatement: InsertStatementProvider): Int + @InsertProvider(type = SqlProviderAdapter::class, method = "generalInsert") + fun generalInsert(insertStatement: GeneralInsertStatementProvider): Int + @InsertProvider(type = SqlProviderAdapter::class, method = "insertMultiple") fun insertMultiple(insertStatement: MultiRowInsertStatementProvider): Int } @@ -179,6 +182,9 @@ fun PersonMapper.insert(record: PersonRecord) = map(addressId).toProperty("addressId") } +fun PersonMapper.insert(completer: GeneralInsertCompleter) = + insertInto(this::generalInsert, Person, completer) + fun PersonMapper.insertMultiple(vararg records: PersonRecord) = insertMultiple(records.toList()) @@ -205,7 +211,7 @@ fun PersonMapper.insertSelective(record: PersonRecord) = } ``` -Note these methods use Kotlin utility methods named `insert` and `insertMultiple`. Both methods accept a function with a receiver that will allow column mappings. The methods will build and execute insert statements.= with the supplied column mappings. +Note these methods use Kotlin utility methods named `insert`, `insertInto`, and `insertMultiple`. Those methods accept a function with a receiver that will allow column mappings. The methods will build and execute insert statements with the supplied column mappings. Clients use these methods as follows: @@ -214,6 +220,17 @@ Clients use these methods as follows: val record = PersonRecord(100, "Joe", LastName("Jones"), Date(), true, "Developer", 1) val rows = mapper.insert(record) +// general insert... +val rows = mapper.insert { + set(id).toValue(100) + set(firstName).toValue("Joe") + set(lastName).toValue(LastName("Jones")) + set(employed).toValue(true) + set(occupation).toValue("Developer") + set(addressId).toValue(1) + set(birthDate).toValue(Date()) +} + // multiple insert... val record1 = PersonRecord(100, "Joe", LastName("Jones"), Date(), true, "Developer", 1) val record2 = PersonRecord(101, "Sarah", LastName("Smith"), Date(), true, "Architect", 2) diff --git a/src/site/markdown/docs/kotlinSpring.md b/src/site/markdown/docs/kotlinSpring.md index 25b6567fa..c6e591f50 100644 --- a/src/site/markdown/docs/kotlinSpring.md +++ b/src/site/markdown/docs/kotlinSpring.md @@ -101,9 +101,9 @@ There is also an extention method that can be used to count all rows in a table: } ``` -## Insert Method Support +## Insert Record Method Support -Insert method support enables the creation of arbitrary insert statements. +Insert method support enables the creation of arbitrary insert statements given a class that matches a database row. If you do not with to create such a class, then see the general insert support following this section. The DSL for insert methods looks like this: @@ -128,7 +128,7 @@ This code creates an `InsertStatementProvider` that can be executed with an exte val rows = template.insert(insertStatement) // rows is an Int ``` -This is the two step execution process. This can be combined into a single step with code like the following: +This is the two step execution process. These steps can be combined into a single step with code like the following: ```kotlin val record = PersonRecord(100, "Joe", "Jones", Date(), "Yes", "Developer", 1) @@ -146,6 +146,49 @@ This is the two step execution process. This can be combined into a single step Note the use of the `toPropertyWhenPresent` mapping - this will only set the insert value if the value of the property is non null. Also note that you can use the mapping methods to map insert fields to nulls and constants if desired. +## General Insert Method Support + +General insert method support enables the creation of arbitrary insert statements and does not require the creation of a class matching the database row. + +The DSL for general insert methods looks like this: + +```kotlin + val insertStatement = insertInto(Person) { // insertStatement is a GeneralInsertStatementProvider + set(id).toValue(100) + set(firstName).toValue("Joe") + set(lastName).toValue("Jones") + set(birthDate).toValue(Date()) + set(employed).toValue("Yes") + set(occupation).toValue("Developer") + set(addressId).toValue(1) + } +``` + +This code creates a `GeneralInsertStatementProvider` that can be executed with an extension method for `NamedParameterJdbcTemplate` like this: + +```kotlin + val template: NamedParameterJdbcTemplate = getTemplate() // not shown + val rows = template.insert(insertStatement) // rows is an Int +``` + +This is the two step execution process. These steps can be combined into a single step with code like the following: + +```kotlin + val myOccupation = "Developer" + + val rows = template.insertInto(Person) { + set(id).toValue(100) + set(firstName).toValue("Joe") + set(lastName).toValue("Jones") + set(birthDate).toValue(Date()) + set(employed).toValue("Yes") + set(occupation).toValueWhenPresent(myOccupation) + set(addressId).toValue(1) + } +``` + +Note the use of the `toValueWhenPresent` mapping - this will only set the insert value if the value of the property is non null. Also note that you can use the mapping methods to map insert fields to nulls and constants if desired. + ## Select Method Support Select method support enables the creation of methods that execute a query allowing a user to specify a where clause and/or an order by clause and/or pagination clauses at runtime, but abstracting away all other details. diff --git a/src/site/markdown/docs/mybatis3.md b/src/site/markdown/docs/mybatis3.md index bed33e2bc..63590a54a 100644 --- a/src/site/markdown/docs/mybatis3.md +++ b/src/site/markdown/docs/mybatis3.md @@ -73,21 +73,28 @@ int rows = mapper.delete(DeleteDSLCompleter.allRows()); The goal of insert method support is to remove some of the boilerplate code from insert methods in a mapper interfaces. -To use this support, we envision creating several methods on a MyBatis mapper interface. The first two methods are the standard MyBatis Dynamic SQL method that will execute an insert: +To use this support, we envision creating several methods on a MyBatis mapper interface. The first methods are the standard MyBatis methods that will execute an insert: ```java @InsertProvider(type=SqlProviderAdapter.class, method="insert") int insert(InsertStatementProvider insertStatement); +@InsertProvider(type=SqlProviderAdapter.class, method="generalInsert") +int generalInsert(GeneralInsertStatementProvider insertStatement); + @InsertProvider(type=SqlProviderAdapter.class, method="insertMultiple") int insertMultiple(MultiRowInsertStatementProvider insertStatement); ``` -These two methods are standard methods for MyBatis Dynamic SQL. They execute a single row insert and a multiple row insert. +These methods are standard methods for MyBatis Dynamic SQL. They execute a single row insert, a general insert, and a multiple row insert. These methods can be used to implement simplified insert methods: ```java +default int insert(UnaryOperator completer) { + return MyBatis3Utils.insert(this::generalInsert, person, completer); +} + default int insert(PersonRecord record) { return MyBatis3Utils.insert(this::insert, record, person, c -> c.map(id).toProperty("id") @@ -117,7 +124,7 @@ default int insertMultiple(Collection records) { } ``` -In the mapper, only the column mappings need to be specified and no other boilerplate code is needed. +The first insert method is a general insert and can be used to create arbitrary inserts with different combinations of columns specified. The other methods have the insert statements mapped to a POJO "record" class that holds values for the insert statement. ## Select Method Support diff --git a/src/site/markdown/docs/spring.md b/src/site/markdown/docs/spring.md index 45e3a98c8..721608f4d 100644 --- a/src/site/markdown/docs/spring.md +++ b/src/site/markdown/docs/spring.md @@ -23,7 +23,7 @@ MyBatis3 is a higher level abstraction over JDBC than Spring JDBC templates. Whi The Spring Named Parameter JDBC template expects an SQL statement with parameter markers in the Spring format, and a set of matched parameters. MyBatis Dynamic SQL will generate both. The parameters returned from the generated SQL statement can be wrapped in a Spring `MapSqlParameterSource`. Spring also expects you to provide a row mapper for creating the returned objects. The following code shows a complete example: ```java - NamedParameterJdbcTemplate template = getTemplate(); + NamedParameterJdbcTemplate template = getTemplate(); // not shown SelectStatementProvider selectStatement = select(id, firstName, lastName, fullName) .from(generatedAlways) @@ -47,11 +47,46 @@ The Spring Named Parameter JDBC template expects an SQL statement with parameter }); ``` -## Executing Insert Statements -Insert statements are a bit different - MyBatis Dynamic SQL generates a properly formatted SQL string for Spring, but instead of a map of parameters, the parameter mappings are created for the inserted record itself. So the parameters for the Spring template are created by a `BeanPropertySqlParameterSource`. Generated keys in Spring are supported with a `GeneratedKeyHolder`. The following is a complete example: +## Executing General Insert Statements +General insert statements do not require a POJO object matching a table row. Following is a complete example: ```java - NamedParameterJdbcTemplate template = getTemplate(); + NamedParameterJdbcTemplate template = getTemplate(); // not shown + + GeneralInsertStatementProvider insertStatement = insertInto(generatedAlways) + .set(id).toValue(100) + .set(firstName).toValue("Bob") + .set(lastName).toValue("Jones") + .build() + .render(RenderingStrategies.SPRING_NAMED_PARAMETER); + + int rows = template.update(insertStatement.getInsertStatement(), insertStatement.getParameters()); +``` + +If you want to retrieve generated keys for a general insert statement the steps are similar except that you must wrap the parameters in a `MapSqlParameterSource` object and use a `GeneratedKeyHolder`. Following is a complete example of this usage: + +```java + NamedParameterJdbcTemplate template = getTemplate(); // not shown + + GeneralInsertStatementProvider insertStatement = insertInto(generatedAlways) + .set(id).toValue(100) + .set(firstName).toValue("Bob") + .set(lastName).toValue("Jones") + .build() + .render(RenderingStrategies.SPRING_NAMED_PARAMETER); + + MapSqlParameterSource parameterSource = new MapSqlParameterSource(insertStatement.getParameters()); + KeyHolder keyHolder = new GeneratedKeyHolder(); + + int rows = template.update(insertStatement.getInsertStatement(), parameterSource, keyHolder); + String generatedKey = (String) keyHolder.getKeys().get("FULL_NAME"); +``` + +## Executing Insert Record Statements +Insert record statements are a bit different - MyBatis Dynamic SQL generates a properly formatted SQL string for Spring, but instead of a map of parameters, the parameter mappings are created for the inserted record itself. So the parameters for the Spring template are created by a `BeanPropertySqlParameterSource`. Generated keys in Spring are supported with a `GeneratedKeyHolder`. The following is a complete example: + +```java + NamedParameterJdbcTemplate template = getTemplate(); // not shown GeneratedAlwaysRecord record = new GeneratedAlwaysRecord(); record.setId(100); @@ -77,7 +112,7 @@ Insert statements are a bit different - MyBatis Dynamic SQL generates a properly Batch insert support in Spring is a bit different than batch support in MyBatis3 and Spring does not support returning generated keys from a batch insert. The following is a complete example of a batch insert (note the use of `SqlParameterSourceUtils` to create an array of parameter sources from an array of input records): ```java - NamedParameterJdbcTemplate template = getTemplate(); + NamedParameterJdbcTemplate template = getTemplate(); // not shown List records = new ArrayList<>(); GeneratedAlwaysRecord record = new GeneratedAlwaysRecord(); @@ -109,11 +144,11 @@ Batch insert support in Spring is a bit different than batch support in MyBatis3 Updates and deletes use the `MapSqlParameterSource` as with select statements, but use the `update` method in the template. For example: ```java - NamedParameterJdbcTemplate template = getTemplate(); + NamedParameterJdbcTemplate template = getTemplate(); // not shown UpdateStatementProvider updateStatement = update(generatedAlways) .set(firstName).equalToStringConstant("Rob") - .where(id, isIn(1, 5, 22)) + .where(id, isIn(1, 5, 22)) .build() .render(RenderingStrategies.SPRING_NAMED_PARAMETER); diff --git a/src/test/java/examples/animal/data/AnimalDataMapper.java b/src/test/java/examples/animal/data/AnimalDataMapper.java index 25c9952d2..c452bb6f6 100644 --- a/src/test/java/examples/animal/data/AnimalDataMapper.java +++ b/src/test/java/examples/animal/data/AnimalDataMapper.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 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. @@ -28,6 +28,7 @@ import org.apache.ibatis.annotations.SelectProvider; import org.apache.ibatis.annotations.UpdateProvider; import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertSelectStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; @@ -68,6 +69,9 @@ public interface AnimalDataMapper { @InsertProvider(type=SqlProviderAdapter.class, method="insert") int insert(InsertStatementProvider insertStatement); + @InsertProvider(type=SqlProviderAdapter.class, method="generalInsert") + int generalInsert(GeneralInsertStatementProvider insertStatement); + @InsertProvider(type=SqlProviderAdapter.class, method="insertSelect") int insertSelect(InsertSelectStatementProvider insertSelectStatement); diff --git a/src/test/java/examples/animal/data/AnimalDataTest.java b/src/test/java/examples/animal/data/AnimalDataTest.java index 42a81c2b5..06797371d 100644 --- a/src/test/java/examples/animal/data/AnimalDataTest.java +++ b/src/test/java/examples/animal/data/AnimalDataTest.java @@ -44,6 +44,7 @@ import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; import org.mybatis.dynamic.sql.insert.render.BatchInsert; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertSelectStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategies; @@ -1735,6 +1736,32 @@ void testInsertSelectWithoutColumnList() { } } + @Test + void testGeneralInsert() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + + GeneralInsertStatementProvider insertStatement = insertInto(animalData) + .set(id).toValue(101) + .set(animalName).toStringConstant("Fred") + .set(brainWeight).toConstant("2.2") + .set(bodyWeight).toValue(4.5) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expected = "insert into AnimalData (id, animal_name, brain_weight, body_weight) " + + "values (#{parameters.p1,jdbcType=INTEGER}, 'Fred', 2.2, #{parameters.p2,jdbcType=DOUBLE})"; + + assertThat(insertStatement.getInsertStatement()).isEqualTo(expected); + assertThat(insertStatement.getParameters()).hasSize(2); + assertThat(insertStatement.getParameters()).containsEntry("p1", 101); + assertThat(insertStatement.getParameters()).containsEntry("p2", 4.5); + + int rows = mapper.generalInsert(insertStatement); + assertThat(rows).isEqualTo(1); + } + } + @Test void testUpdateWithSelect() { try (SqlSession sqlSession = sqlSessionFactory.openSession()) { diff --git a/src/test/java/examples/generated/always/spring/SpringTest.java b/src/test/java/examples/generated/always/spring/SpringTest.java index 4a91c840e..ce3c672f7 100644 --- a/src/test/java/examples/generated/always/spring/SpringTest.java +++ b/src/test/java/examples/generated/always/spring/SpringTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; import org.mybatis.dynamic.sql.insert.render.BatchInsert; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; @@ -142,9 +143,41 @@ void testInsert() { assertThat(rows).isEqualTo(1); assertThat(generatedKey).isEqualTo("Bob Jones"); - } - + + @Test + void testGeneralInsert() { + GeneralInsertStatementProvider insertStatement = insertInto(generatedAlways) + .set(id).toValue(100) + .set(firstName).toValue("Bob") + .set(lastName).toValue("Jones") + .build() + .render(RenderingStrategies.SPRING_NAMED_PARAMETER); + + int rows = template.update(insertStatement.getInsertStatement(), insertStatement.getParameters()); + + assertThat(rows).isEqualTo(1); + } + + @Test + void testGeneralInsertWithGeneratedKey() { + GeneralInsertStatementProvider insertStatement = insertInto(generatedAlways) + .set(id).toValue(100) + .set(firstName).toValue("Bob") + .set(lastName).toValue("Jones") + .build() + .render(RenderingStrategies.SPRING_NAMED_PARAMETER); + + MapSqlParameterSource parameterSource = new MapSqlParameterSource(insertStatement.getParameters()); + KeyHolder keyHolder = new GeneratedKeyHolder(); + + int rows = template.update(insertStatement.getInsertStatement(), parameterSource, keyHolder); + String generatedKey = (String) keyHolder.getKeys().get("FULL_NAME"); + + assertThat(rows).isEqualTo(1); + assertThat(generatedKey).isEqualTo("Bob Jones"); + } + @Test void testInsertBatch() { List records = new ArrayList<>(); diff --git a/src/test/java/examples/simple/PersonMapper.java b/src/test/java/examples/simple/PersonMapper.java index 37c1f09c5..35b547b60 100644 --- a/src/test/java/examples/simple/PersonMapper.java +++ b/src/test/java/examples/simple/PersonMapper.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.function.UnaryOperator; import org.apache.ibatis.annotations.DeleteProvider; import org.apache.ibatis.annotations.InsertProvider; @@ -35,6 +36,8 @@ import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.delete.DeleteDSLCompleter; import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; +import org.mybatis.dynamic.sql.insert.GeneralInsertDSL; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider; import org.mybatis.dynamic.sql.select.CountDSLCompleter; @@ -62,6 +65,9 @@ public interface PersonMapper { @DeleteProvider(type=SqlProviderAdapter.class, method="delete") int delete(DeleteStatementProvider deleteStatement); + @InsertProvider(type=SqlProviderAdapter.class, method="generalInsert") + int generalInsert(GeneralInsertStatementProvider insertStatement); + @InsertProvider(type=SqlProviderAdapter.class, method="insert") int insert(InsertStatementProvider insertStatement); @@ -104,6 +110,10 @@ default int deleteByPrimaryKey(Integer id_) { ); } + default int insert(UnaryOperator completer) { + return MyBatis3Utils.insert(this::generalInsert, person, completer); + } + default int insert(PersonRecord record) { return MyBatis3Utils.insert(this::insert, record, person, c -> c.map(id).toProperty("id") diff --git a/src/test/java/examples/simple/PersonMapperTest.java b/src/test/java/examples/simple/PersonMapperTest.java index 5885b6c67..d1cfe4f04 100644 --- a/src/test/java/examples/simple/PersonMapperTest.java +++ b/src/test/java/examples/simple/PersonMapperTest.java @@ -212,6 +212,24 @@ void testInsert() { } } + @Test + void testGeneralInsert() { + try (SqlSession session = sqlSessionFactory.openSession()) { + PersonMapper mapper = session.getMapper(PersonMapper.class); + int rows = mapper.insert(c -> + c.set(id).toValue(100) + .set(firstName).toValue("Joe") + .set(lastName).toValue(LastName.of("Jones")) + .set(birthDate).toValue(new Date()) + .set(employed).toValue(true) + .set(occupation).toValue("Developer") + .set(addressId).toValue(1) + ); + + assertThat(rows).isEqualTo(1); + } + } + @Test void testInsertMultiple() { try (SqlSession session = sqlSessionFactory.openSession()) { diff --git a/src/test/java/org/mybatis/dynamic/sql/insert/GeneralInsertStatementTest.java b/src/test/java/org/mybatis/dynamic/sql/insert/GeneralInsertStatementTest.java new file mode 100644 index 000000000..c0cfd2a6a --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/insert/GeneralInsertStatementTest.java @@ -0,0 +1,105 @@ +/** + * Copyright 2016-2020 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 + * + * http://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.insert; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mybatis.dynamic.sql.SqlBuilder.insertInto; + +import java.sql.JDBCType; + +import org.junit.jupiter.api.Test; +import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; +import org.mybatis.dynamic.sql.render.RenderingStrategies; + +class GeneralInsertStatementTest { + + private static final SqlTable foo = SqlTable.of("foo"); + private static final SqlColumn id = foo.column("id", JDBCType.INTEGER); + private static final SqlColumn firstName = foo.column("first_name", JDBCType.VARCHAR); + private static final SqlColumn lastName = foo.column("last_name", JDBCType.VARCHAR); + private static final SqlColumn occupation = foo.column("occupation", JDBCType.VARCHAR); + + @Test + void testFullInsertStatementBuilder() { + + GeneralInsertStatementProvider insertStatement = insertInto(foo) + .set(id).toValue(2) + .set(firstName).toValue("Jones") + .set(occupation).toValue("dino driver") + .build() + .render(RenderingStrategies.MYBATIS3); + + String expectedStatement = "insert into foo " + + "(id, first_name, occupation) " + + "values (#{parameters.p1,jdbcType=INTEGER}, #{parameters.p2,jdbcType=VARCHAR}, #{parameters.p3,jdbcType=VARCHAR})"; + + assertThat(insertStatement.getInsertStatement()).isEqualTo(expectedStatement); + } + + @Test + void testInsertStatementBuilderWithNulls() { + + GeneralInsertStatementProvider insertStatement = insertInto(foo) + .set(id).toValue(1) + .set(firstName).toValue("Fred") + .set(lastName).toValue("Smith") + .set(occupation).toNull() + .build() + .render(RenderingStrategies.MYBATIS3); + + String expected = "insert into foo (id, first_name, last_name, occupation) " + + "values (#{parameters.p1,jdbcType=INTEGER}, #{parameters.p2,jdbcType=VARCHAR}, #{parameters.p3,jdbcType=VARCHAR}, null)"; + assertThat(insertStatement.getInsertStatement()).isEqualTo(expected); + } + + @Test + void testInsertStatementBuilderWithConstants() { + + GeneralInsertStatementProvider insertStatement = insertInto(foo) + .set(id).toConstant("3") + .set(firstName).toValue("Fred") + .set(lastName).toValue("Jones") + .set(occupation).toStringConstant("Y") + .build() + .render(RenderingStrategies.MYBATIS3); + + String expected = "insert into foo (id, first_name, last_name, occupation) " + + "values (3, #{parameters.p1,jdbcType=VARCHAR}, #{parameters.p2,jdbcType=VARCHAR}, 'Y')"; + assertThat(insertStatement.getInsertStatement()).isEqualTo(expected); + } + + @Test + void testSelectiveInsertStatementBuilder() { + Integer myId = null; + String myFirstName = null; + String myLastName = "jones"; + String myOccupation = "dino driver"; + + GeneralInsertStatementProvider insertStatement = insertInto(foo) + .set(id).toValueWhenPresent(() -> myId) + .set(firstName).toValueWhenPresent(myFirstName) + .set(lastName).toValueWhenPresent(() -> myLastName) + .set(occupation).toValueWhenPresent(myOccupation) + .build() + .render(RenderingStrategies.MYBATIS3); + + String expected = "insert into foo (last_name, occupation) " + + "values (#{parameters.p1,jdbcType=VARCHAR}, #{parameters.p2,jdbcType=VARCHAR})"; + assertThat(insertStatement.getInsertStatement()).isEqualTo(expected); + } +} diff --git a/src/test/java/org/mybatis/dynamic/sql/insert/InsertStatementTest.java b/src/test/java/org/mybatis/dynamic/sql/insert/InsertStatementTest.java index 3a0ca0f77..e657a70dd 100644 --- a/src/test/java/org/mybatis/dynamic/sql/insert/InsertStatementTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/insert/InsertStatementTest.java @@ -28,6 +28,8 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; import org.mybatis.dynamic.sql.insert.render.FieldAndValue; +import org.mybatis.dynamic.sql.insert.render.FieldAndValueAndParameters; +import org.mybatis.dynamic.sql.insert.render.FieldAndValueAndParametersCollector; import org.mybatis.dynamic.sql.insert.render.FieldAndValueCollector; import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategies; @@ -152,6 +154,40 @@ private FieldAndValue newFieldAndValue(String fieldName, String valuePhrase) { .build(); } + @Test + void testParallelStreamWithParameters() { + + List mappings = new ArrayList<>(); + + mappings.add(newFieldAndValueAndParameter(id.name(), "{p1}", "p1", 1)); + mappings.add(newFieldAndValueAndParameter(firstName.name(), "{p2}", "p2", "Fred")); + mappings.add(newFieldAndValueAndParameter(lastName.name(), "{p3}", "p3", "Flintstone")); + mappings.add(newFieldAndValueAndParameter(occupation.name(), "{p4}", "p4", "Driver")); + + FieldAndValueAndParametersCollector collector = + mappings.parallelStream().collect(Collector.of( + FieldAndValueAndParametersCollector::new, + FieldAndValueAndParametersCollector::add, + FieldAndValueAndParametersCollector::merge)); + + String expectedColumnsPhrase = "(id, first_name, last_name, occupation)"; + String expectedValuesPhrase = "values ({p1}, {p2}, {p3}, {p4})"; + + assertAll( + () -> assertThat(collector.columnsPhrase()).isEqualTo(expectedColumnsPhrase), + () -> assertThat(collector.valuesPhrase()).isEqualTo(expectedValuesPhrase), + () -> assertThat(collector.parameters()).hasSize(4) + ); + } + + private FieldAndValueAndParameters newFieldAndValueAndParameter(String fieldName, String valuePhrase, String parameterName, + Object parameterValue) { + return FieldAndValueAndParameters.withFieldName(fieldName) + .withValuePhrase(valuePhrase) + .withParameter(parameterName, parameterValue) + .build(); + } + @Test void testParallelStreamForMultiRecord() { diff --git a/src/test/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitorTest.java b/src/test/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitorTest.java new file mode 100644 index 000000000..bc367f96b --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitorTest.java @@ -0,0 +1,178 @@ +/** + * Copyright 2016-2020 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 + * + * http://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.util; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import org.junit.jupiter.api.Test; +import org.mybatis.dynamic.sql.SqlBuilder; +import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.SqlTable; + +class ColumnMappingVisitorTest { + + @Test + void testThatGeneralInsertVisitorErrorsForColumnToColumnMapping() { + TestTable table = new TestTable(); + GeneralInsertVisitor tv = new GeneralInsertVisitor(); + ColumnToColumnMapping mapping = ColumnToColumnMapping.of(table.id, table.description); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)); + } + + @Test + void testThatGeneralInsertVisitorErrorsForSelectMapping() { + TestTable table = new TestTable(); + GeneralInsertVisitor tv = new GeneralInsertVisitor(); + SelectMapping mapping = SelectMapping.of(table.id, SqlBuilder.select(table.id).from(table)); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)); + } + + @Test + void testThatGeneralInsertVisitorErrorsForPropertyMapping() { + TestTable table = new TestTable(); + GeneralInsertVisitor tv = new GeneralInsertVisitor(); + PropertyMapping mapping = PropertyMapping.of(table.id, "id"); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)); + } + + @Test + void testThatInsertVisitorErrorsForColumnToColumnMapping() { + TestTable table = new TestTable(); + InsertVisitor tv = new InsertVisitor(); + ColumnToColumnMapping mapping = ColumnToColumnMapping.of(table.id, table.description); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)); + } + + @Test + void testThatInsertVisitorErrorsForSelectMapping() { + TestTable table = new TestTable(); + InsertVisitor tv = new InsertVisitor(); + SelectMapping mapping = SelectMapping.of(table.id, SqlBuilder.select(table.id).from(table)); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)); + } + + @Test + void testThatInsertVisitorErrorsForValueMapping() { + TestTable table = new TestTable(); + InsertVisitor tv = new InsertVisitor(); + ValueMapping mapping = ValueMapping.of(table.id, () -> 3); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)); + } + + @Test + void testThatUpdateVisitorErrorsForPropertyMapping() { + TestTable table = new TestTable(); + UpdateVisitor tv = new UpdateVisitor(); + PropertyMapping mapping = PropertyMapping.of(table.id, "id"); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)); + } + + private static class TestTable extends SqlTable { + public SqlColumn id; + public SqlColumn description; + + public TestTable() { + super("Test"); + + id = column("id"); + description = column("description"); + } + } + + private static class GeneralInsertVisitor implements GeneralInsertMappingVisitor { + @Override + public String visit(NullMapping mapping) { + return "Null Mapping"; + } + + @Override + public String visit(ConstantMapping mapping) { + return "Constant Mapping"; + } + + @Override + public String visit(StringConstantMapping mapping) { + return "String Constant Mapping"; + } + + @Override + public String visit(ValueMapping mapping) { + return "Value Mapping"; + } + } + + private static class InsertVisitor implements InsertMappingVisitor { + @Override + public String visit(NullMapping mapping) { + return "Null Mapping"; + } + + @Override + public String visit(ConstantMapping mapping) { + return "Constant Mapping"; + } + + @Override + public String visit(StringConstantMapping mapping) { + return "String Constant Mapping"; + } + + @Override + public String visit(PropertyMapping mapping) { + return "Property Mapping"; + } + } + + private static class UpdateVisitor implements UpdateMappingVisitor { + @Override + public String visit(NullMapping mapping) { + return "Null Mapping"; + } + + @Override + public String visit(ConstantMapping mapping) { + return "Constant Mapping"; + } + + @Override + public String visit(StringConstantMapping mapping) { + return "String Constant Mapping"; + } + + @Override + public String visit(ValueMapping mapping) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String visit(SelectMapping mapping) { + return "Select Mapping"; + } + + @Override + public String visit(ColumnToColumnMapping columnMapping) { + return "Column to Column Mapping"; + } + } +} diff --git a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapper.kt b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapper.kt index 8876eaec4..d9c696620 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapper.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapper.kt @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -18,6 +18,7 @@ package examples.kotlin.mybatis3.canonical import org.apache.ibatis.annotations.* import org.apache.ibatis.type.JdbcType import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider +import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider import org.mybatis.dynamic.sql.insert.render.MultiRowInsertStatementProvider import org.mybatis.dynamic.sql.select.render.SelectStatementProvider @@ -42,6 +43,9 @@ interface PersonMapper { @InsertProvider(type = SqlProviderAdapter::class, method = "insert") fun insert(insertStatement: InsertStatementProvider): Int + @InsertProvider(type = SqlProviderAdapter::class, method = "generalInsert") + fun generalInsert(insertStatement: GeneralInsertStatementProvider): Int + @InsertProvider(type = SqlProviderAdapter::class, method = "insertMultiple") fun insertMultiple(insertStatement: MultiRowInsertStatementProvider): Int diff --git a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperExtensions.kt b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperExtensions.kt index 5c7926212..acbbe86d2 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperExtensions.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperExtensions.kt @@ -1,5 +1,5 @@ /** - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2020 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. @@ -49,6 +49,9 @@ fun PersonMapper.insert(record: PersonRecord) = map(addressId).toProperty("addressId") } +fun PersonMapper.insert(completer: GeneralInsertCompleter) = + insertInto(this::generalInsert, Person, completer) + fun PersonMapper.insertMultiple(vararg records: PersonRecord) = insertMultiple(records.toList()) diff --git a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt index 85b509fbc..c592d79d1 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperTest.kt @@ -15,6 +15,8 @@ */ package examples.kotlin.mybatis3.canonical +import examples.kotlin.mybatis3.canonical.PersonDynamicSqlSupport.Person.addressId +import examples.kotlin.mybatis3.canonical.PersonDynamicSqlSupport.Person.birthDate import examples.kotlin.mybatis3.canonical.PersonDynamicSqlSupport.Person.employed import examples.kotlin.mybatis3.canonical.PersonDynamicSqlSupport.Person.firstName import examples.kotlin.mybatis3.canonical.PersonDynamicSqlSupport.Person.id @@ -197,6 +199,25 @@ class PersonMapperTest { } } + @Test + fun testGeneralInsert() { + newSession().use { session -> + val mapper = session.getMapper(PersonMapper::class.java) + + val rows = mapper.insert { + set(id).toValue(100) + set(firstName).toValue("Joe") + set(lastName).toValue(LastName("Jones")) + set(employed).toValue(true) + set(occupation).toValue("Developer") + set(addressId).toValue(1) + set(birthDate).toValue(Date()) + } + + assertThat(rows).isEqualTo(1) + } + } + @Test fun testInsertMultiple() { newSession().use { session -> diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTemplateDirectTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTemplateDirectTest.kt index 6f50651c4..529c5f829 100644 --- a/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTemplateDirectTest.kt +++ b/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTemplateDirectTest.kt @@ -156,6 +156,21 @@ class CanonicalSpringKotlinTemplateDirectTest { assertThat(rows).isEqualTo(1) } + @Test + fun testGeneralInsert() { + val rows = template.insertInto(Person) { + set(id).toValue(100) + set(firstName).toValue("Joe") + set(lastName).toValue("Jones") + set(birthDate).toValue(Date()) + set(employed).toValue("Yes") + set(occupation).toValue("Developer") + set(addressId).toValue(1) + } + + assertThat(rows).isEqualTo(1) + } + @Test fun testSelectAll() { val rows = template.select(id, firstName, lastName, birthDate, employed, occupation, addressId) diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTest.kt index fba9d55b8..d410c5182 100644 --- a/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTest.kt +++ b/src/test/kotlin/examples/kotlin/spring/canonical/CanonicalSpringKotlinTest.kt @@ -211,6 +211,52 @@ class CanonicalSpringKotlinTest { assertThat(rows).isEqualTo(1) } + @Test + fun testGeneralInsert() { + + val insertStatement = insertInto(Person) { + set(id).toValue(100) + set(firstName).toValue("Joe") + set(lastName).toValue("Jones") + set(birthDate).toValue(Date()) + set(employed).toValue("Yes") + set(occupation).toValue("Developer") + set(addressId).toValue(1) + } + + val expected = "insert into Person (id, first_name, last_name, birth_date, employed, occupation, address_id)" + + " values (:p1, :p2, :p3, :p4, :p5, :p6, :p7)" + + assertThat(insertStatement.insertStatement).isEqualTo(expected) + + val rows = template.insert(insertStatement) + val record = template.selectOne(id, firstName, lastName, birthDate, employed, occupation, addressId) + .from(Person) { + where(id, isEqualTo(100)) + }.withRowMapper { rs, _ -> + val record = PersonRecord() + record.id = rs.getInt(1) + record.firstName = rs.getString(2) + record.lastName = rs.getString(3) + record.birthDate = rs.getTimestamp(4) + record.employed = rs.getString(5) + record.occupation = rs.getString(6) + record.addressId = rs.getInt(7) + record + } + + assertThat(rows).isEqualTo(1) + with(record!!) { + assertThat(id).isEqualTo(100) + assertThat(firstName).isEqualTo("Joe") + assertThat(lastName).isEqualTo("Jones") + assertThat(birthDate).isNotNull() + assertThat(employed).isEqualTo("Yes") + assertThat(occupation).isEqualTo("Developer") + assertThat(addressId).isEqualTo(1) + } + } + @Test fun testRawSelect() { val selectStatement = select(id.`as`("A_ID"), firstName, lastName, birthDate, employed, occupation,