Skip to content

Commit 3ab01c5

Browse files
committed
Update insert bindings for record based inserts
Record based inserts (insert, batch insert, and multirow insert) have a different form of binding - rather than binding to a value in a Map, these bindings bind to a property in a row class. This commit makes the bindings consistent for those statements. Spring support is also updated to support the new binding form.
1 parent 336b325 commit 3ab01c5

File tree

11 files changed

+107
-65
lines changed

11 files changed

+107
-65
lines changed

src/main/java/org/mybatis/dynamic/sql/insert/render/AbstractMultiRowValuePhraseVisitor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,8 @@ public FieldAndValueAndParameters visit(PropertyMapping mapping) {
6262
.build();
6363
}
6464

65-
abstract String calculateJdbcPlaceholder(SqlColumn<?> column, String parameterName);
65+
private String calculateJdbcPlaceholder(SqlColumn<?> column, String parameterName) {
66+
return column.renderingStrategy().orElse(renderingStrategy)
67+
.getRecordBasedInsertBinding(column, prefix, parameterName);
68+
}
6669
}

src/main/java/org/mybatis/dynamic/sql/insert/render/BatchValuePhraseVisitor.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,11 @@
1515
*/
1616
package org.mybatis.dynamic.sql.insert.render;
1717

18-
import org.mybatis.dynamic.sql.SqlColumn;
1918
import org.mybatis.dynamic.sql.render.RenderingStrategy;
2019

2120
public class BatchValuePhraseVisitor extends AbstractMultiRowValuePhraseVisitor {
2221

2322
public BatchValuePhraseVisitor(RenderingStrategy renderingStrategy, String prefix) {
2423
super(renderingStrategy, prefix);
2524
}
26-
27-
@Override
28-
String calculateJdbcPlaceholder(SqlColumn<?> column, String parameterName) {
29-
return column.renderingStrategy().orElse(renderingStrategy)
30-
.getFormattedJdbcPlaceholder(column, prefix, parameterName);
31-
}
3225
}

src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,11 @@
1515
*/
1616
package org.mybatis.dynamic.sql.insert.render;
1717

18-
import org.mybatis.dynamic.sql.SqlColumn;
1918
import org.mybatis.dynamic.sql.render.RenderingStrategy;
2019

2120
public class MultiRowValuePhraseVisitor extends AbstractMultiRowValuePhraseVisitor {
2221

2322
public MultiRowValuePhraseVisitor(RenderingStrategy renderingStrategy, String prefix) {
2423
super(renderingStrategy, prefix);
2524
}
26-
27-
@Override
28-
String calculateJdbcPlaceholder(SqlColumn<?> column, String parameterName) {
29-
return column.renderingStrategy().orElse(renderingStrategy)
30-
.getMultiRowFormattedJdbcPlaceholder(column, prefix, parameterName);
31-
}
3225
}

src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,6 @@ public Optional<FieldAndValueAndParameters> visit(PropertyWhenPresentMapping map
7373

7474
private String calculateJdbcPlaceholder(SqlColumn<?> column, String parameterName) {
7575
return column.renderingStrategy().orElse(renderingStrategy)
76-
.getFormattedJdbcPlaceholder(column, "row", parameterName); //$NON-NLS-1$
76+
.getRecordBasedInsertBinding(column, "row", parameterName); //$NON-NLS-1$
7777
}
7878
}

src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121

