Skip to content

Major Enhancements for Spring #227

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1d27098
Return null on missing record rather than throwing exception
jeffgbutler Jul 29, 2020
bed8e84
Use a common row handler
jeffgbutler Jul 29, 2020
fb8a122
Initial work on parameter type converter
jeffgbutler Jul 29, 2020
055b3ed
Refactoring usage of parameter value converter
jeffgbutler Jul 30, 2020
bdb2e58
Add support for count statements
jeffgbutler Jul 30, 2020
ee35e82
Formatting
jeffgbutler Jul 30, 2020
bf0c57a
Add an extension like class for Spring support
jeffgbutler Jul 30, 2020
6c2b614
Add tests for the improved Spring support
jeffgbutler Jul 30, 2020
cfedb8c
Fix Multi-Row Inserts in Spring
jeffgbutler Jul 30, 2020
b670991
Add utility support for batch inserts
jeffgbutler Jul 30, 2020
e74c8eb
Mappings don't need to be generic
jeffgbutler Jul 30, 2020
9af5955
Formatting and style
jeffgbutler Jul 30, 2020
45ccf0c
Spelling errors
jeffgbutler Jul 30, 2020
3c4d63c
Checkstyle
jeffgbutler Jul 30, 2020
1e7d50d
Remove some cognitive complexity
jeffgbutler Jul 30, 2020
1cc137f
Removing more useless indirection
jeffgbutler Jul 30, 2020
dda1245
Polishing
jeffgbutler Jul 30, 2020
58c8521
Doc updates
jeffgbutler Jul 30, 2020
b7b4206
Parameter type converter can be compatible with Spring converters
jeffgbutler Jul 30, 2020
6cb91e5
Convert the Kotlin tests to use the new type handler support
jeffgbutler Jul 31, 2020
6a57b76
Implement multi row insert for Kotlin
jeffgbutler Jul 31, 2020
2f3db58
Fix the awkward single row insert for Kotlin
jeffgbutler Jul 31, 2020
59ba4fd
Start work on KeyHolder support in Kotlin
jeffgbutler Aug 2, 2020
6b4b900
Basic support for generated keys in Spring
jeffgbutler Aug 4, 2020
dfe5d5c
Batch insert for Kotlin
jeffgbutler Aug 4, 2020
1eaed6a
Add KeyHolder support to the template direct inserts
jeffgbutler Aug 4, 2020
d228b8d
Formatting
jeffgbutler Aug 4, 2020
0b39d41
Make sure the type converters work with nulls
jeffgbutler Aug 4, 2020
3e8c9c4
Make the type converters more Kotlin-ish
jeffgbutler Aug 4, 2020
685e797
Another Type Handler Test
jeffgbutler Aug 4, 2020
23b0a81
Polishing
jeffgbutler Aug 5, 2020
1246416
Rename method for consistency
jeffgbutler Aug 7, 2020
0696425
Deprecate old insert method in favor of insertBatch
jeffgbutler Aug 7, 2020
c2c3e12
Better Kotlin Style
jeffgbutler Aug 7, 2020
bce715b
Copyrights
jeffgbutler Aug 7, 2020
1c79ee0
Updates to Spring Documentation
jeffgbutler Aug 9, 2020
0d6c42e
Add an explanation of the changing types in the "with" methods
jeffgbutler Aug 10, 2020
50a301b
Update the Kotlin DSL for KeyHolder Support
jeffgbutler Aug 10, 2020
bcc9d27
Consistent Method Names
jeffgbutler Aug 10, 2020
094c62d
Documentation updates
jeffgbutler Aug 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,28 @@ GitHub milestone: [https://github.com/mybatis/mybatis-dynamic-sql/issues?q=miles

### General Announcements

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).
This release includes major improvements to the Spring support in the library. Spring support is now functionally equivalent to MyBatis support.

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.
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.

With this release, we deprecated several insert methods because they were inconsistently named or awkward. All deprecated methods have documented direct replacements.

