Skip to content

Commit

Permalink
Merge pull request #738 from jeffgbutler/gh737
Browse files Browse the repository at this point in the history
Add BoundValue Implementation
  • Loading branch information
jeffgbutler authored Jan 30, 2024
2 parents 1dcdbfd + 3592b30 commit f08f334
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ types - which is a rare usage. Please let us know if this causes an undo hardshi
initial enhancement request that inspired this change. As a result of the changes, one method is deprecated
in the `BasicColumn` object. If you have implemented any custom functions, please note this deprecation and update
your code accordingly. ([#662](https://github.com/mybatis/mybatis-dynamic-sql/pull/662))
2. Added the ability to code a bound value in rendered SQL. This is similar to a constant, but the value is added to
the parameter map and a bind parameter marker is rendered. ([#738](https://github.com/mybatis/mybatis-dynamic-sql/pull/738))

## Release 1.5.0 - April 21, 2023

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2016-2023 the original author or authors.
Copyright 2016-2024 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.
Expand Down
71 changes: 71 additions & 0 deletions src/main/java/org/mybatis/dynamic/sql/BoundValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2016-2024 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
*
* https://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.exception.InvalidSqlException;
import org.mybatis.dynamic.sql.render.RenderedParameterInfo;
import org.mybatis.dynamic.sql.render.RenderingContext;
import org.mybatis.dynamic.sql.util.FragmentAndParameters;
import org.mybatis.dynamic.sql.util.Messages;

/**
* BoundValues are added to rendered SQL as a parameter marker only.
*
* <p>BoundValues are most useful in the context of functions. For example, a column value could be
* incremented with an update statement like this:
* <code>
* UpdateStatementProvider updateStatement = update(person)
* .set(age).equalTo(add(age, value(1)))
* .where(id, isEqualTo(5))
* .build()
* .render(RenderingStrategies.MYBATIS3);
* </code>
*
* @param <T> the column type
* @since 1.5.1
*/
public class BoundValue<T> implements BindableColumn<T> {
private final T value;

private BoundValue(T value) {
this.value = Objects.requireNonNull(value);
}

@Override
public FragmentAndParameters render(RenderingContext renderingContext) {
RenderedParameterInfo rpi = renderingContext.calculateParameterInfo(this);
return FragmentAndParameters.withFragment(rpi.renderedPlaceHolder())
.withParameter(rpi.parameterMapKey(), value)
.build();
}

@Override
public Optional<String> alias() {
return Optional.empty();
}

@Override
public BoundValue<T> as(String alias) {
throw new InvalidSqlException(Messages.getString("ERROR.38")); //$NON-NLS-1$
}

public static <T> BoundValue<T> of(T value) {
return new BoundValue<>(value);
}
}
6 changes: 5 additions & 1 deletion src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 the original author or authors.
* Copyright 2016-2024 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.
Expand Down Expand Up @@ -492,6 +492,10 @@ static StringConstant stringConstant(String constant) {
return StringConstant.of(constant);
}

static <T> BoundValue<T> value(T value) {
return BoundValue.of(value);
}

// functions
static <T> Add<T> add(BindableColumn<T> firstColumn, BasicColumn secondColumn,
BasicColumn... subsequentColumns) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 the original author or authors.
* Copyright 2016-2024 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.
Expand All @@ -19,6 +19,7 @@ package org.mybatis.dynamic.sql.util.kotlin.elements
import org.mybatis.dynamic.sql.AndOrCriteriaGroup
import org.mybatis.dynamic.sql.BasicColumn
import org.mybatis.dynamic.sql.BindableColumn
import org.mybatis.dynamic.sql.BoundValue
import org.mybatis.dynamic.sql.Constant
import org.mybatis.dynamic.sql.SortSpecification
import org.mybatis.dynamic.sql.SqlBuilder
Expand Down Expand Up @@ -117,6 +118,8 @@ fun <T> constant(constant: String): Constant<T> = SqlBuilder.constant(constant)

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

fun <T> value(value: T): BoundValue<T> = SqlBuilder.value(value)

// functions
fun <T> add(
firstColumn: BindableColumn<T>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2016-2023 the original author or authors.
# Copyright 2016-2024 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.
Expand Down Expand Up @@ -54,4 +54,5 @@ ERROR.34=You must specify "select" or "selectDistinct" before any other clauses
ERROR.35=Multi-select statements must have at least one "union" or "union all" expression
ERROR.36=You must either implement the "render" or "renderWithTableAlias" method in a column or function
ERROR.37=The "{0}" function does not support conditions that fail to render
ERROR.38=Bound values cannot be aliased
INTERNAL.ERROR=Internal Error {0}
20 changes: 19 additions & 1 deletion src/test/java/examples/simple/PersonMapperTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 the original author or authors.
* Copyright 2016-2024 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.
Expand All @@ -25,6 +25,7 @@
import static examples.simple.PersonDynamicSqlSupport.occupation;
import static examples.simple.PersonDynamicSqlSupport.person;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mybatis.dynamic.sql.SqlBuilder.*;

import java.io.InputStream;
Expand Down Expand Up @@ -56,6 +57,7 @@
import org.mybatis.dynamic.sql.select.CountDSLCompleter;
import org.mybatis.dynamic.sql.select.SelectDSLCompleter;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;

class PersonMapperTest {

Expand Down Expand Up @@ -973,4 +975,20 @@ void testMultiSelectPagingVariation() {

assertThat(selectStatement.getSelectStatement()).isEqualTo(expected);
}

@Test
void gh737() {
UpdateStatementProvider updateStatement = update(person)
.set(addressId).equalTo(add(addressId, value(4)))
.where(id, isEqualTo(5))
.build()
.render(RenderingStrategies.MYBATIS3);

String expected = "update Person " +
"set address_id = (address_id + #{parameters.p1}) " +
"where id = #{parameters.p2,jdbcType=INTEGER}";

assertThat(updateStatement.getUpdateStatement()).isEqualTo(expected);
assertThat(updateStatement.getParameters()).containsExactly(entry("p1", 4), entry("p2", 5));
}
}
13 changes: 12 additions & 1 deletion src/test/java/org/mybatis/dynamic/sql/InvalidSQLTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 the original author or authors.
* Copyright 2016-2024 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.
Expand All @@ -21,6 +21,7 @@
import static org.mybatis.dynamic.sql.SqlBuilder.insertInto;
import static org.mybatis.dynamic.sql.SqlBuilder.select;
import static org.mybatis.dynamic.sql.SqlBuilder.update;
import static org.mybatis.dynamic.sql.SqlBuilder.value;

import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -253,6 +254,16 @@ void testInvalidPagingModel() {
.withMessage(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_13));
}

@Test
void testInvalidValueAlias() {
BoundValue<Integer> foo = value(1);

assertThat(foo.alias()).isEmpty();
assertThatExceptionOfType(InvalidSqlException.class)
.isThrownBy(() -> foo.as("foo"))
.withMessage(Messages.getString("ERROR.38"));
}

@Test
void testBadColumn() {
SelectModel selectModel = select(new BadCount<>()).from(person).build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 the original author or authors.
* Copyright 2016-2024 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.
Expand All @@ -15,13 +15,16 @@
*/
package examples.kotlin.spring.canonical

import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.addressId
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.employed
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.person
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.firstName
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.id
import examples.kotlin.spring.canonical.PersonDynamicSqlSupport.lastName
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.entry
import org.junit.jupiter.api.Test
import org.mybatis.dynamic.sql.util.kotlin.elements.add
import org.mybatis.dynamic.sql.util.kotlin.elements.applyOperator
import org.mybatis.dynamic.sql.util.kotlin.elements.avg
import org.mybatis.dynamic.sql.util.kotlin.elements.concat
Expand Down Expand Up @@ -50,9 +53,11 @@ import org.mybatis.dynamic.sql.util.kotlin.elements.stringConstant
import org.mybatis.dynamic.sql.util.kotlin.elements.substring
import org.mybatis.dynamic.sql.util.kotlin.elements.subtract
import org.mybatis.dynamic.sql.util.kotlin.elements.sum
import org.mybatis.dynamic.sql.util.kotlin.elements.value
import org.mybatis.dynamic.sql.util.kotlin.spring.select
import org.mybatis.dynamic.sql.util.kotlin.spring.selectList
import org.mybatis.dynamic.sql.util.kotlin.spring.selectOne
import org.mybatis.dynamic.sql.util.kotlin.spring.update
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig
Expand Down Expand Up @@ -654,4 +659,19 @@ open class KotlinElementsTest {
assertThat(rows).hasSize(6)
assertThat(rows[0]).isEqualTo("Fred")
}

@Test
fun testValue() {
val updateStatement = update(person) {
set(addressId) equalTo add(addressId, value(4))
where {
id isEqualTo 5
}
}

assertThat(updateStatement.updateStatement).isEqualTo(
"update Person set address_id = (address_id + :p1) where id = :p2"
)
assertThat(updateStatement.parameters).containsExactly(entry("p1", 4), entry("p2", 5))
}
}

0 comments on commit f08f334

Please sign in to comment.