2222
/**
2323
* A rendering strategy is used to generate a platform specific binding.
24-
* <p>
25-
* Rendering strategies are used during the rendering phase of statement generation.
24+
*
25+
* <p>Rendering strategies are used during the rendering phase of statement generation.
2626
* All generated SQL statements include the generated statement itself, and a map of parameters that
2727
* should be bound to the statement at execution time. For example, a generated select statement may
2828
* look like this when rendered for MyBatis:
29-
* <p>
30-
* <code>select foo from bar where id = #{parameters.p1,jdbcType=INTEGER}</code>
31-
* <p>
32-
* In this case, the binding is <code>#{parameters.p1,jdbcType=INTEGER}</code>. MyBatis knows how to interpret this
29+
*
30+
* <p><code>select foo from bar where id = #{parameters.p1,jdbcType=INTEGER}</code>
31+
*
32+
* <p>In this case, the binding is <code>#{parameters.p1,jdbcType=INTEGER}</code>. MyBatis knows how to interpret this
3333
* binding - it will look for a value in the <code>parameters.p1</code> property of the parameter object
3434
* passed to the statement and bind it as a prepared statement parameter when executing the statement.
3535
*/
@@ -42,61 +42,62 @@ public String formatParameterMapKey(AtomicInteger sequence) {
4242

4343
/**
4444
* This method generates a binding for a parameter to a placeholder in a generated SQL statement.
45-
* <p>
46-
* This binding is appropriate when there can be a mapping between a parameter and a known target column,
45+
*
46+
* <p>This binding is appropriate when there can be a mapping between a parameter and a known target column,
4747
* In MyBatis, the binding can specify type information based on the column. The bindings are specific
4848
* to the target framework.
49-
* <p>
50-
* For MyBatis, a binding looks like this: "#{prefix.parameterName,jdbcType=xxx,typeHandler=xxx,javaType=xxx}"
51-
* <p>
52-
* For Spring, a binding looks like this: ":parameterName"
49+
*
50+
* <p>For MyBatis, a binding looks like this: "#{prefix.parameterName,jdbcType=xxx,typeHandler=xxx,javaType=xxx}"
51+
*
52+
* <p>For Spring, a binding looks like this: ":parameterName"
5353
*
5454
* @param column column definition used for generating type details in a MyBatis binding. Ignored for Spring.
5555
* @param prefix parameter prefix used for locating the parameters in a SQL provider object. Typically, will be
5656
* {@link RenderingStrategy#DEFAULT_PARAMETER_PREFIX}. This is ignored for Spring.
5757
* @param parameterName name of the parameter. Typically generated by calling
58-
* {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
58+
* {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
5959
* @return the generated binding
6060
*/
6161
public abstract String getFormattedJdbcPlaceholder(BindableColumn<?> column, String prefix, String parameterName);
6262

6363
/**
6464
* This method generates a binding for a parameter to a placeholder in a generated SQL statement.
65-
* <p>
66-
* This binding is appropriate when the parameter is bound to placeholder that is not a known column (such as
65+
*
66+
* <p>This binding is appropriate when the parameter is bound to placeholder that is not a known column (such as
6767
* a limit or offset parameter). The bindings are specific to the target framework.
68-
* <p>
69-
* For MyBatis, a binding looks like this: "#{prefix.parameterName}"
70-
* <p>
71-
* For Spring, a binding looks like this: ":parameterName"
68+
*
69+
* <p>For MyBatis, a binding looks like this: "#{prefix.parameterName}"
70+
*
71+
* <p>For Spring, a binding looks like this: ":parameterName"
7272
*
7373
* @param prefix parameter prefix used for locating the parameters in a SQL provider object. Typically, will be
7474
* {@link RenderingStrategy#DEFAULT_PARAMETER_PREFIX}. This is ignored for Spring.
7575
* @param parameterName name of the parameter. Typically generated by calling
76-
* {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
76+
* {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
7777
* @return the generated binding
7878
*/
7979
public abstract String getFormattedJdbcPlaceholder(String prefix, String parameterName);
8080

8181
/**
82-
* This method generates a binding for a parameter to a placeholder in a generated multirow insert statement.
83-
* <p>
84-
* This binding is specifically for use with a multirow insert. The Spring implementation changes the binding
85-
* to match values expected for a multirow insert statement. For MyBatis, the binding is the same
82+
* This method generates a binding for a parameter to a placeholder in a record based insert statement.
83+
*
84+
* <p>This binding is specifically for use with insert, batch insert, and multirow insert statements.
85+
* These statements bind parameters to properties of a row class. The Spring implementation changes the binding
86+
* to match values expected for a these insert statements. For MyBatis, the binding is the same
8687
* as {@link RenderingStrategy#getFormattedJdbcPlaceholder(BindableColumn, String, String)}.
87-
* <p>
88-
* For MyBatis, a binding looks like this: "#{prefix.parameterName,jdbcType=xxx,typeHandler=xxx,javaType=xxx}"
89-
* <p>
90-
* For Spring, a binding looks like this: ":prefix.parameterName"
88+
*
89+
* <p>For MyBatis, a binding looks like this: "#{prefix.parameterName,jdbcType=xxx,typeHandler=xxx,javaType=xxx}"
90+
*
91+
* <p>For Spring, a binding looks like this: ":prefix.parameterName"
9192
*
9293
* @param column column definition used for generating type details in a MyBatis binding. Ignored for Spring.
9394
* @param prefix parameter prefix used for locating the parameters in a SQL provider object. Typically, will be
94-
* {@link RenderingStrategy#DEFAULT_PARAMETER_PREFIX}. This is ignored for Spring.
95+
* {@link RenderingStrategy#DEFAULT_PARAMETER_PREFIX}. This is ignored for Spring.
9596
* @param parameterName name of the parameter. Typically generated by calling
96-
* {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
97+
* {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
9798
* @return the generated binding
9899
*/
99-
public String getMultiRowFormattedJdbcPlaceholder(BindableColumn<?> column, String prefix, String parameterName) {
100+
public String getRecordBasedInsertBinding(BindableColumn<?> column, String prefix, String parameterName) {
100101
return getFormattedJdbcPlaceholder(column, prefix, parameterName);
101102
}
102103
}

src/main/java/org/mybatis/dynamic/sql/render/SpringNamedParameterRenderingStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public String getFormattedJdbcPlaceholder(String prefix, String parameterName) {
3030
}
3131

3232
@Override
33-
public String getMultiRowFormattedJdbcPlaceholder(BindableColumn<?> column, String prefix, String parameterName) {
33+
public String getRecordBasedInsertBinding(BindableColumn<?> column, String prefix, String parameterName) {
3434
return ":" + prefix + "." + parameterName; //$NON-NLS-1$ //$NON-NLS-2$
3535
}
3636
}
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.util.spring;
17+
18+
import java.util.List;
19+
import java.util.stream.Collectors;
20+
21+
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
22+
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
23+
24+
/**
25+
* Utility class for converting a list of rows to an array or SqlParameterSources.
26+
*
27+
* <p>This class is necessary due to the way that the library generates bindings for batch insert
28+
* statements. The bindings will be of the form <code>:row.propertyName</code>. The <code>createBatch</code> method
29+
* in this class will wrap all input rows in a class - RowHolder - with a single property named "row".
30+
* This will allow the generated bindings to function properly with a Spring batch insert.
31+
*/
32+
public class BatchInsertUtility {
33+
private BatchInsertUtility() {}
34+
35+
public static <T> SqlParameterSource[] createBatch(List<T> rows) {
36+
List<RowHolder<T>> tt = rows.stream()
37+
.map(RowHolder::new)
38+
.collect(Collectors.toList());
39+
40+
return SqlParameterSourceUtils.createBatch(tt);
41+
}
42+
43+
public static class RowHolder<T> {
44+
private final T row;
45+
46+
public RowHolder(T row) {
47+
this.row = row;
48+
}
49+
50+
public T getRow() {
51+
return row;
52+
}
53+
}
54+
}

src/main/java/org/mybatis/dynamic/sql/util/spring/NamedParameterJdbcTemplateExtensions.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
4141
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
4242
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
43-
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
44-
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
4543
import org.springframework.jdbc.support.KeyHolder;
4644

4745
public class NamedParameterJdbcTemplateExtensions {
@@ -90,7 +88,7 @@ public <T> int insert(Buildable<InsertModel<T>> insertStatement) {
9088

9189
public <T> int insert(InsertStatementProvider<T> insertStatement) {
9290
return template.update(insertStatement.getInsertStatement(),
93-
new BeanPropertySqlParameterSource(insertStatement.getRow()));
91+
new BeanPropertySqlParameterSource(insertStatement));
9492
}
9593

9694
public <T> int insert(Buildable<InsertModel<T>> insertStatement, KeyHolder keyHolder) {
@@ -99,16 +97,16 @@ public <T> int insert(Buildable<InsertModel<T>> insertStatement, KeyHolder keyHo
9997

10098
public <T> int insert(InsertStatementProvider<T> insertStatement, KeyHolder keyHolder) {
10199
return template.update(insertStatement.getInsertStatement(),
102-
new BeanPropertySqlParameterSource(insertStatement.getRow()), keyHolder);
100+
new BeanPropertySqlParameterSource(insertStatement), keyHolder);
103101
}
104102

105103
public <T> int[] insertBatch(Buildable<BatchInsertModel<T>> insertStatement) {
106104
return insertBatch(insertStatement.build().render(RenderingStrategies.SPRING_NAMED_PARAMETER));
107105
}
108106

109107
public <T> int[] insertBatch(BatchInsert<T> insertStatement) {
110-
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(insertStatement.getRecords());
111-
return template.batchUpdate(insertStatement.getInsertStatementSQL(), batch);
108+
return template.batchUpdate(insertStatement.getInsertStatementSQL(),
109+
BatchInsertUtility.createBatch(insertStatement.getRecords()));
112110
}
113111

114112
public <T> int insertMultiple(Buildable<MultiRowInsertModel<T>> insertStatement) {

src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/spring/NamedParameterJdbcTemplateExtensions.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ import org.mybatis.dynamic.sql.util.kotlin.KotlinMultiRowInsertCompleter
3636
import org.mybatis.dynamic.sql.util.kotlin.MyBatisDslMarker
3737
import org.mybatis.dynamic.sql.util.kotlin.SelectCompleter
3838
import org.mybatis.dynamic.sql.util.kotlin.UpdateCompleter
39+
import org.mybatis.dynamic.sql.util.spring.BatchInsertUtility
3940
import org.springframework.dao.EmptyResultDataAccessException
4041
import org.springframework.jdbc.core.RowMapper
4142
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource
4243
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource
4344
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
44-
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils
4545
import org.springframework.jdbc.support.KeyHolder
4646
import java.sql.ResultSet
4747
import kotlin.reflect.KClass
@@ -66,7 +66,7 @@ fun NamedParameterJdbcTemplate.deleteFrom(table: SqlTable, completer: DeleteComp
6666

6767
// batch insert
6868
fun <T> NamedParameterJdbcTemplate.insertBatch(insertStatement: BatchInsert<T>): IntArray =
69-
batchUpdate(insertStatement.insertStatementSQL, SqlParameterSourceUtils.createBatch(insertStatement.records))
69+
batchUpdate(insertStatement.insertStatementSQL, BatchInsertUtility.createBatch(insertStatement.records))
7070

7171
fun <T : Any> NamedParameterJdbcTemplate.insertBatch(
7272
vararg records: T,
@@ -82,13 +82,13 @@ fun <T : Any> NamedParameterJdbcTemplate.insertBatch(
8282

8383
// single row insert
8484
fun <T> NamedParameterJdbcTemplate.insert(insertStatement: InsertStatementProvider<T>): Int =
85-
update(insertStatement.insertStatement, BeanPropertySqlParameterSource(insertStatement.row))
85+
update(insertStatement.insertStatement, BeanPropertySqlParameterSource(insertStatement))
8686

8787
fun <T> NamedParameterJdbcTemplate.insert(
8888
insertStatement: InsertStatementProvider<T>,
8989
keyHolder: KeyHolder
9090
): Int =
91-
update(insertStatement.insertStatement, BeanPropertySqlParameterSource(insertStatement.row), keyHolder)
91+
update(insertStatement.insertStatement, BeanPropertySqlParameterSource(insertStatement), keyHolder)
9292

9393
fun <T : Any> NamedParameterJdbcTemplate.insert(row: T, completer: KotlinInsertCompleter<T>): Int =
9494
insert(org.mybatis.dynamic.sql.util.kotlin.spring.insert(row, completer))

src/test/java/examples/generated/always/spring/SpringTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@
3838
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
3939
import org.mybatis.dynamic.sql.util.Buildable;
4040
import org.mybatis.dynamic.sql.util.spring.NamedParameterJdbcTemplateExtensions;
41+
import org.mybatis.dynamic.sql.util.spring.BatchInsertUtility;
4142
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
4243
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
4344
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
4445
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
45-
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
4646
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
4747
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
4848
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@@ -142,7 +142,7 @@ void testInsert() {
142142
.build()
143143
.render(RenderingStrategies.SPRING_NAMED_PARAMETER);
144144

145-
SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(insertStatement.getRow());
145+
SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(insertStatement);
146146
KeyHolder keyHolder = new GeneratedKeyHolder();
147147

148148
int rows = template.update(insertStatement.getInsertStatement(), parameterSource, keyHolder);
@@ -241,7 +241,7 @@ record = new GeneratedAlwaysRecord();
241241
record.setLastName("Smith");
242242
records.add(record);
243243

244-
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(records);
244+
SqlParameterSource[] batch = BatchInsertUtility.createBatch(records);
245245

246246
BatchInsert<GeneratedAlwaysRecord> batchInsert = insertBatch(records)
247247
.into(generatedAlways)

0 commit comments

Comments
 (0)