In the next major release of the library, all deprecated code will be removed.

### Added

- 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))
- 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))
- 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))
- Added the ability to write a function that will change the column data type ([#197](https://github.com/mybatis/mybatis-dynamic-sql/issues/197))
- 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))
- Added convenience methods for count(column) and count(distinct column)([#221](https://github.com/mybatis/mybatis-dynamic-sql/issues/221))
- Added support for union queries in Kotlin([#187](https://github.com/mybatis/mybatis-dynamic-sql/issues/187))
- Added convenience methods for count(column) and count(distinct column) ([#221](https://github.com/mybatis/mybatis-dynamic-sql/issues/221))
- Added support for union queries in Kotlin ([#187](https://github.com/mybatis/mybatis-dynamic-sql/issues/187))
- Many enhancements for Spring including:
- Fixed a bug where multi-row insert statements did not render properly for Spring ([#224](https://github.com/mybatis/mybatis-dynamic-sql/issues/224))
- 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))
- Added a utility class which simplifies the use of the named parameter JDBC template for Java code - `org.mybatis.dynamic.sql.util.spring.NamedParameterJdbcTemplateExtensions`
- 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))
- Added support for generated keys in the Kotlin DSL for Spring ([#226](https://github.com/mybatis/mybatis-dynamic-sql/issues/226))

## Release 1.1.4 - November 23, 2019

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

- 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))
- 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))
- 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))
- 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))
- 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))
- 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))
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/org/mybatis/dynamic/sql/BindableColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
*
* @author Jeff Butler
*
* @param <T> - even though the type is not directly used in this class,
* it is used by the compiler to match columns with conditions so it should
* not be removed.
* @param <T> - the Java type that corresponds to this column
*/
public interface BindableColumn<T> extends BasicColumn {

Expand All @@ -49,4 +47,8 @@ default Optional<String> typeHandler() {
default Optional<RenderingStrategy> renderingStrategy() {
return Optional.empty();
}

default Object convertParameterType(T value) {
return value;
}
}
54 changes: 54 additions & 0 deletions src/main/java/org/mybatis/dynamic/sql/ParameterTypeConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright 2016-2020 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
*
* http://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;

/**
* A parameter type converter is used to change a parameter value from one type to another
* during statement rendering and before the parameter is placed into the parameter map. This can be used
* to somewhat mimic the function of a MyBatis type handler for runtimes such as Spring that don't have
* a corresponding concept.
*
* <p>Since Spring does not have the concept of type handlers, it is a best practice to only use
* Java data types that have a clear correlation to SQL data types (for example Java String correlates
* automatically with VARCHAR). Using a parameter type converter will allow you to use data types in your
* model classes that would otherwise be difficult to use with Spring.
*
* <p>A parameter type converter is associated with a SqlColumn.
*
* <p>This interface is based on Spring's general Converter interface and is intentionally compatible with it.
* Existing converters may be reused if they are marked with this additional interface.
*
* <p>The converter is only used for parameters in a parameter map. It is not used for result set processing.
* It is also not used for insert statements that are based on an external record class. The converter will be called
* in the following circumstances:
*
* <ul>
* <li>Parameters in a general insert statement (for the Value and ValueWhenPresent mappings)</li>
* <li>Parameters in an update statement (for the Value and ValueWhenPresent mappings)</li>
* <li>Parameters in a where clause in any statement (for conditions that accept a value or multiple values)</li>
* </ul>
*
* @param <S> Source Type
* @param <T> Target Type
*
* @see SqlColumn
* @author Jeff Butler
* @since 1.1.5
*/
@FunctionalInterface
public interface ParameterTypeConverter<S, T> {
T convert(S source);
}
73 changes: 68 additions & 5 deletions src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,21 +136,84 @@ static DeleteDSL<DeleteModel> deleteFrom(SqlTable table) {
static <T> InsertDSL.IntoGatherer<T> insert(T record) {
return InsertDSL.insert(record);
}


/**
* Insert a Batch of records. The model object is structured to support bulk inserts with JDBC batch support.
*
* @param records records to insert
* @param <T> the type of record to insert
* @return the next step in the DSL
* @deprecated Use {@link SqlBuilder#insertBatch(Object[])} instead
*/
@Deprecated
@SafeVarargs
static <T> BatchInsertDSL.IntoGatherer<T> insert(T...records) {
return BatchInsertDSL.insert(records);
return insertBatch(records);
}


/**
* Insert a Batch of records. The model object is structured to support bulk inserts with JDBC batch support.
*
* @param records records to insert
* @param <T> the type of record to insert
* @return the next step in the DSL
* @deprecated Use {@link SqlBuilder#insertBatch(Collection)} instead
*/
@Deprecated
static <T> BatchInsertDSL.IntoGatherer<T> insert(Collection<T> records) {
return insertBatch(records);
}

/**
* Insert a Batch of records. The model object is structured to support bulk inserts with JDBC batch support.
*
* @param records records to insert
* @param <T> the type of record to insert
* @return the next step in the DSL
*/
@SafeVarargs
static <T> BatchInsertDSL.IntoGatherer<T> insertBatch(T...records) {
return BatchInsertDSL.insert(records);
}


/**
* Insert a Batch of records. The model object is structured to support bulk inserts with JDBC batch support.
*
* @param records records to insert
* @param <T> the type of record to insert
* @return the next step in the DSL
*/
static <T> BatchInsertDSL.IntoGatherer<T> insertBatch(Collection<T> records) {
return BatchInsertDSL.insert(records);
}

/**
* Insert multiple records in a single statement. The model object is structured as a single insert statement with
* multiple values clauses. This statement is suitable for use with a small number of records. It is not suitable
* for large bulk inserts as it is possible to exceed the limit of parameter markers in a prepared statement.
*
* <p>For large bulk inserts, see {@link SqlBuilder#insertBatch(Object[])}
*
* @param records records to insert
* @param <T> the type of record to insert
* @return the next step in the DSL
*/
@SafeVarargs
static <T> MultiRowInsertDSL.IntoGatherer<T> insertMultiple(T...records) {
return MultiRowInsertDSL.insert(records);
}


/**
* Insert multiple records in a single statement. The model object is structured as a single insert statement with
* multiple values clauses. This statement is suitable for use with a small number of records. It is not suitable
* for large bulk inserts as it is possible to exceed the limit of parameter markers in a prepared statement.
*
* <p>For large bulk inserts, see {@link SqlBuilder#insertBatch(Collection)}
*
* @param records records to insert
* @param <T> the type of record to insert
* @return the next step in the DSL
*/
static <T> MultiRowInsertDSL.IntoGatherer<T> insertMultiple(Collection<T> records) {
return MultiRowInsertDSL.insert(records);
}
Expand Down
45 changes: 38 additions & 7 deletions src/main/java/org/mybatis/dynamic/sql/SqlColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Objects;
import java.util.Optional;

import org.jetbrains.annotations.NotNull;
import org.mybatis.dynamic.sql.render.RenderingStrategy;
import org.mybatis.dynamic.sql.render.TableAliasCalculator;

Expand All @@ -31,22 +32,24 @@ public class SqlColumn<T> implements BindableColumn<T>, SortSpecification {
protected String alias;
protected String typeHandler;
protected RenderingStrategy renderingStrategy;

protected ParameterTypeConverter<T, ?> parameterTypeConverter;

private SqlColumn(Builder builder) {
name = Objects.requireNonNull(builder.name);
jdbcType = builder.jdbcType;
table = Objects.requireNonNull(builder.table);
typeHandler = builder.typeHandler;
}
protected SqlColumn(SqlColumn<?> sqlColumn) {

protected SqlColumn(SqlColumn<T> sqlColumn) {
name = sqlColumn.name;
table = sqlColumn.table;
jdbcType = sqlColumn.jdbcType;
isDescending = sqlColumn.isDescending;
alias = sqlColumn.alias;
typeHandler = sqlColumn.typeHandler;
renderingStrategy = sqlColumn.renderingStrategy;
parameterTypeConverter = sqlColumn.parameterTypeConverter;
}

public String name() {
Expand All @@ -72,6 +75,11 @@ public Optional<String> typeHandler() {
return Optional.ofNullable(typeHandler);
}

@Override
public Object convertParameterType(T value) {
return parameterTypeConverter == null ? value : parameterTypeConverter.convert(value);
}

@Override
public SortSpecification descending() {
SqlColumn<T> column = new SqlColumn<>(this);
Expand Down Expand Up @@ -107,19 +115,42 @@ public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) {
public Optional<RenderingStrategy> renderingStrategy() {
return Optional.ofNullable(renderingStrategy);
}

public SqlColumn<T> withTypeHandler(String typeHandler) {
SqlColumn<T> column = new SqlColumn<>(this);

@NotNull
public <S> SqlColumn<S> withTypeHandler(String typeHandler) {
SqlColumn<S> column = copy();
column.typeHandler = typeHandler;
return column;
}

@NotNull
public <S> SqlColumn<S> withRenderingStrategy(RenderingStrategy renderingStrategy) {
SqlColumn<S> column = new SqlColumn<>(this);
SqlColumn<S> column = copy();
column.renderingStrategy = renderingStrategy;
return column;
}

@NotNull
public <S> SqlColumn<S> withParameterTypeConverter(ParameterTypeConverter<S, ?> parameterTypeConverter) {
SqlColumn<S> column = copy();
column.parameterTypeConverter = parameterTypeConverter;
return column;
}

/**
* This method helps us tell a bit of fiction to the Java compiler. Java, for better or worse,
* does not carry generic type information through chained methods. We want to enable method
* chaining in the "with" methods. With this bit of fiction, we force the compiler to delay type
* inference to the last method in the chain.
*
* @param <S> the type. Will be the same as T for this usage.
* @return a new SqlColumn of type S (S is the same as T)
*/
@SuppressWarnings("unchecked")
private <S> SqlColumn<S> copy() {
return new SqlColumn<>((SqlColumn<S>) this);
}

private String applyTableAlias(String tableAlias) {
return tableAlias + "." + name(); //$NON-NLS-1$
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2020 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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2020 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 @@ -23,12 +23,13 @@
import org.mybatis.dynamic.sql.SqlColumn;
import org.mybatis.dynamic.sql.SqlTable;
import org.mybatis.dynamic.sql.util.AbstractColumnMapping;
import org.mybatis.dynamic.sql.util.Buildable;
import org.mybatis.dynamic.sql.util.ConstantMapping;
import org.mybatis.dynamic.sql.util.NullMapping;
import org.mybatis.dynamic.sql.util.PropertyMapping;
import org.mybatis.dynamic.sql.util.StringConstantMapping;

public class BatchInsertDSL<T> {
public class BatchInsertDSL<T> implements Buildable<BatchInsertModel<T>> {

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

@Override
public BatchInsertModel<T> build() {
return BatchInsertModel.withRecords(records)
.withTable(table)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2020 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 @@ -17,6 +17,7 @@

import java.util.Collection;

import org.jetbrains.annotations.NotNull;
import org.mybatis.dynamic.sql.insert.render.BatchInsert;
import org.mybatis.dynamic.sql.insert.render.BatchInsertRenderer;
import org.mybatis.dynamic.sql.render.RenderingStrategy;
Expand All @@ -27,6 +28,7 @@ private BatchInsertModel(Builder<T> builder) {
super(builder);
}

@NotNull
public BatchInsert<T> render(RenderingStrategy renderingStrategy) {
return BatchInsertRenderer.withBatchInsertModel(this)
.withRenderingStrategy(renderingStrategy)
Expand Down
Loading