Skip to content

Commit 52d939d

Browse files
authored
Merge pull request #227 from jeffgbutler/gh-131
Major Enhancements for Spring
2 parents 96c1889 + 094c62d commit 52d939d

File tree

91 files changed

+3378
-683
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+3378
-683
lines changed

CHANGELOG.md

+16-6
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,28 @@ GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=miles
88

99
### General Announcements
1010

11-
This release includes a significant refactoring of the classes in the "org.mybatis.dynamic.sql.select.function" package. The new classes are more consistent and flexible and should be compatible with existing code at the source level (meaning that code should be recompiled for the new version of the library).
11+
This release includes major improvements to the Spring support in the library. Spring support is now functionally equivalent to MyBatis support.
1212

13-
If you have written your own set of functions to extend the library, you will notice that the base classes 'AbstractFunction" and "AbstractMultipleColumnArithmeticFunction" are now deprecated. Their replacement classes are "AbstractUniTypeFunction" and "OperatorFunction" respectively.
13+
This release includes a significant refactoring of the classes in the "org.mybatis.dynamic.sql.select.function" package. The new classes are more consistent and flexible and should be compatible with existing code at the source level (meaning code should be recompiled for the new version of the library). If you have written your own set of functions to extend the library, you will notice that the base classes 'AbstractFunction" and "AbstractMultipleColumnArithmeticFunction" are now deprecated. Their replacement classes are "AbstractUniTypeFunction" and "OperatorFunction" respectively.
14+
15+
With this release, we deprecated several insert methods because they were inconsistently named or awkward. All deprecated methods have documented direct replacements.
16+
17+
In the next major release of the library, all deprecated code will be removed.
1418

1519
### Added
1620

