Skip to content

Commit 54666a7

Browse files
committed
Add BoundValue implementation
1 parent 674b308 commit 54666a7

File tree

9 files changed

+137
-7
lines changed

9 files changed

+137
-7
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ types - which is a rare usage. Please let us know if this causes an undo hardshi
3737
initial enhancement request that inspired this change. As a result of the changes, one method is deprecated
3838
in the `BasicColumn` object. If you have implemented any custom functions, please note this deprecation and update
3939
your code accordingly. ([#662](https://github.com/mybatis/mybatis-dynamic-sql/pull/662))
40+
2. Added the ability to code a bound value in rendered SQL. This is similar to a constant, but the value is added to
41+
the parameter map and a bind parameter marker is rendered.
4042

4143
## Release 1.5.0 - April 21, 2023
4244

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2016-2023 the original author or authors.
4+
Copyright 2016-2024 the original author or authors.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2016-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.dynamic.sql;
17+
18+
import java.util.Objects;
19+
import java.util.Optional;
20+
21+
import org.mybatis.dynamic.sql.exception.InvalidSqlException;
22+
import org.mybatis.dynamic.sql.render.RenderedParameterInfo;
23+
import org.mybatis.dynamic.sql.render.RenderingContext;
24+
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
25+
import org.mybatis.dynamic.sql.util.Messages;
26+
27+
/**
28+
* BoundValues are added to rendered SQL as a parameter marker only.
29+
*
30+
* <p>BoundValues are most useful in the context of functions. For example, a column value could be
31+
* incremented with an update statement like this:
32+
* <code>
33+
* UpdateStatementProvider updateStatement = update(person)
34+
* .set(age).equalTo(add(age, value(1)))
35+
* .where(id, isEqualTo(5))
36+
* .build()
37+
* .render(RenderingStrategies.MYBATIS3);
38+
* </code>
39+
*
40+
* @param <T> the column type
41+
* @since 1.5.1
42+
*/
43+
public class BoundValue<T> implements BindableColumn<T> {
44+
private final T value;
45+
46+
private BoundValue(T value) {
47+
this.value = Objects.requireNonNull(value);
48+
}
49+
50+
@Override
51+
public FragmentAndParameters render(RenderingContext renderingContext) {
52+
RenderedParameterInfo rpi = renderingContext.calculateParameterInfo(this);
53+
return FragmentAndParameters.withFragment(rpi.renderedPlaceHolder())
54+
.withParameter(rpi.parameterMapKey(), value)
55+
.build();
56+
}
57+
58+
@Override
59+
public Optional<String> alias() {
60+
return Optional.empty();
61+
}
62+
63+
@Override
64+
public BoundValue<T> as(String alias) {
65+
throw new InvalidSqlException(Messages.getString("ERROR.38")); //$NON-NLS-1$
66+
}
67+
68+
public static <T> BoundValue<T> of(T value) {
69+
return new BoundValue<>(value);
70+
}
71+
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2023 the original author or authors.
2+
* Copyright 2016-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -492,6 +492,10 @@ static StringConstant stringConstant(String constant) {
492492
return StringConstant.of(constant);
493493
}
494494

495+
static <T> BoundValue<T> value(T value) {
496+
return BoundValue.of(value);
497+
}
498+
495499
// functions
496500
static <T> Add<T> add(BindableColumn<T> firstColumn, BasicColumn secondColumn,
497501
BasicColumn... subsequentColumns) {

src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2023 the original author or authors.
2+
* Copyright 2016-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ package org.mybatis.dynamic.sql.util.kotlin.elements
1919
import org.mybatis.dynamic.sql.AndOrCriteriaGroup
2020
import org.mybatis.dynamic.sql.BasicColumn
2121
import org.mybatis.dynamic.sql.BindableColumn
22+
import org.mybatis.dynamic.sql.BoundValue
2223
import org.mybatis.dynamic.sql.Constant
2324
import org.mybatis.dynamic.sql.SortSpecification
2425
import org.mybatis.dynamic.sql.SqlBuilder
@@ -117,6 +118,8 @@ fun <T> constant(constant: String): Constant<T> = SqlBuilder.constant(constant)
117118

118119
fun stringConstant(constant: String): StringConstant = SqlBuilder.stringConstant(constant)
119120

121+
fun <T> value(value: T): BoundValue<T> = SqlBuilder.value(value)
122+
120123
// functions
121124
fun <T> add(
122125
firstColumn: BindableColumn<T>,

src/main/resources/org/mybatis/dynamic/sql/util/messages.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2016-2023 the original author or authors.
2+
# Copyright 2016-2024 the original author or authors.
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
55
# you may not use this file except in compliance with the License.
@@ -54,4 +54,5 @@ ERROR.34=You must specify "select" or "selectDistinct" before any other clauses
5454
ERROR.35=Multi-select statements must have at least one "union" or "union all" expression
5555
ERROR.36=You must either implement the "render" or "renderWithTableAlias" method in a column or function
5656
ERROR.37=The "{0}" function does not support conditions that fail to render
57+
ERROR.38=Bound values cannot be aliased
5758
INTERNAL.ERROR=Internal Error {0}

src/test/java/examples/simple/PersonMapperTest.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2023 the original author or authors.
2+
* Copyright 2016-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import static examples.simple.PersonDynamicSqlSupport.occupation;
2626
import static examples.simple.PersonDynamicSqlSupport.person;
2727
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.assertj.core.api.Assertions.entry;
2829
import static org.mybatis.dynamic.sql.SqlBuilder.*;
2930

3031
import java.io.InputStream;
@@ -56,6 +57,7 @@
5657
import org.mybatis.dynamic.sql.select.CountDSLCompleter;
5758
import org.mybatis.dynamic.sql.select.SelectDSLCompleter;
5859
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
60+
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
5961

6062
class PersonMapperTest {
6163

@@ -973,4 +975,20 @@ void testMultiSelectPagingVariation() {
973975

974976
assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
975977
}
978+
979+
@Test
980+
void gh737() {
981+
UpdateStatementProvider updateStatement = update(person)
982+
.set(addressId).equalTo(add(addressId, value(4)))
983+
.where(id, isEqualTo(5))
984+
.build()
985+
.render(RenderingStrategies.MYBATIS3);
986+
987+
String expected = "update Person " +
988+
"set address_id = (address_id + #{parameters.p1}) " +
989+
"where id = #{parameters.p2,jdbcType=INTEGER}";
990+
991+
assertThat(updateStatement.getUpdateStatement()).isEqualTo(expected);
992+
assertThat(updateStatement.getParameters()).containsExactly(entry("p1", 4), entry("p2", 5));
993+
}
976994
}

src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2023 the original author or authors.
2+
* Copyright 2016-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import static org.mybatis.dynamic.sql.SqlBuilder.insertInto;
2222
import static org.mybatis.dynamic.sql.SqlBuilder.select;
2323
import static org.mybatis.dynamic.sql.SqlBuilder.update;
24+
import static org.mybatis.dynamic.sql.SqlBuilder.value;
2425

2526
import java.util.ArrayList;
2627
import java.util.Collections;
@@ -253,6 +254,16 @@ void testInvalidPagingModel() {
253254
.withMessage(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_13));
254255
}
255256

257+
@Test
258+
void testInvalidValueAlias() {
259+
BoundValue<Integer> foo = value(1);
260+
261+
assertThat(foo.alias()).isEmpty();
262+
assertThatExceptionOfType(InvalidSqlException.class)
263+
.isThrownBy(() -> foo.as("foo"))
264+
.withMessage(Messages.getString("ERROR.38"));
265+
}
266+
256267
@Test
257268
void testBadColumn() {
258269
SelectModel selectModel = select(new BadCount<>()).from(person).build();

src/test/kotlin/examples/kotlin/spring/canonical/KotlinElementsTest.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2023 the original author or authors.
2+
* Copyright 2016-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,13 +15,16 @@
1515
*/
1616
package examples.kotlin.spring.canonical
1717

18+
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.addressId
1819
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.employed
1920
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.person
2021
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.firstName
2122
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.id
2223
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.lastName
2324
import org.assertj.core.api.Assertions.assertThat
25+
import org.assertj.core.api.Assertions.entry
2426
import org.junit.jupiter.api.Test
27+
import org.mybatis.dynamic.sql.util.kotlin.elements.add
2528
import org.mybatis.dynamic.sql.util.kotlin.elements.applyOperator
2629
import org.mybatis.dynamic.sql.util.kotlin.elements.avg
2730
import org.mybatis.dynamic.sql.util.kotlin.elements.concat
@@ -50,9 +53,11 @@ import org.mybatis.dynamic.sql.util.kotlin.elements.stringConstant
5053
import org.mybatis.dynamic.sql.util.kotlin.elements.substring
5154
import org.mybatis.dynamic.sql.util.kotlin.elements.subtract
5255
import org.mybatis.dynamic.sql.util.kotlin.elements.sum
56+
import org.mybatis.dynamic.sql.util.kotlin.elements.value
5357
import org.mybatis.dynamic.sql.util.kotlin.spring.select
5458
import org.mybatis.dynamic.sql.util.kotlin.spring.selectList
5559
import org.mybatis.dynamic.sql.util.kotlin.spring.selectOne
60+
import org.mybatis.dynamic.sql.util.kotlin.spring.update
5661
import org.springframework.beans.factory.annotation.Autowired
5762
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
5863
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig
@@ -654,4 +659,19 @@ open class KotlinElementsTest {
654659
assertThat(rows).hasSize(6)
655660
assertThat(rows[0]).isEqualTo("Fred")
656661
}
662+
663+
@Test
664+
fun testValue() {
665+
val updateStatement = update(person) {
666+
set(addressId) equalTo add(addressId, value(4))
667+
where {
668+
id isEqualTo 5
669+
}
670+
}
671+
672+
assertThat(updateStatement.updateStatement).isEqualTo(
673+
"update Person set address_id = (address_id + :p1) where id = :p2"
674+
)
675+
assertThat(updateStatement.parameters).containsExactly(entry("p1", 4), entry("p2", 5))
676+
}
657677
}

0 commit comments

Comments
 (0)