Skip to content

Commit 481b76a

Browse files
authored
Merge pull request #573 from jeffgbutler/concat-function
Add concat(x, y, z) Function for Wider Database Compatibility
2 parents 7cbc7c2 + f69d6a1 commit 481b76a

File tree

7 files changed

+132
-3
lines changed

7 files changed

+132
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ The pull request for this change is ([#550](https://github.com/mybatis/mybatis-d
5151
algorithm. Also deprecated the constructors on SqlTable that accept Suppliers for table name - this creates an
5252
effectively mutable object and goes against the principles of immutability that we strive for in the library.
5353
([#572](https://github.com/mybatis/mybatis-dynamic-sql/pull/572))
54-
55-
54+
5. Add `SqlBuilder.concat` and the equivalent in Kotlin. This is a concatenate function that works on more databases.
55+
([#573](https://github.com/mybatis/mybatis-dynamic-sql/pull/573))
5656

5757
## Release 1.4.1 - October 7, 2022
5858

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.mybatis.dynamic.sql.select.aggregate.Min;
4343
import org.mybatis.dynamic.sql.select.aggregate.Sum;
4444
import org.mybatis.dynamic.sql.select.function.Add;
45+
import org.mybatis.dynamic.sql.select.function.Concat;
4546
import org.mybatis.dynamic.sql.select.function.Concatenate;
4647
import org.mybatis.dynamic.sql.select.function.Divide;
4748
import org.mybatis.dynamic.sql.select.function.Lower;
@@ -488,11 +489,34 @@ static <T> Subtract<T> subtract(BindableColumn<T> firstColumn, BasicColumn secon
488489
return Subtract.of(firstColumn, secondColumn, subsequentColumns);
489490
}
490491

492+
/**
493+
* Concatenate function that renders as "(x || y || z)". This will not work on some
494+
* databases like MySql. In that case, use {@link SqlBuilder#concat(BindableColumn, BasicColumn...)}
495+
*
496+
* @param firstColumn first column
497+
* @param secondColumn second column
498+
* @param subsequentColumns subsequent columns
499+
* @return a Concatenate instance
500+
* @param <T> type of column
501+
*/
491502
static <T> Concatenate<T> concatenate(BindableColumn<T> firstColumn, BasicColumn secondColumn,
492503
BasicColumn... subsequentColumns) {
493504
return Concatenate.concatenate(firstColumn, secondColumn, subsequentColumns);
494505
}
495506

507+
/**
508+
* Concatenate function that renders as "concat(x, y, z)". This version works on more databases
509+
* than {@link SqlBuilder#concatenate(BindableColumn, BasicColumn, BasicColumn...)}
510+
*
511+
* @param firstColumn first column
512+
* @param subsequentColumns subsequent columns
513+
* @return a Concat instance
514+
* @param <T> type of column
515+
*/
516+
static <T> Concat<T> concat(BindableColumn<T> firstColumn, BasicColumn... subsequentColumns) {
517+
return Concat.concat(firstColumn, subsequentColumns);
518+
}
519+
496520
static <T> OperatorFunction<T> applyOperator(String operator, BindableColumn<T> firstColumn,
497521
BasicColumn secondColumn, BasicColumn... subsequentColumns) {
498522
return OperatorFunction.of(operator, firstColumn, secondColumn, subsequentColumns);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2016-2023 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.select.function;
17+
18+
import java.util.ArrayList;
19+
import java.util.Arrays;
20+
import java.util.List;
21+
import java.util.stream.Collectors;
22+
23+
import org.mybatis.dynamic.sql.BasicColumn;
24+
import org.mybatis.dynamic.sql.BindableColumn;
25+
import org.mybatis.dynamic.sql.render.TableAliasCalculator;
26+
27+
public class Concat<T> extends AbstractUniTypeFunction<T, Concat<T>> {
28+
private final List<BasicColumn> allColumns = new ArrayList<>();
29+
30+
protected Concat(BindableColumn<T> firstColumn, List<BasicColumn> subsequentColumns) {
31+
super(firstColumn);
32+
allColumns.add(firstColumn);
33+
this.allColumns.addAll(subsequentColumns);
34+
}
35+
36+
@Override
37+
public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
38+
return allColumns.stream().map(c -> c.renderWithTableAlias(tableAliasCalculator))
39+
.collect(Collectors.joining(", ", "concat(", ")")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
40+
}
41+
42+
@Override
43+
protected Concat<T> copy() {
44+
return new Concat<>(column, allColumns.subList(1, allColumns.size()));
45+
}
46+
47+
public static <T> Concat<T> concat(BindableColumn<T> firstColumn, BasicColumn... subsequentColumns) {
48+
return new Concat<>(firstColumn, Arrays.asList(subsequentColumns));
49+
}
50+
51+
public static <T> Concat<T> of(BindableColumn<T> firstColumn, List<BasicColumn> subsequentColumns) {
52+
return new Concat<>(firstColumn, subsequentColumns);
53+
}
54+
}

src/main/java/org/mybatis/dynamic/sql/select/function/OperatorFunction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected OperatorFunction<T> copy() {
5050
public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
5151
String paddedOperator = " " + operator + " "; //$NON-NLS-1$ //$NON-NLS-2$
5252

53-
// note - the cast below is added for a type inference bug in the Java9 compiler.
53+
// note - the cast below is added for type inference issues in some compilers
5454
return Stream.of(Stream.of((BasicColumn) column), Stream.of(secondColumn), subsequentColumns.stream())
5555
.flatMap(Function.identity())
5656
.map(column -> column.renderWithTableAlias(tableAliasCalculator))

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import org.mybatis.dynamic.sql.select.aggregate.Max
3232
import org.mybatis.dynamic.sql.select.aggregate.Min
3333
import org.mybatis.dynamic.sql.select.aggregate.Sum
3434
import org.mybatis.dynamic.sql.select.function.Add
35+
import org.mybatis.dynamic.sql.select.function.Concat
3536
import org.mybatis.dynamic.sql.select.function.Concatenate
3637
import org.mybatis.dynamic.sql.select.function.Divide
3738
import org.mybatis.dynamic.sql.select.function.Lower
@@ -138,6 +139,11 @@ fun <T> subtract(
138139
vararg subsequentColumns: BasicColumn
139140
): Subtract<T> = Subtract.of(firstColumn, secondColumn, subsequentColumns.asList())
140141

142+
fun <T> concat(
143+
firstColumn: BindableColumn<T>,
144+
vararg subsequentColumns: BasicColumn
145+
): Concat<T> = Concat.of(firstColumn, subsequentColumns.asList())
146+
141147
fun <T> concatenate(
142148
firstColumn: BindableColumn<T>,
143149
secondColumn: BasicColumn,

src/test/java/examples/animal/data/AnimalDataTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,33 @@ void testAddConstantWithConstantFirst() {
10381038
}
10391039
}
10401040

1041+
@Test
1042+
void testConcat() {
1043+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
1044+
CommonSelectMapper mapper = sqlSession.getMapper(CommonSelectMapper.class);
1045+
1046+
SelectStatementProvider selectStatement = select(id, concat(animalName, stringConstant(" - The Legend")).as("display_name"))
1047+
.from(animalData, "a")
1048+
.where(add(bodyWeight, brainWeight), isGreaterThan(10000.0))
1049+
.build()
1050+
.render(RenderingStrategies.MYBATIS3);
1051+
1052+
String expected = "select a.id, concat(a.animal_name, ' - The Legend') as display_name "
1053+
+ "from AnimalData a "
1054+
+ "where (a.body_weight + a.brain_weight) > #{parameters.p1,jdbcType=DOUBLE}";
1055+
1056+
List<Map<String, Object>> animals = mapper.selectManyMappedRows(selectStatement);
1057+
1058+
assertAll(
1059+
() -> assertThat(selectStatement.getSelectStatement()).isEqualTo(expected),
1060+
() -> assertThat(animals).hasSize(3),
1061+
() -> assertThat(animals.get(0)).containsEntry("DISPLAY_NAME", "African elephant - The Legend"),
1062+
() -> assertThat(animals.get(1)).containsEntry("DISPLAY_NAME", "Dipliodocus - The Legend"),
1063+
() -> assertThat(animals.get(2)).containsEntry("DISPLAY_NAME", "Brachiosaurus - The Legend")
1064+
);
1065+
}
1066+
}
1067+
10411068
@Test
10421069
void testConcatenate() {
10431070
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.assertj.core.api.Assertions.assertThat
2424
import org.junit.jupiter.api.Test
2525
import org.mybatis.dynamic.sql.util.kotlin.elements.applyOperator
2626
import org.mybatis.dynamic.sql.util.kotlin.elements.avg
27+
import org.mybatis.dynamic.sql.util.kotlin.elements.concat
2728
import org.mybatis.dynamic.sql.util.kotlin.elements.concatenate
2829
import org.mybatis.dynamic.sql.util.kotlin.elements.constant
2930
import org.mybatis.dynamic.sql.util.kotlin.elements.count
@@ -189,6 +190,23 @@ open class KotlinElementsTest {
189190
assertThat(rows[5]).isEqualTo(4)
190191
}
191192

193+
@Test
194+
fun testConcat() {
195+
val selectStatement = select(concat(firstName, stringConstant(" "), lastName)) {
196+
from(person)
197+
orderBy(id)
198+
}
199+
200+
assertThat(selectStatement.selectStatement).isEqualTo(
201+
"select concat(first_name, ' ', last_name) from Person order by id"
202+
)
203+
204+
val rows = template.selectList(selectStatement, String::class)
205+
206+
assertThat(rows).hasSize(6)
207+
assertThat(rows[5]).isEqualTo("Bamm Bamm Rubble")
208+
}
209+
192210
@Test
193211
fun testConcatenate() {
194212
val selectStatement = select(concatenate(firstName, stringConstant(" "), lastName)) {

0 commit comments

Comments
 (0)