1721
- Added a general insert statement that does not require a separate record class to hold values for the insert. ([#201](https://github.com/mybatis/mybatis-dynamic-sql/issues/201))
18-
- Added the capability to specify a rendering strategy on a column to override the defaut rendering strategy for a statement. This will allow certain edge cases where a parameter marker needs to be formatted in a unique way (for example, "::jsonb" needs to be added to parameter markers for JSON fields in PostgreSQL) ([#200](https://github.com/mybatis/mybatis-dynamic-sql/issues/200))
22+
- Added the capability to specify a rendering strategy on a column to override the default rendering strategy for a statement. This will allow certain edge cases where a parameter marker needs to be formatted uniquely (for example, "::jsonb" needs to be added to parameter markers for JSON fields in PostgreSQL) ([#200](https://github.com/mybatis/mybatis-dynamic-sql/issues/200))
1923
- Added the ability to write a function that will change the column data type ([#197](https://github.com/mybatis/mybatis-dynamic-sql/issues/197))
2024
- Added the `applyOperator` function to make it easy to use non-standard database operators in expressions ([#220](https://github.com/mybatis/mybatis-dynamic-sql/issues/220))
21-
- Added convenience methods for count(column) and count(distinct column)([#221](https://github.com/mybatis/mybatis-dynamic-sql/issues/221))
22-
- Added support for union queries in Kotlin([#187](https://github.com/mybatis/mybatis-dynamic-sql/issues/187))
25+
- Added convenience methods for count(column) and count(distinct column) ([#221](https://github.com/mybatis/mybatis-dynamic-sql/issues/221))
26+
- Added support for union queries in Kotlin ([#187](https://github.com/mybatis/mybatis-dynamic-sql/issues/187))
27+
- Many enhancements for Spring including:
28+
- Fixed a bug where multi-row insert statements did not render properly for Spring ([#224](https://github.com/mybatis/mybatis-dynamic-sql/issues/224))
29+
- Added support for a parameter type converter for use cases where the Java type of a column does not match the database column type ([#131](https://github.com/mybatis/mybatis-dynamic-sql/issues/131))
30+
- Added a utility class which simplifies the use of the named parameter JDBC template for Java code - `org.mybatis.dynamic.sql.util.spring.NamedParameterJdbcTemplateExtensions`
31+
- Added support for general inserts, multi-row inserts, batch inserts in the Kotlin DSL for Spring ([#225](https://github.com/mybatis/mybatis-dynamic-sql/issues/225))
32+
- Added support for generated keys in the Kotlin DSL for Spring ([#226](https://github.com/mybatis/mybatis-dynamic-sql/issues/226))
2333

2434
## Release 1.1.4 - November 23, 2019
2535

@@ -53,7 +63,7 @@ GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=miles
5363
### Added
5464

5565
- Changed the public SQLBuilder API to accept Collection instead of List for in conditions and batch record inserts. This should have no impact on existing code, but allow for some future flexibility ([#88](https://github.com/mybatis/mybatis-dynamic-sql/pull/88))
56-
- Added the ability have have table catalog and/or schema calculated at query runtime. This is useful for situations where there are different database schemas for different environments, or in some sharding situations ([#92](https://github.com/mybatis/mybatis-dynamic-sql/pull/92))
66+
- Added the ability to have table catalog and/or schema calculated at runtime. This is useful for situations where there are different database schemas for different environments, or in some sharding situations ([#92](https://github.com/mybatis/mybatis-dynamic-sql/pull/92))
5767
- Add support for paging queries with "offset" and "fetch first" - this seems to be standard on most databases ([#96](https://github.com/mybatis/mybatis-dynamic-sql/pull/96))
5868
- Added the ability to call a builder method on any intermediate object in a select statement and receive a fully rendered statement. This makes it easier to build very dynamic queries ([#106](https://github.com/mybatis/mybatis-dynamic-sql/pull/106))
5969
- Add the ability to modify values on any condition before they are placed in the parameter map ([#105](https://github.com/mybatis/mybatis-dynamic-sql/issues/105))

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@
2626
*
2727
* @author Jeff Butler
2828
*
29-
* @param <T> - even though the type is not directly used in this class,
30-
* it is used by the compiler to match columns with conditions so it should
31-
* not be removed.
29+
* @param <T> - the Java type that corresponds to this column
3230
*/
3331
public interface BindableColumn<T> extends BasicColumn {
3432

@@ -49,4 +47,8 @@ default Optional<String> typeHandler() {
4947
default Optional<RenderingStrategy> renderingStrategy() {
5048
return Optional.empty();
5149
}
50+
51+
default Object convertParameterType(T value) {
52+
return value;
53+
}
5254
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* Copyright 2016-2020 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+
* http://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+
/**
19+
* A parameter type converter is used to change a parameter value from one type to another
20+
* during statement rendering and before the parameter is placed into the parameter map. This can be used
21+
* to somewhat mimic the function of a MyBatis type handler for runtimes such as Spring that don't have
22+
* a corresponding concept.
23+
*
24+
* <p>Since Spring does not have the concept of type handlers, it is a best practice to only use
25+
* Java data types that have a clear correlation to SQL data types (for example Java String correlates
26+
* automatically with VARCHAR). Using a parameter type converter will allow you to use data types in your
27+
* model classes that would otherwise be difficult to use with Spring.
28+
*
29+
* <p>A parameter type converter is associated with a SqlColumn.
30+
*
31+
* <p>This interface is based on Spring's general Converter interface and is intentionally compatible with it.
32+
* Existing converters may be reused if they are marked with this additional interface.
33+
*
34+
* <p>The converter is only used for parameters in a parameter map. It is not used for result set processing.
35+
* It is also not used for insert statements that are based on an external record class. The converter will be called
36+
* in the following circumstances:
37+
*
38+
* <ul>
39+
* <li>Parameters in a general insert statement (for the Value and ValueWhenPresent mappings)</li>
40+
* <li>Parameters in an update statement (for the Value and ValueWhenPresent mappings)</li>
41+
* <li>Parameters in a where clause in any statement (for conditions that accept a value or multiple values)</li>
42+
* </ul>
43+
*
44+
* @param <S> Source Type
45+
* @param <T> Target Type
46+
*
47+
* @see SqlColumn
48+
* @author Jeff Butler
49+
* @since 1.1.5
50+
*/
51+
@FunctionalInterface
52+
public interface ParameterTypeConverter<S, T> {
53+
T convert(S source);
54+
}

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

+68-5
Original file line numberDiff line numberDiff line change
@@ -136,21 +136,84 @@ static DeleteDSL<DeleteModel> deleteFrom(SqlTable table) {
136136
static <T> InsertDSL.IntoGatherer<T> insert(T record) {
137137
return InsertDSL.insert(record);
138138
}
139-
139+
140+
/**
141+
* Insert a Batch of records. The model object is structured to support bulk inserts with JDBC batch support.
142+
*
143+
* @param records records to insert
144+
* @param <T> the type of record to insert
145+
* @return the next step in the DSL
146+
* @deprecated Use {@link SqlBuilder#insertBatch(Object[])} instead
147+
*/
148+
@Deprecated
140149
@SafeVarargs
141150
static <T> BatchInsertDSL.IntoGatherer<T> insert(T...records) {
142-
return BatchInsertDSL.insert(records);
151+
return insertBatch(records);
143152
}
144-
153+
154+
/**
155+
* Insert a Batch of records. The model object is structured to support bulk inserts with JDBC batch support.
156+
*
157+
* @param records records to insert
158+
* @param <T> the type of record to insert
159+
* @return the next step in the DSL
160+
* @deprecated Use {@link SqlBuilder#insertBatch(Collection)} instead
161+
*/
162+
@Deprecated
145163
static <T> BatchInsertDSL.IntoGatherer<T> insert(Collection<T> records) {
164+
return insertBatch(records);
165+
}
166+
167+
/**
168+
* Insert a Batch of records. The model object is structured to support bulk inserts with JDBC batch support.
169+
*
170+
* @param records records to insert
171+
* @param <T> the type of record to insert
172+
* @return the next step in the DSL
173+
*/
174+
@SafeVarargs
175+
static <T> BatchInsertDSL.IntoGatherer<T> insertBatch(T...records) {
146176
return BatchInsertDSL.insert(records);
147177
}
148-
178+
179+
/**
180+
* Insert a Batch of records. The model object is structured to support bulk inserts with JDBC batch support.
181+
*
182+
* @param records records to insert
183+
* @param <T> the type of record to insert
184+
* @return the next step in the DSL
185+
*/
186+
static <T> BatchInsertDSL.IntoGatherer<T> insertBatch(Collection<T> records) {
187+
return BatchInsertDSL.insert(records);
188+
}
189+
190+
/**
191+
* Insert multiple records in a single statement. The model object is structured as a single insert statement with
192+
* multiple values clauses. This statement is suitable for use with a small number of records. It is not suitable
193+
* for large bulk inserts as it is possible to exceed the limit of parameter markers in a prepared statement.
194+
*
195+
* <p>For large bulk inserts, see {@link SqlBuilder#insertBatch(Object[])}
196+
*
197+
* @param records records to insert
198+
* @param <T> the type of record to insert
199+
* @return the next step in the DSL
200+
*/
149201
@SafeVarargs
150202
static <T> MultiRowInsertDSL.IntoGatherer<T> insertMultiple(T...records) {
151203
return MultiRowInsertDSL.insert(records);
152204
}
153-
205+
206+
/**
207+
* Insert multiple records in a single statement. The model object is structured as a single insert statement with
208+
* multiple values clauses. This statement is suitable for use with a small number of records. It is not suitable
209+
* for large bulk inserts as it is possible to exceed the limit of parameter markers in a prepared statement.
210+
*
211+
* <p>For large bulk inserts, see {@link SqlBuilder#insertBatch(Collection)}
212+
*
213+
* @param records records to insert
214+
* @param <T> the type of record to insert
215+
* @return the next step in the DSL
216+
*/
154217
static <T> MultiRowInsertDSL.IntoGatherer<T> insertMultiple(Collection<T> records) {
155218
return MultiRowInsertDSL.insert(records);
156219
}

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

+38-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Objects;
2020
import java.util.Optional;
2121

22+
import org.jetbrains.annotations.NotNull;
2223
import org.mybatis.dynamic.sql.render.RenderingStrategy;
2324
import org.mybatis.dynamic.sql.render.TableAliasCalculator;
2425

@@ -31,22 +32,24 @@ public class SqlColumn<T> implements BindableColumn<T>, SortSpecification {
3132
protected String alias;
3233
protected String typeHandler;
3334
protected RenderingStrategy renderingStrategy;
34-
35+
protected ParameterTypeConverter<T, ?> parameterTypeConverter;
36+
3537
private SqlColumn(Builder builder) {
3638
name = Objects.requireNonNull(builder.name);
3739
jdbcType = builder.jdbcType;
3840
table = Objects.requireNonNull(builder.table);
3941
typeHandler = builder.typeHandler;
4042
}
41-
42-
protected SqlColumn(SqlColumn<?> sqlColumn) {
43+
44+
protected SqlColumn(SqlColumn<T> sqlColumn) {
4345
name = sqlColumn.name;
4446
table = sqlColumn.table;
4547
jdbcType = sqlColumn.jdbcType;
4648
isDescending = sqlColumn.isDescending;
4749
alias = sqlColumn.alias;
4850
typeHandler = sqlColumn.typeHandler;
4951
renderingStrategy = sqlColumn.renderingStrategy;
52+
parameterTypeConverter = sqlColumn.parameterTypeConverter;
5053
}
5154

5255
public String name() {
@@ -72,6 +75,11 @@ public Optional<String> typeHandler() {
7275
return Optional.ofNullable(typeHandler);
7376
}
7477

78+
@Override
79+
public Object convertParameterType(T value) {
80+
return parameterTypeConverter == null ? value : parameterTypeConverter.convert(value);
81+
}
82+
7583
@Override
7684
public SortSpecification descending() {
7785
SqlColumn<T> column = new SqlColumn<>(this);
@@ -107,19 +115,42 @@ public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
107115
public Optional<RenderingStrategy> renderingStrategy() {
108116
return Optional.ofNullable(renderingStrategy);
109117
}
110-
111-
public SqlColumn<T> withTypeHandler(String typeHandler) {
112-
SqlColumn<T> column = new SqlColumn<>(this);
118+
119+
@NotNull
120+
public <S> SqlColumn<S> withTypeHandler(String typeHandler) {
121+
SqlColumn<S> column = copy();
113122
column.typeHandler = typeHandler;
114123
return column;
115124
}
116125

126+
@NotNull
117127
public <S> SqlColumn<S> withRenderingStrategy(RenderingStrategy renderingStrategy) {
118-
SqlColumn<S> column = new SqlColumn<>(this);
128+
SqlColumn<S> column = copy();
119129
column.renderingStrategy = renderingStrategy;
120130
return column;
121131
}
122132

133+
@NotNull
134+
public <S> SqlColumn<S> withParameterTypeConverter(ParameterTypeConverter<S, ?> parameterTypeConverter) {
135+
SqlColumn<S> column = copy();
136+
column.parameterTypeConverter = parameterTypeConverter;
137+
return column;
138+
}
139+
140+
/**
141+
* This method helps us tell a bit of fiction to the Java compiler. Java, for better or worse,
142+
* does not carry generic type information through chained methods. We want to enable method
143+
* chaining in the "with" methods. With this bit of fiction, we force the compiler to delay type
144+
* inference to the last method in the chain.
145+
*
146+
* @param <S> the type. Will be the same as T for this usage.
147+
* @return a new SqlColumn of type S (S is the same as T)
148+
*/
149+
@SuppressWarnings("unchecked")
150+
private <S> SqlColumn<S> copy() {
151+
return new SqlColumn<>((SqlColumn<S>) this);
152+
}
153+
123154
private String applyTableAlias(String tableAlias) {
124155
return tableAlias + "." + name(); //$NON-NLS-1$
125156
}

src/main/java/org/mybatis/dynamic/sql/insert/AbstractMultiRowInsertModel.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2020 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.

src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2020 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.
@@ -23,12 +23,13 @@
2323
import org.mybatis.dynamic.sql.SqlColumn;
2424
import org.mybatis.dynamic.sql.SqlTable;
2525
import org.mybatis.dynamic.sql.util.AbstractColumnMapping;
26+
import org.mybatis.dynamic.sql.util.Buildable;
2627
import org.mybatis.dynamic.sql.util.ConstantMapping;
2728
import org.mybatis.dynamic.sql.util.NullMapping;
2829
import org.mybatis.dynamic.sql.util.PropertyMapping;
2930
import org.mybatis.dynamic.sql.util.StringConstantMapping;
3031

31-
public class BatchInsertDSL<T> {
32+
public class BatchInsertDSL<T> implements Buildable<BatchInsertModel<T>> {
3233

3334
private Collection<T> records;
3435
private SqlTable table;
@@ -43,6 +44,7 @@ public <F> ColumnMappingFinisher<F> map(SqlColumn<F> column) {
4344
return new ColumnMappingFinisher<>(column);
4445
}
4546

47+
@Override
4648
public BatchInsertModel<T> build() {
4749
return BatchInsertModel.withRecords(records)
4850
.withTable(table)

src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertModel.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2016-2019 the original author or authors.
2+
* Copyright 2016-2020 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.
@@ -17,6 +17,7 @@
1717

1818
import java.util.Collection;
1919

20+
import org.jetbrains.annotations.NotNull;
2021
import org.mybatis.dynamic.sql.insert.render.BatchInsert;
2122
import org.mybatis.dynamic.sql.insert.render.BatchInsertRenderer;
2223
import org.mybatis.dynamic.sql.render.RenderingStrategy;
@@ -27,6 +28,7 @@ private BatchInsertModel(Builder<T> builder) {
2728
super(builder);
2829
}
2930

31+
@NotNull
3032
public BatchInsert<T> render(RenderingStrategy renderingStrategy) {
3133
return BatchInsertRenderer.withBatchInsertModel(this)
3234
.withRenderingStrategy(renderingStrategy)

0 commit comments

Comments
 (0)