From af417bd0dceca7a8ac8ba1f4e777474bb150ef5a Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Wed, 7 Feb 2018 16:13:18 -0500 Subject: [PATCH] Add ability to use a function in an update statement This also removes the incrementBy, decrementBy, etc. operations as they are replaced by more flexible alternatives. --- .../sql/{select/function => }/Constant.java | 19 ++- .../org/mybatis/dynamic/sql/SqlBuilder.java | 9 +- .../mybatis/dynamic/sql/StringConstant.java | 52 ++++++++ .../mybatis/dynamic/sql/update/UpdateDSL.java | 45 +------ .../sql/update/render/SetPhraseVisitor.java | 26 ++-- .../sql/util/ArithmeticConstantMapping.java | 50 ------- ...meticOperation.java => ColumnMapping.java} | 29 ++-- .../sql/util/UpdateMappingVisitor.java | 4 +- src/site/markdown/docs/update.md | 1 + .../animal/data/AnimalDataMapper.java | 6 +- .../examples/animal/data/AnimalDataTest.java | 89 ++++++++++++- .../examples/animal/data/BindingTest.java | 125 ++++++++++++++++++ .../sql/update/UpdateStatementTest.java | 16 +-- 13 files changed, 326 insertions(+), 145 deletions(-) rename src/main/java/org/mybatis/dynamic/sql/{select/function => }/Constant.java (73%) create mode 100644 src/main/java/org/mybatis/dynamic/sql/StringConstant.java delete mode 100644 src/main/java/org/mybatis/dynamic/sql/util/ArithmeticConstantMapping.java rename src/main/java/org/mybatis/dynamic/sql/util/{ArithmeticOperation.java => ColumnMapping.java} (51%) create mode 100644 src/test/java/examples/animal/data/BindingTest.java diff --git a/src/main/java/org/mybatis/dynamic/sql/select/function/Constant.java b/src/main/java/org/mybatis/dynamic/sql/Constant.java similarity index 73% rename from src/main/java/org/mybatis/dynamic/sql/select/function/Constant.java rename to src/main/java/org/mybatis/dynamic/sql/Constant.java index f67f1d957..e6a56bf54 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/function/Constant.java +++ b/src/main/java/org/mybatis/dynamic/sql/Constant.java @@ -13,20 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mybatis.dynamic.sql.select.function; +package org.mybatis.dynamic.sql; import java.util.Objects; import java.util.Optional; -import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.render.TableAliasCalculator; -public class Constant implements BasicColumn { +public class Constant implements BasicColumn { private String alias; - private T value; + private String value; - private Constant(T value) { + private Constant(String value) { this.value = Objects.requireNonNull(value); } @@ -37,17 +36,17 @@ public Optional alias() { @Override public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) { - return value.toString(); + return value; } @Override - public Constant as(String alias) { - Constant copy = new Constant<>(value); + public Constant as(String alias) { + Constant copy = new Constant(value); copy.alias = alias; return copy; } - public static Constant of(T value) { - return new Constant<>(value); + public static Constant of(String value) { + return new Constant(value); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java index 42d4adfee..4208a54f6 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java @@ -35,7 +35,6 @@ import org.mybatis.dynamic.sql.select.aggregate.Min; import org.mybatis.dynamic.sql.select.aggregate.Sum; import org.mybatis.dynamic.sql.select.function.Add; -import org.mybatis.dynamic.sql.select.function.Constant; import org.mybatis.dynamic.sql.select.function.Divide; import org.mybatis.dynamic.sql.select.function.Lower; import org.mybatis.dynamic.sql.select.function.Multiply; @@ -192,12 +191,12 @@ static Sum sum(BasicColumn column) { } // constants - static Constant constant(T number) { - return Constant.of(number); + static Constant constant(String constant) { + return Constant.of(constant); } - static Constant constant(String string) { - return Constant.of("'" + string + "'"); //$NON-NLS-1$ //$NON-NLS-2$ + static StringConstant stringConstant(String constant) { + return StringConstant.of(constant); } // functions diff --git a/src/main/java/org/mybatis/dynamic/sql/StringConstant.java b/src/main/java/org/mybatis/dynamic/sql/StringConstant.java new file mode 100644 index 000000000..00361e562 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/StringConstant.java @@ -0,0 +1,52 @@ +/** + * Copyright 2016-2018 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; + +import java.util.Objects; +import java.util.Optional; + +import org.mybatis.dynamic.sql.render.TableAliasCalculator; + +public class StringConstant implements BasicColumn { + + private String alias; + private String value; + + private StringConstant(String value) { + this.value = Objects.requireNonNull(value); + } + + @Override + public Optional alias() { + return Optional.ofNullable(alias); + } + + @Override + public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) { + return "'" + value + "'"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public StringConstant as(String alias) { + StringConstant copy = new StringConstant(value); + copy.alias = alias; + return copy; + } + + public static StringConstant of(String value) { + return new StringConstant(value); + } +} 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 c3bdacf43..846467090 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/UpdateDSL.java @@ -21,6 +21,7 @@ import java.util.function.Function; import java.util.function.Supplier; +import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.BindableColumn; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlCriterion; @@ -28,9 +29,8 @@ 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.ArithmeticConstantMapping; -import org.mybatis.dynamic.sql.util.ArithmeticOperation; import org.mybatis.dynamic.sql.util.Buildable; +import org.mybatis.dynamic.sql.util.ColumnMapping; import org.mybatis.dynamic.sql.util.ConstantMapping; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.SelectMapping; @@ -126,6 +126,11 @@ public UpdateDSL equalTo(Buildable buildable) { return UpdateDSL.this; } + public UpdateDSL equalTo(BasicColumn rightColumn) { + columnMappings.add(ColumnMapping.of(column, rightColumn)); + return UpdateDSL.this; + } + public UpdateDSL equalToWhenPresent(T value) { return equalToWhenPresent(() -> value); } @@ -136,42 +141,6 @@ public UpdateDSL equalToWhenPresent(Supplier valueSupplier) { } return UpdateDSL.this; } - - public UpdateDSL incrementBy(T value) { - return incrementBy(() -> value); - } - - public UpdateDSL incrementBy(Supplier valueSupplier) { - columnMappings.add(ArithmeticConstantMapping.of(column, ArithmeticOperation.ADD, valueSupplier)); - return UpdateDSL.this; - } - - public UpdateDSL decrementBy(T value) { - return decrementBy(() -> value); - } - - public UpdateDSL decrementBy(Supplier valueSupplier) { - columnMappings.add(ArithmeticConstantMapping.of(column, ArithmeticOperation.SUBTRACT, valueSupplier)); - return UpdateDSL.this; - } - - public UpdateDSL multiplyBy(T value) { - return multiplyBy(() -> value); - } - - public UpdateDSL multiplyBy(Supplier valueSupplier) { - columnMappings.add(ArithmeticConstantMapping.of(column, ArithmeticOperation.MULTIPLY, valueSupplier)); - return UpdateDSL.this; - } - - public UpdateDSL divideBy(T value) { - return divideBy(() -> value); - } - - public UpdateDSL divideBy(Supplier valueSupplier) { - columnMappings.add(ArithmeticConstantMapping.of(column, ArithmeticOperation.DIVIDE, valueSupplier)); - return UpdateDSL.this; - } } public class UpdateWhereBuilder extends AbstractWhereDSL { 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 4712646e5..e2e465329 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 @@ -21,9 +21,10 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.render.RenderingStrategy; +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.ArithmeticConstantMapping; +import org.mybatis.dynamic.sql.util.ColumnMapping; import org.mybatis.dynamic.sql.util.ConstantMapping; import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.NullMapping; @@ -80,19 +81,6 @@ public FragmentAndParameters visit(ValueMapping mapping) { .build(); } - @Override - public FragmentAndParameters visit(ArithmeticConstantMapping mapping) { - String fragment = mapping.mapColumn(SqlColumn::name) - + " = " //$NON-NLS-1$ - + mapping.mapColumn(SqlColumn::name) - + " " //$NON-NLS-1$ - + mapping.operation().getOperator() - + " " //$NON-NLS-1$ - + mapping.valueSupplier().get(); - return FragmentAndParameters.withFragment(fragment) - .build(); - } - @Override public FragmentAndParameters visit(SelectMapping mapping) { SelectStatementProvider selectStatement = SelectRenderer.withSelectModel(mapping.selectModel()) @@ -111,6 +99,16 @@ public FragmentAndParameters visit(SelectMapping mapping) { .build(); } + @Override + public FragmentAndParameters visit(ColumnMapping mapping) { + String setPhrase = mapping.mapColumn(SqlColumn::name) + + " = " //$NON-NLS-1$ + + mapping.rightColumn().renderWithTableAlias(TableAliasCalculator.empty()); + + return FragmentAndParameters.withFragment(setPhrase) + .build(); + } + private Function, String> toJdbcPlaceholder(String parameterName) { return column -> renderingStrategy .getFormattedJdbcPlaceholder(column, "parameters", parameterName); //$NON-NLS-1$ diff --git a/src/main/java/org/mybatis/dynamic/sql/util/ArithmeticConstantMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/ArithmeticConstantMapping.java deleted file mode 100644 index b06079da6..000000000 --- a/src/main/java/org/mybatis/dynamic/sql/util/ArithmeticConstantMapping.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2016-2018 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 java.util.Objects; -import java.util.function.Supplier; - -import org.mybatis.dynamic.sql.SqlColumn; - -public class ArithmeticConstantMapping extends AbstractColumnMapping implements UpdateMapping { - private Supplier valueSupplier; - private ArithmeticOperation operation; - - private ArithmeticConstantMapping(SqlColumn column, ArithmeticOperation operation, Supplier valueSupplier) { - super(column); - this.operation = Objects.requireNonNull(operation); - this.valueSupplier = Objects.requireNonNull(valueSupplier); - } - - public Supplier valueSupplier() { - return valueSupplier; - } - - public ArithmeticOperation operation() { - return operation; - } - - @Override - public R accept(UpdateMappingVisitor visitor) { - return visitor.visit(this); - } - - public static ArithmeticConstantMapping of(SqlColumn column, ArithmeticOperation operation, - Supplier valueSupplier) { - return new ArithmeticConstantMapping<>(column, operation, valueSupplier); - } -} diff --git a/src/main/java/org/mybatis/dynamic/sql/util/ArithmeticOperation.java b/src/main/java/org/mybatis/dynamic/sql/util/ColumnMapping.java similarity index 51% rename from src/main/java/org/mybatis/dynamic/sql/util/ArithmeticOperation.java rename to src/main/java/org/mybatis/dynamic/sql/util/ColumnMapping.java index 1566ad004..fbe668c7d 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/ArithmeticOperation.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/ColumnMapping.java @@ -15,19 +15,28 @@ */ package org.mybatis.dynamic.sql.util; -public enum ArithmeticOperation { - ADD("+"), //$NON-NLS-1$ - SUBTRACT("-"), //$NON-NLS-1$ - MULTIPLY("*"), //$NON-NLS-1$ - DIVIDE("/"); //$NON-NLS-1$ +import org.mybatis.dynamic.sql.BasicColumn; +import org.mybatis.dynamic.sql.SqlColumn; + +public class ColumnMapping extends AbstractColumnMapping implements UpdateMapping { + + private BasicColumn rightColumn; - private String operator; + private ColumnMapping(SqlColumn column, BasicColumn rightColumn) { + super(column); + this.rightColumn = rightColumn; + } - private ArithmeticOperation(String operator) { - this.operator = operator; + public BasicColumn rightColumn() { + return rightColumn; } - public String getOperator() { - return operator; + @Override + public R accept(UpdateMappingVisitor visitor) { + return visitor.visit(this); + } + + public static ColumnMapping of(SqlColumn column, BasicColumn rightColumn) { + return new ColumnMapping(column, rightColumn); } } 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 66b397c04..18f43602d 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java @@ -24,7 +24,7 @@ public interface UpdateMappingVisitor { T visit(ValueMapping mapping); - T visit(ArithmeticConstantMapping mapping); - T visit(SelectMapping mapping); + + T visit(ColumnMapping columnMapping); } diff --git a/src/site/markdown/docs/update.md b/src/site/markdown/docs/update.md index bd46aac69..479a81fc1 100644 --- a/src/site/markdown/docs/update.md +++ b/src/site/markdown/docs/update.md @@ -25,6 +25,7 @@ Notice the `set` method. It is used to set the value of a database column. Ther 6. `set(column).equalToWhenPresent(T value)` will set 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. This is used to generate a "selective" update as defined in MyBatis Generator. 7. `set(column).equalToWhenPresent(Supplier valueSupplier)` will set 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. This is used to generate a "selective" update as defined in MyBatis Generator. 8. `set(column).equalTo(Buildable selectModelBuilder)` will set the result of a sub-query into a column. The query should only have one column and the type of the returned column must be able to be converted by the database if it is not the same type. These constraints are NOT validated by the library. +9. `set(column).equalTo(BasicColumn rightColumn)` will set the value of a column the be the value of another column. This is also useful for specifying a function such as add, subtract, etc. You can also build an update statement without a where clause. This will update every row in a table. For example: diff --git a/src/test/java/examples/animal/data/AnimalDataMapper.java b/src/test/java/examples/animal/data/AnimalDataMapper.java index 58df0ad08..25c9952d2 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-2017 the original author or authors. + * Copyright 2016-2018 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. @@ -46,6 +46,10 @@ public interface AnimalDataMapper { }) List selectMany(SelectStatementProvider selectStatement); + @SelectProvider(type=SqlProviderAdapter.class, method="select") + @ResultMap("AnimalDataResult") + AnimalData selectOne(SelectStatementProvider selectStatement); + @SelectProvider(type=SqlProviderAdapter.class, method="select") List> generalSelect(SelectStatementProvider selectStatement); diff --git a/src/test/java/examples/animal/data/AnimalDataTest.java b/src/test/java/examples/animal/data/AnimalDataTest.java index cb5aa73ed..2c8eb8b00 100644 --- a/src/test/java/examples/animal/data/AnimalDataTest.java +++ b/src/test/java/examples/animal/data/AnimalDataTest.java @@ -49,6 +49,7 @@ import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.render.TableAliasCalculator; +import org.mybatis.dynamic.sql.select.SelectDSL; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider; import org.mybatis.dynamic.sql.where.render.WhereClauseProvider; @@ -639,7 +640,7 @@ public void testNumericConstant() { try { AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); - SelectStatementProvider selectStatement = select(id, animalName, constant(3).as("some_number")) + SelectStatementProvider selectStatement = select(id, animalName, constant("3").as("some_number")) .from(animalData, "a") .where(add(bodyWeight, brainWeight), isGreaterThan(10000.0)) .build() @@ -672,7 +673,7 @@ public void testStringConstant() { try { AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); - SelectStatementProvider selectStatement = select(id, animalName, constant("fred").as("some_string")) + SelectStatementProvider selectStatement = select(id, animalName, stringConstant("fred").as("some_string")) .from(animalData, "a") .where(add(bodyWeight, brainWeight), isGreaterThan(10000.0)) .build() @@ -738,7 +739,7 @@ public void testAddConstant() { try { AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); - SelectStatementProvider selectStatement = select(id, animalName, add(bodyWeight, constant(22), constant(33)).as("calculated_weight")) + SelectStatementProvider selectStatement = select(id, animalName, add(bodyWeight, constant("22"), constant("33")).as("calculated_weight")) .from(animalData, "a") .where(add(bodyWeight, brainWeight), isGreaterThan(10000.0)) .build() @@ -804,7 +805,7 @@ public void testDivideConstant() { try { AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); - SelectStatementProvider selectStatement = select(id, animalName, divide(bodyWeight, constant(10.0)).as("calculated_weight")) + SelectStatementProvider selectStatement = select(id, animalName, divide(bodyWeight, constant("10.0")).as("calculated_weight")) .from(animalData, "a") .where(add(bodyWeight, brainWeight), isGreaterThan(10000.0)) .build() @@ -870,7 +871,7 @@ public void testMultiplyConstant() { try { AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); - SelectStatementProvider selectStatement = select(id, animalName, multiply(bodyWeight, constant(2.0)).as("calculated_weight")) + SelectStatementProvider selectStatement = select(id, animalName, multiply(bodyWeight, constant("2.0")).as("calculated_weight")) .from(animalData, "a") .where(add(bodyWeight, brainWeight), isGreaterThan(10000.0)) .build() @@ -936,7 +937,7 @@ public void testSubtractConstant() { try { AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); - SelectStatementProvider selectStatement = select(id, animalName, subtract(bodyWeight, constant(5.5)).as("calculated_weight")) + SelectStatementProvider selectStatement = select(id, animalName, subtract(bodyWeight, constant("5.5")).as("calculated_weight")) .from(animalData, "a") .where(add(bodyWeight, brainWeight), isGreaterThan(10000.0)) .build() @@ -969,7 +970,7 @@ public void testComplexExpression() { try { AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); - SelectStatementProvider selectStatement = select(id, animalName, add(multiply(bodyWeight, constant(5.5)), subtract(brainWeight, constant(2))).as("calculated_weight")) + SelectStatementProvider selectStatement = select(id, animalName, add(multiply(bodyWeight, constant("5.5")), subtract(brainWeight, constant("2"))).as("calculated_weight")) .from(animalData, "a") .where(add(bodyWeight, brainWeight), isGreaterThan(10000.0)) .build() @@ -1813,4 +1814,78 @@ public void testUpdateWithSelect() { sqlSession.close(); } } + + @Test + public void testUpdateWithAddAndSubtract() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + + UpdateStatementProvider updateStatement = update(animalData) + .set(brainWeight).equalTo(add(brainWeight, constant("2"))) + .set(bodyWeight).equalTo(subtract(bodyWeight, constant("3"))) + .where(id, isEqualTo(1)) + .build() + .render(RenderingStrategy.MYBATIS3); + + String expected = "update AnimalData " + + "set brain_weight = (brain_weight + 2), body_weight = (body_weight - 3) " + + "where id = #{parameters.p1,jdbcType=INTEGER}"; + assertThat(updateStatement.getUpdateStatement()).isEqualTo(expected); + assertThat(updateStatement.getParameters().size()).isEqualTo(1); + assertThat(updateStatement.getParameters().get("p1")).isEqualTo(1); + + int rows = mapper.update(updateStatement); + assertThat(rows).isEqualTo(1); + + AnimalData record = SelectDSL.selectWithMapper(mapper::selectOne, id, bodyWeight, brainWeight) + .from(animalData) + .where(id, isEqualTo(1)) + .build() + .execute(); + + assertThat(record.getBodyWeight()).isEqualTo(-2.86); + assertThat(record.getBrainWeight()).isEqualTo(2.005); + + } finally { + sqlSession.close(); + } + } + + @Test + public void testUpdateWithMultiplyAndDivide() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + + UpdateStatementProvider updateStatement = update(animalData) + .set(brainWeight).equalTo(divide(brainWeight, constant("2"))) + .set(bodyWeight).equalTo(multiply(bodyWeight, constant("3"))) + .where(id, isEqualTo(1)) + .build() + .render(RenderingStrategy.MYBATIS3); + + String expected = "update AnimalData " + + "set brain_weight = (brain_weight / 2), body_weight = (body_weight * 3) " + + "where id = #{parameters.p1,jdbcType=INTEGER}"; + assertThat(updateStatement.getUpdateStatement()).isEqualTo(expected); + assertThat(updateStatement.getParameters().size()).isEqualTo(1); + assertThat(updateStatement.getParameters().get("p1")).isEqualTo(1); + + int rows = mapper.update(updateStatement); + assertThat(rows).isEqualTo(1); + + AnimalData record = SelectDSL.selectWithMapper(mapper::selectOne, id, bodyWeight, brainWeight) + .from(animalData) + .where(id, isEqualTo(1)) + .build() + .execute(); + + assertThat(record.getBodyWeight()).isEqualTo(0.42, within(.001)); + assertThat(record.getBrainWeight()).isEqualTo(.0025); + + } finally { + sqlSession.close(); + } + } } diff --git a/src/test/java/examples/animal/data/BindingTest.java b/src/test/java/examples/animal/data/BindingTest.java new file mode 100644 index 000000000..04f65504e --- /dev/null +++ b/src/test/java/examples/animal/data/BindingTest.java @@ -0,0 +1,125 @@ +/** + * Copyright 2016-2018 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 examples.animal.data; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +@RunWith(JUnitPlatform.class) +/** + * Tests for understanding where bind parameters are allowed + * + */ +public class BindingTest { + + private static final String JDBC_URL = "jdbc:hsqldb:mem:aname"; + private static final String JDBC_DRIVER = "org.hsqldb.jdbcDriver"; + + private SqlSessionFactory sqlSessionFactory; + + @BeforeEach + public void setup() throws Exception { + Class.forName(JDBC_DRIVER); + InputStream is = getClass().getResourceAsStream("/examples/animal/data/CreateAnimalData.sql"); + try (Connection connection = DriverManager.getConnection(JDBC_URL, "sa", "")) { + ScriptRunner sr = new ScriptRunner(connection); + sr.setLogWriter(null); + sr.runScript(new InputStreamReader(is)); + } + + UnpooledDataSource ds = new UnpooledDataSource(JDBC_DRIVER, JDBC_URL, "sa", ""); + Environment environment = new Environment("test", new JdbcTransactionFactory(), ds); + Configuration config = new Configuration(environment); + config.addMapper(AnimalDataMapper.class); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(config); + } + + @Test + public void testBindInSelectList() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Connection connection = sqlSession.getConnection(); + + PreparedStatement ps = connection.prepareStatement("select brain_weight + ? as calc from AnimalData where id = ?"); + ps.setDouble(1, 1.0); + ps.setInt(2, 1); + + ResultSet rs = ps.executeQuery(); + double calculatedWeight = 0.0; + if (rs.next()) { + calculatedWeight = rs.getDouble("CALC"); + } + + rs.close(); + ps.close(); + + assertThat(calculatedWeight).isEqualTo(1.005); + } catch (SQLException e) { + fail("SQL Exception", e); + } finally { + sqlSession.close(); + } + } + + @Test + public void testBindInWeirdWhere() { + SqlSession sqlSession = sqlSessionFactory.openSession(); + try { + Connection connection = sqlSession.getConnection(); + + PreparedStatement ps = connection.prepareStatement("select brain_weight from AnimalData where brain_weight + ? > ? and id = ?"); + ps.setDouble(1, 1.0); + ps.setDouble(2, 1.0); + ps.setInt(3, 1); + + ResultSet rs = ps.executeQuery(); + double calculatedWeight = 0.0; + if (rs.next()) { + calculatedWeight = rs.getDouble("BRAIN_WEIGHT"); + } + + rs.close(); + ps.close(); + + assertThat(calculatedWeight).isEqualTo(.005); + } catch (SQLException e) { + fail("SQL Exception", e); + } finally { + sqlSession.close(); + } + } +} diff --git a/src/test/java/org/mybatis/dynamic/sql/update/UpdateStatementTest.java b/src/test/java/org/mybatis/dynamic/sql/update/UpdateStatementTest.java index eaa4ee7ee..b6f59d099 100644 --- a/src/test/java/org/mybatis/dynamic/sql/update/UpdateStatementTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/update/UpdateStatementTest.java @@ -174,18 +174,18 @@ public void testFullUpdateStatementNoWhere() { @Test public void testUpdateStatementArithmeticOperation() { UpdateStatementProvider updateStatement = update(foo) - .set(id).incrementBy(1) - .set(id).decrementBy(2) - .set(id).multiplyBy(3) - .set(id).divideBy(4) + .set(id).equalTo(add(id, constant("1"))) + .set(id).equalTo(subtract(id, constant("2"))) + .set(id).equalTo(multiply(id, constant("3"))) + .set(id).equalTo(divide(id, constant("4"))) .build() .render(RenderingStrategy.MYBATIS3); String expectedStatement = "update foo " - + "set id = id + 1, " - + "id = id - 2, " - + "id = id * 3, " - + "id = id / 4"; + + "set id = (id + 1), " + + "id = (id - 2), " + + "id = (id * 3), " + + "id = (id / 4)"; SoftAssertions.assertSoftly(softly -> { softly.assertThat(updateStatement.getUpdateStatement()).isEqualTo(expectedStatement);