From 368a79855b1781337fb3da9145cbef3932d2d3c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 00:14:24 +0000 Subject: [PATCH 01/68] Update dependency maven to v3.9.11 --- .mvn/wrapper/maven-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 0d35f6dbb..b06697c61 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -16,5 +16,5 @@ # under the License. wrapperVersion=3.3.2 distributionType=source -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.10/apache-maven-3.9.10-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar From 9e614ffe2a4e866cfcb210a292f3e6d3b75f556f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 11:13:20 +0000 Subject: [PATCH 02/68] Update dependency org.springframework:spring-jdbc to v6.2.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9e41feb33..1e8cd801d 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.springframework spring-jdbc - 6.2.8 + 6.2.9 provided true From 88dece895d4d25f0ebc6fdebf8ebc5733942cd0e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 10:58:02 +0000 Subject: [PATCH 03/68] Update junit-framework monorepo to v5.13.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1e8cd801d..7c2cdb32c 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 17 17 17 - 5.13.3 + 5.13.4 5.2.2 checkstyle-override.xml From 95f3fa98546719e047ac6c6117cfa415e9eb73ab Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Fri, 25 Jul 2025 16:54:38 -0400 Subject: [PATCH 04/68] Support "mappedColumn" on insert statements We added a "javaProperty" to SqlColumn so properties can be centrally configured, and then used on generated insert statements. This will make the mappers generated from MyBatis Generator less verbose. --- .../org/mybatis/dynamic/sql/SqlColumn.java | 20 +++++- .../mybatis/dynamic/sql/insert/InsertDSL.java | 12 ++++ .../dynamic/sql/insert/MultiRowInsertDSL.java | 6 ++ .../render/MultiRowValuePhraseVisitor.java | 19 ++++++ .../sql/insert/render/ValuePhraseVisitor.java | 29 +++++++++ .../sql/util/ColumnMappingVisitor.java | 4 ++ .../sql/util/GeneralInsertMappingVisitor.java | 10 +++ .../dynamic/sql/util/InternalError.java | 7 +- .../dynamic/sql/util/MappedColumnMapping.java | 34 ++++++++++ .../util/MappedColumnWhenPresentMapping.java | 43 +++++++++++++ .../util/MultiRowInsertMappingVisitor.java | 5 ++ .../sql/util/UpdateMappingVisitor.java | 10 +++ .../dynamic/sql/util/messages.properties | 1 + .../simple/PersonDynamicSqlSupport.java | 22 +++++-- .../java/examples/simple/PersonMapper.java | 42 ++++++------ .../examples/spring/PersonTemplateTest.java | 5 +- .../sql/insert/render/InsertVisitorsTest.java | 60 +++++++++++++++++ .../sql/util/ColumnMappingVisitorTest.java | 64 +++++++++++++++++++ 18 files changed, 360 insertions(+), 33 deletions(-) create mode 100644 src/main/java/org/mybatis/dynamic/sql/util/MappedColumnMapping.java create mode 100644 src/main/java/org/mybatis/dynamic/sql/util/MappedColumnWhenPresentMapping.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/insert/render/InsertVisitorsTest.java diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index 4c841d86b..0d3a9ec44 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -37,6 +37,7 @@ public class SqlColumn implements BindableColumn, SortSpecification { protected final ParameterTypeConverter parameterTypeConverter; protected final @Nullable String tableQualifier; protected final @Nullable Class javaType; + protected final @Nullable String javaProperty; private SqlColumn(Builder builder) { name = Objects.requireNonNull(builder.name); @@ -49,6 +50,7 @@ private SqlColumn(Builder builder) { parameterTypeConverter = Objects.requireNonNull(builder.parameterTypeConverter); tableQualifier = builder.tableQualifier; javaType = builder.javaType; + javaProperty = builder.javaProperty; } public String name() { @@ -79,6 +81,10 @@ public Optional> javaType() { return Optional.ofNullable(javaType); } + public Optional javaProperty() { + return Optional.ofNullable(javaProperty); + } + @Override public @Nullable Object convertParameterType(@Nullable T value) { return value == null ? null : parameterTypeConverter.convert(value); @@ -164,6 +170,11 @@ public SqlColumn withJavaType(Class javaType) { return b.withJavaType(javaType).build(); } + public SqlColumn withJavaProperty(String javaProperty) { + Builder b = copy(); + return b.withJavaProperty(javaProperty).build(); + } + /** * 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 @@ -185,7 +196,8 @@ private Builder copy() { .withRenderingStrategy(this.renderingStrategy) .withParameterTypeConverter((ParameterTypeConverter) this.parameterTypeConverter) .withTableQualifier(this.tableQualifier) - .withJavaType((Class) this.javaType); + .withJavaType((Class) this.javaType) + .withJavaProperty(this.javaProperty); } public static SqlColumn of(String name, SqlTable table) { @@ -212,6 +224,7 @@ public static class Builder { protected ParameterTypeConverter parameterTypeConverter = v -> v; protected @Nullable String tableQualifier; protected @Nullable Class javaType; + protected @Nullable String javaProperty; public Builder withName(String name) { this.name = name; @@ -263,6 +276,11 @@ public Builder withJavaType(@Nullable Class javaType) { return this; } + public Builder withJavaProperty(@Nullable String javaProperty) { + this.javaProperty = javaProperty; + return this; + } + public SqlColumn build() { return new SqlColumn<>(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java index d622ced8c..8b95a9c0f 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/InsertDSL.java @@ -27,6 +27,8 @@ 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.MappedColumnMapping; +import org.mybatis.dynamic.sql.util.MappedColumnWhenPresentMapping; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping; @@ -49,6 +51,16 @@ public ColumnMappingFinisher map(SqlColumn column) { return new ColumnMappingFinisher<>(column); } + public InsertDSL withMappedColumn(SqlColumn column) { + columnMappings.add(MappedColumnMapping.of(column)); + return this; + } + + public InsertDSL withMappedColumnWhenPresent(SqlColumn column, Supplier valueSupplier) { + columnMappings.add(MappedColumnWhenPresentMapping.of(column, valueSupplier)); + return this; + } + @Override public InsertModel build() { return InsertModel.withRow(row) diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/MultiRowInsertDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/MultiRowInsertDSL.java index 8e5f30e49..87705c415 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/MultiRowInsertDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/MultiRowInsertDSL.java @@ -25,6 +25,7 @@ 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.MappedColumnMapping; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.RowMapping; @@ -46,6 +47,11 @@ public ColumnMappingFinisher map(SqlColumn column) { return new ColumnMappingFinisher<>(column); } + public MultiRowInsertDSL withMappedColumn(SqlColumn column) { + columnMappings.add(MappedColumnMapping.of(column)); + return this; + } + @Override public MultiRowInsertModel build() { return MultiRowInsertModel.withRecords(records) diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java index ba2d2ffa5..49f362750 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java @@ -16,8 +16,11 @@ package org.mybatis.dynamic.sql.insert.render; import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.util.ConstantMapping; +import org.mybatis.dynamic.sql.util.MappedColumnMapping; +import org.mybatis.dynamic.sql.util.Messages; import org.mybatis.dynamic.sql.util.MultiRowInsertMappingVisitor; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; @@ -69,6 +72,22 @@ public FieldAndValueAndParameters visit(RowMapping mapping) { .build(); } + @Override + public FieldAndValueAndParameters visit(MappedColumnMapping mapping) { + return FieldAndValueAndParameters.withFieldName(mapping.columnName()) + .withValuePhrase(calculateJdbcPlaceholder( + mapping.column(), + getMappedPropertyName(mapping.column())) + ) + .build(); + } + + private String getMappedPropertyName(SqlColumn column) { + return column.javaProperty().orElseThrow(() -> + new InvalidSqlException(Messages + .getString("ERROR.50", column.name()))); //$NON-NLS-1$ + } + private String calculateJdbcPlaceholder(SqlColumn column) { return column.renderingStrategy().orElse(renderingStrategy).getRecordBasedInsertBinding(column, prefix); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java index dccf0f14a..a784f6e3b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java @@ -18,9 +18,13 @@ import java.util.Optional; import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.util.ConstantMapping; import org.mybatis.dynamic.sql.util.InsertMappingVisitor; +import org.mybatis.dynamic.sql.util.MappedColumnMapping; +import org.mybatis.dynamic.sql.util.MappedColumnWhenPresentMapping; +import org.mybatis.dynamic.sql.util.Messages; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping; @@ -80,6 +84,31 @@ public Optional visit(RowMapping mapping) { .buildOptional(); } + @Override + public Optional visit(MappedColumnMapping mapping) { + return FieldAndValueAndParameters.withFieldName(mapping.columnName()) + .withValuePhrase(calculateJdbcPlaceholder( + mapping.column(), + getMappedPropertyName(mapping.column())) + ) + .buildOptional(); + } + + @Override + public Optional visit(MappedColumnWhenPresentMapping mapping) { + if (mapping.shouldRender()) { + return visit((MappedColumnMapping) mapping); + } else { + return Optional.empty(); + } + } + + private String getMappedPropertyName(SqlColumn column) { + return column.javaProperty().orElseThrow(() -> + new InvalidSqlException(Messages + .getString("ERROR.50", column.name()))); //$NON-NLS-1$ + } + private String calculateJdbcPlaceholder(SqlColumn column) { return column.renderingStrategy().orElse(renderingStrategy) .getRecordBasedInsertBinding(column, "row"); //$NON-NLS-1$ diff --git a/src/main/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitor.java b/src/main/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitor.java index 19f949f09..34516105b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitor.java @@ -52,4 +52,8 @@ public interface ColumnMappingVisitor { R visit(ColumnToColumnMapping mapping); R visit(RowMapping mapping); + + R visit(MappedColumnMapping mapping); + + R visit(MappedColumnWhenPresentMapping mapping); } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/GeneralInsertMappingVisitor.java b/src/main/java/org/mybatis/dynamic/sql/util/GeneralInsertMappingVisitor.java index e55e3d100..21303d49b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/GeneralInsertMappingVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/GeneralInsertMappingVisitor.java @@ -40,4 +40,14 @@ public final R visit(ColumnToColumnMapping columnMapping) { public final R visit(RowMapping mapping) { throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_14)); } + + @Override + public R visit(MappedColumnMapping mapping) { + throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_16)); + } + + @Override + public R visit(MappedColumnWhenPresentMapping mapping) { + throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_17)); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/InternalError.java b/src/main/java/org/mybatis/dynamic/sql/util/InternalError.java index ed6b0a3cc..e973dad55 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/InternalError.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/InternalError.java @@ -33,7 +33,12 @@ public enum InternalError { INTERNAL_ERROR_12(12), INTERNAL_ERROR_13(13), INTERNAL_ERROR_14(14), - INTERNAL_ERROR_15(15); + INTERNAL_ERROR_15(15), + INTERNAL_ERROR_16(16), + INTERNAL_ERROR_17(17), + INTERNAL_ERROR_18(18), + INTERNAL_ERROR_19(19), + INTERNAL_ERROR_20(20); private final int number; diff --git a/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnMapping.java new file mode 100644 index 000000000..bef0a57e0 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnMapping.java @@ -0,0 +1,34 @@ +/* + * Copyright 2016-2025 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.util; + +import org.mybatis.dynamic.sql.SqlColumn; + +public class MappedColumnMapping extends AbstractColumnMapping { + + protected MappedColumnMapping(SqlColumn column) { + super(column); + } + + @Override + public R accept(ColumnMappingVisitor visitor) { + return visitor.visit(this); + } + + public static MappedColumnMapping of(SqlColumn column) { + return new MappedColumnMapping(column); + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnWhenPresentMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnWhenPresentMapping.java new file mode 100644 index 000000000..2e0729ed0 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnWhenPresentMapping.java @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2025 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.util; + +import org.mybatis.dynamic.sql.SqlColumn; + +import java.util.Objects; +import java.util.function.Supplier; + +public class MappedColumnWhenPresentMapping extends MappedColumnMapping { + private final Supplier valueSupplier; + + private MappedColumnWhenPresentMapping(SqlColumn column, Supplier valueSupplier) { + super(column); + this.valueSupplier = Objects.requireNonNull(valueSupplier); + } + + public boolean shouldRender() { + return valueSupplier.get() != null; + } + + @Override + public R accept(ColumnMappingVisitor visitor) { + return visitor.visit(this); + } + + public static MappedColumnWhenPresentMapping of(SqlColumn column, Supplier valueSupplier) { + return new MappedColumnWhenPresentMapping(column, valueSupplier); + } +} diff --git a/src/main/java/org/mybatis/dynamic/sql/util/MultiRowInsertMappingVisitor.java b/src/main/java/org/mybatis/dynamic/sql/util/MultiRowInsertMappingVisitor.java index 18a75221d..668d064e7 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/MultiRowInsertMappingVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/MultiRowInsertMappingVisitor.java @@ -20,4 +20,9 @@ public abstract class MultiRowInsertMappingVisitor extends InsertMappingVisit public final R visit(PropertyWhenPresentMapping mapping) { throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_12)); } + + @Override + public R visit(MappedColumnWhenPresentMapping mapping) { + throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_18)); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java b/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java index 8d06585ab..b6597685c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/UpdateMappingVisitor.java @@ -30,4 +30,14 @@ public final R visit(PropertyWhenPresentMapping mapping) { public final R visit(RowMapping mapping) { throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_15)); } + + @Override + public R visit(MappedColumnMapping mapping) { + throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_19)); + } + + @Override + public R visit(MappedColumnWhenPresentMapping mapping) { + throw new UnsupportedOperationException(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_20)); + } } diff --git a/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties b/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties index 3cf560a78..89ea08e70 100644 --- a/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties +++ b/src/main/resources/org/mybatis/dynamic/sql/util/messages.properties @@ -67,4 +67,5 @@ ERROR.47=A Kotlin case statement must specify a "then" clause for every "when" c ERROR.48=You cannot call more than one of "forUpdate", "forNoKeyUpdate", "forShare", or "forKeyShare" in a select \ statement ERROR.49=You cannot call more than one of "skipLocked", or "nowait" in a select statement +ERROR.50=Mapped column {0} does not have a javaProperty configured INTERNAL.ERROR=Internal Error {0} diff --git a/src/test/java/examples/simple/PersonDynamicSqlSupport.java b/src/test/java/examples/simple/PersonDynamicSqlSupport.java index 215f8a909..ba31eea5d 100644 --- a/src/test/java/examples/simple/PersonDynamicSqlSupport.java +++ b/src/test/java/examples/simple/PersonDynamicSqlSupport.java @@ -32,13 +32,21 @@ public final class PersonDynamicSqlSupport { public static final SqlColumn addressId = person.addressId; public static final class Person extends SqlTable { - public final SqlColumn id = column("id", JDBCType.INTEGER); - public final SqlColumn firstName = column("first_name", JDBCType.VARCHAR); - public final SqlColumn lastName = column("last_name", JDBCType.VARCHAR, "examples.simple.LastNameTypeHandler"); - public final SqlColumn birthDate = column("birth_date", JDBCType.DATE); - public final SqlColumn employed = column("employed", JDBCType.VARCHAR, "examples.simple.YesNoTypeHandler"); - public final SqlColumn occupation = column("occupation", JDBCType.VARCHAR); - public final SqlColumn addressId = column("address_id", JDBCType.INTEGER); + public final SqlColumn id = column("id", JDBCType.INTEGER).withJavaProperty("id"); + public final SqlColumn firstName = column("first_name", JDBCType.VARCHAR) + .withJavaProperty("firstName"); + public final SqlColumn lastName = + column("last_name", JDBCType.VARCHAR, "examples.simple.LastNameTypeHandler") + .withJavaProperty("lastName"); + public final SqlColumn birthDate = column("birth_date", JDBCType.DATE) + .withJavaProperty("birthDate"); + public final SqlColumn employed = + column("employed", JDBCType.VARCHAR, "examples.simple.YesNoTypeHandler") + .withJavaProperty("employed"); + public final SqlColumn occupation = column("occupation", JDBCType.VARCHAR) + .withJavaProperty("occupation"); + public final SqlColumn addressId = column("address_id", JDBCType.INTEGER) + .withJavaProperty("addressId"); public Person() { super("Person"); diff --git a/src/test/java/examples/simple/PersonMapper.java b/src/test/java/examples/simple/PersonMapper.java index 819b72c3b..371caaf0b 100644 --- a/src/test/java/examples/simple/PersonMapper.java +++ b/src/test/java/examples/simple/PersonMapper.java @@ -106,13 +106,13 @@ default int generalInsert(UnaryOperator completer) { default int insert(PersonRecord row) { return MyBatis3Utils.insert(this::insert, row, person, c -> - c.map(id).toProperty("id") - .map(firstName).toProperty("firstName") - .map(lastName).toProperty("lastName") - .map(birthDate).toProperty("birthDate") - .map(employed).toProperty("employed") - .map(occupation).toProperty("occupation") - .map(addressId).toProperty("addressId") + c.withMappedColumn(id) + .withMappedColumn(firstName) + .withMappedColumn(lastName) + .withMappedColumn(birthDate) + .withMappedColumn(employed) + .withMappedColumn(occupation) + .withMappedColumn(addressId) ); } @@ -122,25 +122,25 @@ default int insertMultiple(PersonRecord...records) { default int insertMultiple(Collection records) { return MyBatis3Utils.insertMultiple(this::insertMultiple, records, person, c -> - c.map(id).toProperty("id") - .map(firstName).toProperty("firstName") - .map(lastName).toProperty("lastName") - .map(birthDate).toProperty("birthDate") - .map(employed).toProperty("employed") - .map(occupation).toProperty("occupation") - .map(addressId).toProperty("addressId") + c.withMappedColumn(id) + .withMappedColumn(firstName) + .withMappedColumn(lastName) + .withMappedColumn(birthDate) + .withMappedColumn(employed) + .withMappedColumn(occupation) + .withMappedColumn(addressId) ); } default int insertSelective(PersonRecord row) { return MyBatis3Utils.insert(this::insert, row, person, c -> - c.map(id).toPropertyWhenPresent("id", row::id) - .map(firstName).toPropertyWhenPresent("firstName", row::firstName) - .map(lastName).toPropertyWhenPresent("lastName", row::lastName) - .map(birthDate).toPropertyWhenPresent("birthDate", row::birthDate) - .map(employed).toPropertyWhenPresent("employed", row::employed) - .map(occupation).toPropertyWhenPresent("occupation", row::occupation) - .map(addressId).toPropertyWhenPresent("addressId", row::addressId) + c.withMappedColumnWhenPresent(id, row::id) + .withMappedColumnWhenPresent(firstName, row::firstName) + .withMappedColumnWhenPresent(lastName, row::lastName) + .withMappedColumnWhenPresent(birthDate, row::birthDate) + .withMappedColumnWhenPresent(employed, row::employed) + .withMappedColumnWhenPresent(occupation, row::occupation) + .withMappedColumnWhenPresent(addressId, row::addressId) ); } diff --git a/src/test/java/examples/spring/PersonTemplateTest.java b/src/test/java/examples/spring/PersonTemplateTest.java index b92c03a50..9e8de9813 100644 --- a/src/test/java/examples/spring/PersonTemplateTest.java +++ b/src/test/java/examples/spring/PersonTemplateTest.java @@ -201,9 +201,8 @@ void testFirstNameIn() { List rows = template.selectList(selectStatement, personRowMapper); - assertThat(rows).hasSize(2); - - assertThat(rows).satisfiesExactly( + assertThat(rows).hasSize(2) + .satisfiesExactly( person1 -> assertThat(person1).isNotNull() .extracting("lastName").isNotNull() .extracting("name").isEqualTo("Flintstone"), diff --git a/src/test/java/org/mybatis/dynamic/sql/insert/render/InsertVisitorsTest.java b/src/test/java/org/mybatis/dynamic/sql/insert/render/InsertVisitorsTest.java new file mode 100644 index 000000000..f44b0930c --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/insert/render/InsertVisitorsTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2016-2025 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.insert.render; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import org.junit.jupiter.api.Test; +import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.exception.InvalidSqlException; +import org.mybatis.dynamic.sql.render.RenderingStrategies; +import org.mybatis.dynamic.sql.util.MappedColumnMapping; +import org.mybatis.dynamic.sql.util.Messages; + +class InsertVisitorsTest { + @Test + void testThatMultiRowInsertVisitorErrorsForMappedColumnWhenPropertyIsMissing() { + TestTable table = new TestTable(); + MultiRowValuePhraseVisitor tv = new MultiRowValuePhraseVisitor(RenderingStrategies.MYBATIS3, "prefix"); + MappedColumnMapping mapping = MappedColumnMapping.of(table.id); + + assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(() -> tv.visit(mapping)) + .withMessage(Messages.getString("ERROR.50", table.id.name())); + } + + @Test + void testThatValuePhraseVisitorErrorsForMappedColumnWhenPropertyIsMissing() { + TestTable table = new TestTable(); + ValuePhraseVisitor tv = new ValuePhraseVisitor(RenderingStrategies.MYBATIS3); + MappedColumnMapping mapping = MappedColumnMapping.of(table.id); + + assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(() -> tv.visit(mapping)) + .withMessage(Messages.getString("ERROR.50", table.id.name())); + } + + private static class TestTable extends SqlTable { + public final SqlColumn id; + public final SqlColumn description; + + public TestTable() { + super("Test"); + + id = column("id"); + description = column("description"); + } + } +} diff --git a/src/test/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitorTest.java b/src/test/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitorTest.java index 2f17910b2..9369a35ce 100644 --- a/src/test/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitorTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/util/ColumnMappingVisitorTest.java @@ -204,6 +204,56 @@ void testThatUpdateVisitorErrorsForRowMapping() { .withMessage("Internal Error 15"); } + @Test + void testThatUpdateVisitorErrorsForMappedColumnMapping() { + TestTable table = new TestTable(); + UpdateVisitor tv = new UpdateVisitor(); + MappedColumnMapping mapping = MappedColumnMapping.of(table.id); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)) + .withMessage("Internal Error 19"); + } + + @Test + void testThatUpdateVisitorErrorsForMappedWhenPresentColumnMapping() { + TestTable table = new TestTable(); + UpdateVisitor tv = new UpdateVisitor(); + MappedColumnWhenPresentMapping mapping = MappedColumnWhenPresentMapping.of(table.id, () -> 1); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)) + .withMessage("Internal Error 20"); + } + + @Test + void testThatGeneralInsertVisitorErrorsForMappedColumnMapping() { + TestTable table = new TestTable(); + GeneralInsertVisitor tv = new GeneralInsertVisitor(); + MappedColumnMapping mapping = MappedColumnMapping.of(table.id); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)) + .withMessage("Internal Error 16"); + } + + @Test + void testThatGeneralInsertVisitorErrorsForMappedWhenPresentColumnMapping() { + TestTable table = new TestTable(); + GeneralInsertVisitor tv = new GeneralInsertVisitor(); + MappedColumnWhenPresentMapping mapping = MappedColumnWhenPresentMapping.of(table.id, () -> 1); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)) + .withMessage("Internal Error 17"); + } + + @Test + void testThatMultiRowInsertVisitorErrorsForMappedColumnWhenPresentMapping() { + TestTable table = new TestTable(); + MultiRowInsertVisitor tv = new MultiRowInsertVisitor(); + MappedColumnWhenPresentMapping mapping = MappedColumnWhenPresentMapping.of(table.id, () -> 1); + + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> tv.visit(mapping)) + .withMessage("Internal Error 18"); + } + private static class TestTable extends SqlTable { public final SqlColumn id; public final SqlColumn description; @@ -278,6 +328,16 @@ public String visit(PropertyWhenPresentMapping mapping) { public String visit(RowMapping mapping) { return "Row Mapping"; } + + @Override + public String visit(MappedColumnMapping mapping) { + return "Mapped Column Mapping"; + } + + @Override + public String visit(MappedColumnWhenPresentMapping mapping) { + return "Mapped Column When Present Mapping"; + } } private static class UpdateVisitor extends UpdateMappingVisitor { @@ -349,5 +409,9 @@ public String visit(RowMapping mapping) { return "Row Mapping"; } + @Override + public String visit(MappedColumnMapping mapping) { + return "Mapped Column Mapping"; + } } } From 3897fbae98776b337b6bea67592a58bb33c42ae4 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Fri, 25 Jul 2025 16:58:48 -0400 Subject: [PATCH 05/68] checkstyle --- .../dynamic/sql/util/MappedColumnWhenPresentMapping.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnWhenPresentMapping.java b/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnWhenPresentMapping.java index 2e0729ed0..268cae396 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnWhenPresentMapping.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/MappedColumnWhenPresentMapping.java @@ -15,11 +15,11 @@ */ package org.mybatis.dynamic.sql.util; -import org.mybatis.dynamic.sql.SqlColumn; - import java.util.Objects; import java.util.function.Supplier; +import org.mybatis.dynamic.sql.SqlColumn; + public class MappedColumnWhenPresentMapping extends MappedColumnMapping { private final Supplier valueSupplier; From ac2e6dce4b7c431942902caf6bbd649a84ba22fc Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Fri, 25 Jul 2025 17:21:19 -0400 Subject: [PATCH 06/68] Add Kotlin Support for MappedColumns with inserts --- .../util/kotlin/KotlinBatchInsertBuilder.kt | 5 ++ .../sql/util/kotlin/KotlinInsertBuilder.kt | 10 ++++ .../kotlin/KotlinMultiRowInsertBuilder.kt | 5 ++ .../kotlin/elements/SqlTableExtensions.kt | 4 +- .../canonical/PersonDynamicSqlSupport.kt | 16 +++--- .../canonical/PersonMapperExtensions.kt | 56 +++++++++---------- 6 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinBatchInsertBuilder.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinBatchInsertBuilder.kt index aa2dd3703..fed6bf1d6 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinBatchInsertBuilder.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinBatchInsertBuilder.kt @@ -21,6 +21,7 @@ import org.mybatis.dynamic.sql.insert.BatchInsertDSL import org.mybatis.dynamic.sql.insert.BatchInsertModel import org.mybatis.dynamic.sql.util.AbstractColumnMapping import org.mybatis.dynamic.sql.util.Buildable +import org.mybatis.dynamic.sql.util.MappedColumnMapping typealias KotlinBatchInsertCompleter = KotlinBatchInsertBuilder.() -> Unit @@ -37,6 +38,10 @@ class KotlinBatchInsertBuilder (private val rows: Collection): Build columnMappings.add(it) } + fun withMappedColumn(column: SqlColumn) { + columnMappings.add(MappedColumnMapping.of(column)) + } + override fun build(): BatchInsertModel { assertNotNull(table, "ERROR.23") //$NON-NLS-1$ return with(BatchInsertDSL.Builder()) { diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinInsertBuilder.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinInsertBuilder.kt index 8b76231f7..6b2784fed 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinInsertBuilder.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinInsertBuilder.kt @@ -21,6 +21,8 @@ import org.mybatis.dynamic.sql.insert.InsertDSL import org.mybatis.dynamic.sql.insert.InsertModel import org.mybatis.dynamic.sql.util.AbstractColumnMapping import org.mybatis.dynamic.sql.util.Buildable +import org.mybatis.dynamic.sql.util.MappedColumnMapping +import org.mybatis.dynamic.sql.util.MappedColumnWhenPresentMapping typealias KotlinInsertCompleter = KotlinInsertBuilder.() -> Unit @@ -37,6 +39,14 @@ class KotlinInsertBuilder (private val row: T): Buildable withMappedColumn(column: SqlColumn) { + columnMappings.add(MappedColumnMapping.of(column)) + } + + fun withMappedColumnWhenPresent(column: SqlColumn, valueSupplier: () -> Any?) { + columnMappings.add(MappedColumnWhenPresentMapping.of(column, valueSupplier)) + } + override fun build(): InsertModel { assertNotNull(table, "ERROR.25") //$NON-NLS-1$ return with(InsertDSL.Builder()) { diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinMultiRowInsertBuilder.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinMultiRowInsertBuilder.kt index b00a62aef..6b3d72d7c 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinMultiRowInsertBuilder.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/KotlinMultiRowInsertBuilder.kt @@ -21,6 +21,7 @@ import org.mybatis.dynamic.sql.insert.MultiRowInsertDSL import org.mybatis.dynamic.sql.insert.MultiRowInsertModel import org.mybatis.dynamic.sql.util.AbstractColumnMapping import org.mybatis.dynamic.sql.util.Buildable +import org.mybatis.dynamic.sql.util.MappedColumnMapping typealias KotlinMultiRowInsertCompleter = KotlinMultiRowInsertBuilder.() -> Unit @@ -37,6 +38,10 @@ class KotlinMultiRowInsertBuilder (private val rows: Collection): Bu columnMappings.add(it) } + fun withMappedColumn(column: SqlColumn) { + columnMappings.add(MappedColumnMapping.of(column)) + } + override fun build(): MultiRowInsertModel { assertNotNull(table, "ERROR.26") //$NON-NLS-1$ return with(MultiRowInsertDSL.Builder()) { diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlTableExtensions.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlTableExtensions.kt index bbd79dcd7..24962ef01 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlTableExtensions.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlTableExtensions.kt @@ -34,7 +34,8 @@ fun SqlTable.column( typeHandler: String? = null, renderingStrategy: RenderingStrategy? = null, parameterTypeConverter: ((T?) -> Any?) = { it }, - javaType: KClass? = null + javaType: KClass? = null, + javaProperty: String? = null, ): SqlColumn = SqlColumn.Builder().run { withTable(this@column) withName(name) @@ -43,5 +44,6 @@ fun SqlTable.column( withRenderingStrategy(renderingStrategy) withParameterTypeConverter(parameterTypeConverter) withJavaType(javaType?.java) + withJavaProperty(javaProperty) build() } diff --git a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonDynamicSqlSupport.kt b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonDynamicSqlSupport.kt index 94e60161d..dbfbbecba 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonDynamicSqlSupport.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonDynamicSqlSupport.kt @@ -31,20 +31,22 @@ object PersonDynamicSqlSupport { val addressId = person.addressId class Person : SqlTable("Person") { - val id = column(name = "id", jdbcType = JDBCType.INTEGER) - val firstName = column(name = "first_name", jdbcType = JDBCType.VARCHAR) + val id = column(name = "id", jdbcType = JDBCType.INTEGER, javaProperty = "id") + val firstName = column(name = "first_name", jdbcType = JDBCType.VARCHAR, javaProperty = "firstName") val lastName = column( name = "last_name", jdbcType = JDBCType.VARCHAR, - typeHandler = "examples.kotlin.mybatis3.canonical.LastNameTypeHandler" + typeHandler = "examples.kotlin.mybatis3.canonical.LastNameTypeHandler", + javaProperty = "lastName" ) - val birthDate = column(name = "birth_date", jdbcType = JDBCType.DATE) + val birthDate = column(name = "birth_date", jdbcType = JDBCType.DATE, javaProperty = "birthDate") val employed = column( name = "employed", JDBCType.VARCHAR, - typeHandler = "examples.kotlin.mybatis3.canonical.YesNoTypeHandler" + typeHandler = "examples.kotlin.mybatis3.canonical.YesNoTypeHandler", + javaProperty = "employed" ) - val occupation = column(name = "occupation", jdbcType = JDBCType.VARCHAR) - val addressId = column(name = "address_id", jdbcType = JDBCType.INTEGER) + val occupation = column(name = "occupation", jdbcType = JDBCType.VARCHAR, javaProperty = "occupation") + val addressId = column(name = "address_id", jdbcType = JDBCType.INTEGER, javaProperty = "addressId") } } diff --git a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperExtensions.kt b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperExtensions.kt index 237b5ebd7..184805bae 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperExtensions.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/canonical/PersonMapperExtensions.kt @@ -65,13 +65,13 @@ fun PersonMapper.deleteByPrimaryKey(id_: Int) = fun PersonMapper.insert(record: PersonRecord) = insert(this::insert, record, person) { - map(id) toProperty "id" - map(firstName) toProperty "firstName" - map(lastName) toProperty "lastName" - map(birthDate) toProperty "birthDate" - map(employed) toProperty "employed" - map(occupation) toProperty "occupation" - map(addressId) toProperty "addressId" + withMappedColumn(id) + withMappedColumn(firstName) + withMappedColumn(lastName) + withMappedColumn(birthDate) + withMappedColumn(employed) + withMappedColumn(occupation) + withMappedColumn(addressId) } fun PersonMapper.generalInsert(completer: GeneralInsertCompleter) = @@ -85,13 +85,13 @@ fun PersonMapper.insertBatch(vararg records: PersonRecord): List = fun PersonMapper.insertBatch(records: Collection): List = insertBatch(this::insert, records, person) { - map(id) toProperty "id" - map(firstName) toProperty "firstName" - map(lastName) toProperty "lastName" - map(birthDate) toProperty "birthDate" - map(employed) toProperty "employed" - map(occupation) toProperty "occupation" - map(addressId) toProperty "addressId" + withMappedColumn(id) + withMappedColumn(firstName) + withMappedColumn(lastName) + withMappedColumn(birthDate) + withMappedColumn(employed) + withMappedColumn(occupation) + withMappedColumn(addressId) } fun PersonMapper.insertMultiple(vararg records: PersonRecord) = @@ -99,24 +99,24 @@ fun PersonMapper.insertMultiple(vararg records: PersonRecord) = fun PersonMapper.insertMultiple(records: Collection) = insertMultiple(this::insertMultiple, records, person) { - map(id) toProperty "id" - map(firstName) toProperty "firstName" - map(lastName) toProperty "lastName" - map(birthDate) toProperty "birthDate" - map(employed) toProperty "employed" - map(occupation) toProperty "occupation" - map(addressId) toProperty "addressId" + withMappedColumn(id) + withMappedColumn(firstName) + withMappedColumn(lastName) + withMappedColumn(birthDate) + withMappedColumn(employed) + withMappedColumn(occupation) + withMappedColumn(addressId) } fun PersonMapper.insertSelective(record: PersonRecord) = insert(this::insert, record, person) { - map(id).toPropertyWhenPresent("id", record::id) - map(firstName).toPropertyWhenPresent("firstName", record::firstName) - map(lastName).toPropertyWhenPresent("lastName", record::lastName) - map(birthDate).toPropertyWhenPresent("birthDate", record::birthDate) - map(employed).toPropertyWhenPresent("employed", record::employed) - map(occupation).toPropertyWhenPresent("occupation", record::occupation) - map(addressId).toPropertyWhenPresent("addressId", record::addressId) + withMappedColumnWhenPresent(id, record::id) + withMappedColumnWhenPresent(firstName, record::firstName) + withMappedColumnWhenPresent(lastName, record::lastName) + withMappedColumnWhenPresent(birthDate, record::birthDate) + withMappedColumnWhenPresent(employed, record::employed) + withMappedColumnWhenPresent(occupation, record::occupation) + withMappedColumnWhenPresent(addressId, record::addressId) } private val columnList = listOf(id `as` "A_ID", firstName, lastName, birthDate, employed, occupation, addressId) From 0b8c93e924cdfe50f01eaf421f6e8f3645a73a28 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Mon, 28 Jul 2025 09:50:49 -0400 Subject: [PATCH 07/68] Add MappedColumn support for batch inserts in Java --- .../org/mybatis/dynamic/sql/insert/BatchInsertDSL.java | 6 ++++++ .../always/spring/GeneratedAlwaysDynamicSqlSupport.java | 8 ++++---- .../java/examples/generated/always/spring/SpringTest.java | 6 +++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java b/src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java index 71bc350d6..c2938e9c1 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/BatchInsertDSL.java @@ -27,6 +27,7 @@ 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.MappedColumnMapping; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.RowMapping; @@ -48,6 +49,11 @@ public ColumnMappingFinisher map(SqlColumn column) { return new ColumnMappingFinisher<>(column); } + public BatchInsertDSL withMappedColumn(SqlColumn column) { + columnMappings.add(MappedColumnMapping.of(column)); + return this; + } + @Override public BatchInsertModel build() { return BatchInsertModel.withRecords(records) diff --git a/src/test/java/examples/generated/always/spring/GeneratedAlwaysDynamicSqlSupport.java b/src/test/java/examples/generated/always/spring/GeneratedAlwaysDynamicSqlSupport.java index 9a96f8bc2..cc0af0058 100644 --- a/src/test/java/examples/generated/always/spring/GeneratedAlwaysDynamicSqlSupport.java +++ b/src/test/java/examples/generated/always/spring/GeneratedAlwaysDynamicSqlSupport.java @@ -26,10 +26,10 @@ public final class GeneratedAlwaysDynamicSqlSupport { public static final SqlColumn fullName = generatedAlways.fullName; public static final class GeneratedAlways extends SqlTable { - public final SqlColumn id = column("id"); - public final SqlColumn firstName = column("first_name"); - public final SqlColumn lastName = column("last_name"); - public final SqlColumn fullName = column("full_name"); + public final SqlColumn id = column("id").withJavaProperty("id"); + public final SqlColumn firstName = column("first_name").withJavaProperty("firstName"); + public final SqlColumn lastName = column("last_name").withJavaProperty("lastName"); + public final SqlColumn fullName = column("full_name").withJavaProperty("fullName"); public GeneratedAlways() { super("GeneratedAlways"); diff --git a/src/test/java/examples/generated/always/spring/SpringTest.java b/src/test/java/examples/generated/always/spring/SpringTest.java index 59a241368..5fd9b59d1 100644 --- a/src/test/java/examples/generated/always/spring/SpringTest.java +++ b/src/test/java/examples/generated/always/spring/SpringTest.java @@ -241,9 +241,9 @@ void testInsertBatch() { BatchInsert batchInsert = insertBatch(records) .into(generatedAlways) - .map(id).toProperty("id") - .map(firstName).toProperty("firstName") - .map(lastName).toProperty("lastName") + .withMappedColumn(id) + .withMappedColumn(firstName) + .withMappedColumn(lastName) .build() .render(RenderingStrategies.SPRING_NAMED_PARAMETER); From bc175524be9c2175c826cdde71e65b33617fdbb6 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 29 Jul 2025 14:48:38 -0400 Subject: [PATCH 08/68] Documentation --- CHANGELOG.md | 2 + src/site/markdown/docs/insert.md | 60 +++++++++++++++++++ .../examples/simple/PersonMapperTest.java | 22 +++++++ 3 files changed, 84 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c65f2c09c..9375a78e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,8 @@ Runtime behavior changes: are not supported by the library out of the box. The statement renderers now call methods `renderCondition` and `renderLeftColumn` that you can override to implement any rendering you need. In addition, we've made `filter` and `map` support optional if you implement custom conditions +- Added support for configuring a Java property name to be associated with an `SqlColumn`. This property name can be + used with the record based insert methods to reduce the boilerplate code for mapping columns to Java properties. ## Release 1.5.2 - June 3, 2024 diff --git a/src/site/markdown/docs/insert.md b/src/site/markdown/docs/insert.md index b7fe11a70..6ac2e0302 100644 --- a/src/site/markdown/docs/insert.md +++ b/src/site/markdown/docs/insert.md @@ -44,6 +44,66 @@ Notice the `map` method. It is used to map a database column to an attribute of 5. `map(column).toPropertyWhenPresent(property, Supplier valueSupplier)` will insert a value from the record into a column if the value is non-null. The value of the property will be bound to the SQL statement as a prepared statement parameter. This is used to generate a "selective" insert as defined in MyBatis Generator. 6. `map(column).toRow()` will insert the record itself into a column. This is appropriate when the "record" is a simple class like Integer or String. +### Mapped Columns +Starting in version 2.0.0 there are two new methods: + +1. `withMappedColumn(SqlColumn)` that will map a database column to a Java property based on a property name that can + be configured in an `SQLColumn`. +2. `withMappedColumnWhenPresent(SqlColumn, Supplier)` that will map a database column to a Java property based on a + property name that can be configured in an `SQLColumn`. The insert statement will only contain the mapped column when + the Supplier returns a non-null value (this method is for single record inserts only). + +This will allow you to configure mappings in a single place (the `SqlColumn`) and reuse them in multiple insert +statements. For example: + +```java +public final class PersonDynamicSqlSupport { + public static final Person person = new Person(); + public static final SqlColumn id = person.id; + public static final SqlColumn firstName = person.firstName; + public static final SqlColumn lastName = person.lastName; + + public static final class Person extends SqlTable { + public final SqlColumn id = column("id", JDBCType.INTEGER).withJavaProperty("id"); + public final SqlColumn firstName = column("first_name", JDBCType.VARCHAR) + .withJavaProperty("firstName"); + public final SqlColumn lastName = + column("last_name", JDBCType.VARCHAR).withJavaProperty("lastName"); + + public Person() { + super("Person"); + } + } +} +``` + +In this support class, each `SqlColumn` has a configured Java property. This property can be accessed in record based +inserts in the following way: + +```java + @Test + void testRawInsert() { + try (SqlSession session = sqlSessionFactory.openSession()) { + PersonMapper mapper = session.getMapper(PersonMapper.class); + PersonRecord row = new PersonRecord(100, "Joe", "Jones"); + + InsertStatementProvider insertStatement = insert(row).into(person) + .withMappedColumn(id) + .withMappedColumn(firstName) + .withMappedColumn(lastName) + .build().render(RenderingStrategies.MYBATIS3); + + int rows = mapper.insert(insertStatement); + assertThat(rows).isEqualTo(1); + } + } +``` + +In this test, the mapping between a column and the property of a record is calculated by reading the configured Java +property for each column. + +These new methods are available for the record based insert statements (`insert`, `insertMultiple`, `insertBatch`). + ### Annotated Mapper for Single Row Insert Statements The InsertStatementProvider object can be used as a parameter to a MyBatis mapper method directly. If you are using an annotated mapper, the insert method should look like this (with @Options added for generated values if necessary): diff --git a/src/test/java/examples/simple/PersonMapperTest.java b/src/test/java/examples/simple/PersonMapperTest.java index b02d52c4c..ca1e90792 100644 --- a/src/test/java/examples/simple/PersonMapperTest.java +++ b/src/test/java/examples/simple/PersonMapperTest.java @@ -53,6 +53,7 @@ import org.mybatis.dynamic.sql.delete.render.DeleteStatementProvider; import org.mybatis.dynamic.sql.exception.NonRenderingWhereClauseException; import org.mybatis.dynamic.sql.insert.render.GeneralInsertStatementProvider; +import org.mybatis.dynamic.sql.insert.render.InsertStatementProvider; import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.dynamic.sql.select.CountDSLCompleter; import org.mybatis.dynamic.sql.select.SelectDSLCompleter; @@ -338,6 +339,27 @@ void testInsert() { } } + @Test + void testRawInsert() { + try (SqlSession session = sqlSessionFactory.openSession()) { + PersonMapper mapper = session.getMapper(PersonMapper.class); + PersonRecord row = new PersonRecord(100, "Joe", new LastName("Jones"), new Date(), true, "Developer", 1); + + InsertStatementProvider insertStatement = insert(row).into(person) + .withMappedColumn(id) + .withMappedColumn(firstName) + .withMappedColumn(lastName) + .withMappedColumn(birthDate) + .withMappedColumn(employed) + .withMappedColumn(occupation) + .withMappedColumn(addressId) + .build().render(RenderingStrategies.MYBATIS3); + + int rows = mapper.insert(insertStatement); + assertThat(rows).isEqualTo(1); + } + } + @Test void testGeneralInsert() { try (SqlSession session = sqlSessionFactory.openSession()) { From 32bba95a4bbc6d835a2b3cd94dde17c0bfbb0812 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 17:46:49 +0000 Subject: [PATCH 09/68] Update dependency com.mysql:mysql-connector-j to v9.4.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7c2cdb32c..e050c200a 100644 --- a/pom.xml +++ b/pom.xml @@ -214,7 +214,7 @@ com.mysql mysql-connector-j - 9.3.0 + 9.4.0 test From a0470e2e7a7263a40950dd74c4bb021b92d00718 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 16:34:48 +0000 Subject: [PATCH 10/68] Update dependency org.assertj:assertj-core to v3.27.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e050c200a..1da18a04e 100644 --- a/pom.xml +++ b/pom.xml @@ -136,7 +136,7 @@ org.assertj assertj-core - 3.27.3 + 3.27.4 test From 2e38280474d4b8e2a1d9e64998a654c0c75fe1b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:34:00 +0000 Subject: [PATCH 11/68] Update dependency org.mariadb.jdbc:mariadb-java-client to v3.5.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1da18a04e..002cfc078 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.mariadb.jdbc mariadb-java-client - 3.5.4 + 3.5.5 test From 626112b7bcea0d738f5a2c5198f2e2bc98109393 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:03:39 +0000 Subject: [PATCH 12/68] Update actions/checkout action to v5 --- .github/workflows/ci.yaml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/coveralls.yaml | 2 +- .github/workflows/site.yaml | 2 +- .github/workflows/sonar.yaml | 2 +- .github/workflows/sonatype.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 66bba3618..9e7986a3f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: name: Test JDK ${{ matrix.java }}, ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up JDK ${{ matrix.java }} ${{ matrix.distribution }} uses: actions/setup-java@v4 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8e5c9c4b6..3073c7b96 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Java uses: actions/setup-java@v4 diff --git a/.github/workflows/coveralls.yaml b/.github/workflows/coveralls.yaml index b4aaaa991..746fd407a 100644 --- a/.github/workflows/coveralls.yaml +++ b/.github/workflows/coveralls.yaml @@ -9,7 +9,7 @@ jobs: if: github.repository_owner == 'mybatis' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v4 with: diff --git a/.github/workflows/site.yaml b/.github/workflows/site.yaml index de1babe41..6a6d29d76 100644 --- a/.github/workflows/site.yaml +++ b/.github/workflows/site.yaml @@ -13,7 +13,7 @@ jobs: if: github.repository_owner == 'mybatis' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v4 with: diff --git a/.github/workflows/sonar.yaml b/.github/workflows/sonar.yaml index 33c70609c..cd1de642c 100644 --- a/.github/workflows/sonar.yaml +++ b/.github/workflows/sonar.yaml @@ -12,7 +12,7 @@ jobs: if: github.repository_owner == 'mybatis' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: # Disabling shallow clone is recommended for improving relevancy of reporting fetch-depth: 0 diff --git a/.github/workflows/sonatype.yaml b/.github/workflows/sonatype.yaml index c922f382c..59100a36f 100644 --- a/.github/workflows/sonatype.yaml +++ b/.github/workflows/sonatype.yaml @@ -12,7 +12,7 @@ jobs: if: github.repository_owner == 'mybatis' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up JDK uses: actions/setup-java@v4 with: From 997a11b1c93ab69ff894cd749600e0ed1ba96256 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Mon, 11 Aug 2025 11:08:57 -0400 Subject: [PATCH 13/68] Remove copy/paste code --- .../sql/insert/render/InsertRenderingUtilities.java | 9 +++++++++ .../sql/insert/render/MultiRowValuePhraseVisitor.java | 10 +--------- .../dynamic/sql/insert/render/ValuePhraseVisitor.java | 10 +--------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderingUtilities.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderingUtilities.java index de529a2b4..a65b56b85 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderingUtilities.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/InsertRenderingUtilities.java @@ -17,7 +17,10 @@ import static org.mybatis.dynamic.sql.util.StringUtilities.spaceBefore; +import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.exception.InvalidSqlException; +import org.mybatis.dynamic.sql.util.Messages; public class InsertRenderingUtilities { private InsertRenderingUtilities() {} @@ -33,4 +36,10 @@ public static String calculateInsertStatement(SqlTable table, FieldAndValueColle public static String calculateInsertStatementStart(SqlTable table) { return "insert into " + table.tableName(); //$NON-NLS-1$ } + + public static String getMappedPropertyName(SqlColumn column) { + return column.javaProperty().orElseThrow(() -> + new InvalidSqlException(Messages + .getString("ERROR.50", column.name()))); //$NON-NLS-1$ + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java index 49f362750..216fdfcbf 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/MultiRowValuePhraseVisitor.java @@ -16,11 +16,9 @@ package org.mybatis.dynamic.sql.insert.render; import org.mybatis.dynamic.sql.SqlColumn; -import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.util.ConstantMapping; import org.mybatis.dynamic.sql.util.MappedColumnMapping; -import org.mybatis.dynamic.sql.util.Messages; import org.mybatis.dynamic.sql.util.MultiRowInsertMappingVisitor; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; @@ -77,17 +75,11 @@ public FieldAndValueAndParameters visit(MappedColumnMapping mapping) { return FieldAndValueAndParameters.withFieldName(mapping.columnName()) .withValuePhrase(calculateJdbcPlaceholder( mapping.column(), - getMappedPropertyName(mapping.column())) + InsertRenderingUtilities.getMappedPropertyName(mapping.column())) ) .build(); } - private String getMappedPropertyName(SqlColumn column) { - return column.javaProperty().orElseThrow(() -> - new InvalidSqlException(Messages - .getString("ERROR.50", column.name()))); //$NON-NLS-1$ - } - private String calculateJdbcPlaceholder(SqlColumn column) { return column.renderingStrategy().orElse(renderingStrategy).getRecordBasedInsertBinding(column, prefix); } diff --git a/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java index a784f6e3b..d628c77ef 100644 --- a/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/insert/render/ValuePhraseVisitor.java @@ -18,13 +18,11 @@ import java.util.Optional; import org.mybatis.dynamic.sql.SqlColumn; -import org.mybatis.dynamic.sql.exception.InvalidSqlException; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.util.ConstantMapping; import org.mybatis.dynamic.sql.util.InsertMappingVisitor; import org.mybatis.dynamic.sql.util.MappedColumnMapping; import org.mybatis.dynamic.sql.util.MappedColumnWhenPresentMapping; -import org.mybatis.dynamic.sql.util.Messages; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping; @@ -89,7 +87,7 @@ public Optional visit(MappedColumnMapping mapping) { return FieldAndValueAndParameters.withFieldName(mapping.columnName()) .withValuePhrase(calculateJdbcPlaceholder( mapping.column(), - getMappedPropertyName(mapping.column())) + InsertRenderingUtilities.getMappedPropertyName(mapping.column())) ) .buildOptional(); } @@ -103,12 +101,6 @@ public Optional visit(MappedColumnWhenPresentMapping } } - private String getMappedPropertyName(SqlColumn column) { - return column.javaProperty().orElseThrow(() -> - new InvalidSqlException(Messages - .getString("ERROR.50", column.name()))); //$NON-NLS-1$ - } - private String calculateJdbcPlaceholder(SqlColumn column) { return column.renderingStrategy().orElse(renderingStrategy) .getRecordBasedInsertBinding(column, "row"); //$NON-NLS-1$ From 52d98e45ec9129011aa76c7069f00bb332ba46ce Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:57:15 +0000 Subject: [PATCH 14/68] Update dependency org.springframework:spring-jdbc to v6.2.10 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 002cfc078..fb25fa99b 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.springframework spring-jdbc - 6.2.9 + 6.2.10 provided true From 39929c085d3a1a153fa47fa4e4932fb46eff9ffb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 05:46:56 +0000 Subject: [PATCH 15/68] Update actions/setup-java action to v5 --- .github/workflows/ci.yaml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/coveralls.yaml | 2 +- .github/workflows/site.yaml | 2 +- .github/workflows/sonar.yaml | 2 +- .github/workflows/sonatype.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9e7986a3f..0261021ce 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up JDK ${{ matrix.java }} ${{ matrix.distribution }} - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: ${{ matrix.java }} distribution: ${{ matrix.distribution }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3073c7b96..1bf6638ba 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v5 - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: cache: maven distribution: 'temurin' diff --git a/.github/workflows/coveralls.yaml b/.github/workflows/coveralls.yaml index 746fd407a..09ab9b4a7 100644 --- a/.github/workflows/coveralls.yaml +++ b/.github/workflows/coveralls.yaml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: cache: maven distribution: temurin diff --git a/.github/workflows/site.yaml b/.github/workflows/site.yaml index 6a6d29d76..17572ee8e 100644 --- a/.github/workflows/site.yaml +++ b/.github/workflows/site.yaml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: cache: maven distribution: temurin diff --git a/.github/workflows/sonar.yaml b/.github/workflows/sonar.yaml index cd1de642c..a43dd7198 100644 --- a/.github/workflows/sonar.yaml +++ b/.github/workflows/sonar.yaml @@ -17,7 +17,7 @@ jobs: # Disabling shallow clone is recommended for improving relevancy of reporting fetch-depth: 0 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: cache: maven distribution: temurin diff --git a/.github/workflows/sonatype.yaml b/.github/workflows/sonatype.yaml index 59100a36f..9f47d2039 100644 --- a/.github/workflows/sonatype.yaml +++ b/.github/workflows/sonatype.yaml @@ -14,7 +14,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: cache: maven distribution: temurin From 89b301852506bb272f8931d22569742b8f59fd9b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Sep 2025 10:46:51 +0000 Subject: [PATCH 16/68] Update dependency org.springframework:spring-jdbc to v6.2.11 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fb25fa99b..35fdaae2f 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.springframework spring-jdbc - 6.2.10 + 6.2.11 provided true From d38e5d85a6b2141e6290a182966a008a271c9617 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:12:03 +0000 Subject: [PATCH 17/68] Update dependency org.mariadb.jdbc:mariadb-java-client to v3.5.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 35fdaae2f..eaf09ff7c 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.mariadb.jdbc mariadb-java-client - 3.5.5 + 3.5.6 test From c2f6a262c75e75acea6b5f14f8d4793e1d562c49 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 20:44:34 +0000 Subject: [PATCH 18/68] Update spring batch to v5.2.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eaf09ff7c..276e6d7a3 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 17 17 5.13.4 - 5.2.2 + 5.2.3 checkstyle-override.xml From 72776b1bd6b5229f2e731940b55a97d5f09734c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 01:14:38 +0000 Subject: [PATCH 19/68] Update dependency org.assertj:assertj-core to v3.27.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eaf09ff7c..7ebf954f1 100644 --- a/pom.xml +++ b/pom.xml @@ -136,7 +136,7 @@ org.assertj assertj-core - 3.27.4 + 3.27.5 test From 98537da63418b4610e4b2fd381107d2397f1e3d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 01:14:41 +0000 Subject: [PATCH 20/68] Update dependency org.postgresql:postgresql to v42.7.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eaf09ff7c..ae782dd02 100644 --- a/pom.xml +++ b/pom.xml @@ -190,7 +190,7 @@ org.postgresql postgresql - 42.7.7 + 42.7.8 test From e3cd5cd83d9b7b7e57d9b6a7ef4376022620f920 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 18:58:14 +0000 Subject: [PATCH 21/68] Update dependency org.assertj:assertj-core to v3.27.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fdb2a456a..5e5127f20 100644 --- a/pom.xml +++ b/pom.xml @@ -136,7 +136,7 @@ org.assertj assertj-core - 3.27.5 + 3.27.6 test From 1842d42bc6122f8a08c1544e7f5499849dfad204 Mon Sep 17 00:00:00 2001 From: bee0511 Date: Mon, 29 Sep 2025 16:32:17 -0500 Subject: [PATCH 22/68] fix flaky test in DeprecatedJoinMapperTest#testFullJoinWithoutAliases --- .../kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt index d712c46c6..afaa874b3 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt @@ -276,17 +276,17 @@ class DeprecatedJoinMapperTest { assertThat(rows).hasSize(6) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("DESCRIPTION", "Catcher Glove"), entry("ITEM_ID", 55) ) - assertThat(rows[3]).containsExactly( + assertThat(rows[3]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 6) ) - assertThat(rows[5]).containsExactly( + assertThat(rows[5]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), From b5e30472147db6fea01e02c08fcac58faf13f5d3 Mon Sep 17 00:00:00 2001 From: bee0511 Date: Tue, 30 Sep 2025 00:32:32 -0500 Subject: [PATCH 23/68] fix other flaky tests within DeprecatedJoinMapperTest --- .../joins/DeprecatedJoinMapperTest.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt index afaa874b3..4586f72d3 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt @@ -324,12 +324,12 @@ class DeprecatedJoinMapperTest { assertThat(rows).hasSize(5) - assertThat(rows[2]).containsExactly( + assertThat(rows[2]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 6) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -375,12 +375,12 @@ class DeprecatedJoinMapperTest { assertThat(rows).hasSize(5) - assertThat(rows[2]).containsExactly( + assertThat(rows[2]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 6) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -418,12 +418,12 @@ class DeprecatedJoinMapperTest { assertThat(rows).hasSize(5) - assertThat(rows[2]).containsExactly( + assertThat(rows[2]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 6) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -461,12 +461,12 @@ class DeprecatedJoinMapperTest { assertThat(rows).hasSize(5) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("DESCRIPTION", "Catcher Glove"), entry("ITEM_ID", 55) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -512,12 +512,12 @@ class DeprecatedJoinMapperTest { assertThat(rows).hasSize(5) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("DESCRIPTION", "Catcher Glove"), entry("ITEM_ID", 55) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -555,12 +555,12 @@ class DeprecatedJoinMapperTest { assertThat(rows).hasSize(5) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("DESCRIPTION", "Catcher Glove"), entry("ITEM_ID", 55) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), From 91f0a22d53d5206a7bea077019706ee3db7177a9 Mon Sep 17 00:00:00 2001 From: bee0511 Date: Tue, 30 Sep 2025 00:43:51 -0500 Subject: [PATCH 24/68] fix flaky tests in JoinMapperNewSyntaxTest --- .../mybatis3/joins/JoinMapperNewSyntaxTest.kt | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/test/kotlin/examples/kotlin/mybatis3/joins/JoinMapperNewSyntaxTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/joins/JoinMapperNewSyntaxTest.kt index 3274d0595..50f813d86 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/joins/JoinMapperNewSyntaxTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/joins/JoinMapperNewSyntaxTest.kt @@ -407,17 +407,17 @@ class JoinMapperNewSyntaxTest { assertThat(rows).hasSize(6) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("DESCRIPTION", "Catcher Glove"), entry("ITEM_ID", 55) ) - assertThat(rows[3]).containsExactly( + assertThat(rows[3]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 6) ) - assertThat(rows[5]).containsExactly( + assertThat(rows[5]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -455,12 +455,12 @@ class JoinMapperNewSyntaxTest { assertThat(rows).hasSize(5) - assertThat(rows[2]).containsExactly( + assertThat(rows[2]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 6) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -504,12 +504,12 @@ class JoinMapperNewSyntaxTest { assertThat(rows).hasSize(5) - assertThat(rows[2]).containsExactly( + assertThat(rows[2]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 6) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -547,12 +547,12 @@ class JoinMapperNewSyntaxTest { assertThat(rows).hasSize(5) - assertThat(rows[2]).containsExactly( + assertThat(rows[2]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 6) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -590,12 +590,12 @@ class JoinMapperNewSyntaxTest { assertThat(rows).hasSize(5) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("DESCRIPTION", "Catcher Glove"), entry("ITEM_ID", 55) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -639,12 +639,12 @@ class JoinMapperNewSyntaxTest { assertThat(rows).hasSize(5) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("DESCRIPTION", "Catcher Glove"), entry("ITEM_ID", 55) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -682,12 +682,12 @@ class JoinMapperNewSyntaxTest { assertThat(rows).hasSize(5) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("DESCRIPTION", "Catcher Glove"), entry("ITEM_ID", 55) ) - assertThat(rows[4]).containsExactly( + assertThat(rows[4]).containsOnly( entry("ORDER_ID", 2), entry("QUANTITY", 1), entry("DESCRIPTION", "Outfield Glove"), @@ -720,7 +720,7 @@ class JoinMapperNewSyntaxTest { val rows = mapper.selectManyMappedRows(selectStatement) assertThat(rows).hasSize(1) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("USER_ID", 2), entry("USER_NAME", "Barney"), ) @@ -752,7 +752,7 @@ class JoinMapperNewSyntaxTest { val rows = mapper.selectManyMappedRows(selectStatement) assertThat(rows).hasSize(1) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("USER_ID", 2), entry("USER_NAME", "Barney"), ) @@ -783,7 +783,7 @@ class JoinMapperNewSyntaxTest { val rows = mapper.selectManyMappedRows(selectStatement) assertThat(rows).hasSize(1) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("USER_ID", 2), entry("USER_NAME", "Barney"), ) @@ -814,7 +814,7 @@ class JoinMapperNewSyntaxTest { val rows = mapper.selectManyMappedRows(selectStatement) assertThat(rows).hasSize(1) - assertThat(rows[0]).containsExactly( + assertThat(rows[0]).containsOnly( entry("USER_ID", 2), entry("USER_NAME", "Barney"), ) From 834275420a3cdfde7c82a584b2a7cb1f68750d6d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:55:48 +0000 Subject: [PATCH 25/68] Update junit-framework monorepo to v6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5e5127f20..b0a8bb868 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 17 17 17 - 5.13.4 + 6.0.0 5.2.3 checkstyle-override.xml From 22acd90baece24e7a0a7d5b0e10b437b012bf6c2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Sep 2025 17:09:16 +0000 Subject: [PATCH 26/68] Update dependency ch.qos.logback:logback-classic to v1.5.19 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5e5127f20..87c608a43 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ ch.qos.logback logback-classic - 1.5.18 + 1.5.19 test From 970a851f590fc301340aa8686b1457ec85942bb0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 5 Oct 2025 00:25:35 +0000 Subject: [PATCH 27/68] Update dependency org.mybatis:mybatis-parent to v51 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6d1d0fb6a..c3b07343c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ org.mybatis mybatis-parent - 50 + 51 org.mybatis.dynamic-sql From 4e09d6ff64388ea7425aa7fe39c8777c2b0755b6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:59:50 +0000 Subject: [PATCH 28/68] Update github/codeql-action action to v4 --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1bf6638ba..1d481b2df 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -35,15 +35,15 @@ jobs: java-version: 21 - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{ matrix.language }}" From 02fce4bf03a845ee6ac86fed177990fe347c9b41 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 7 Oct 2025 13:59:22 -0400 Subject: [PATCH 29/68] Fix a few more potentially flakey tests --- src/test/java/examples/simple/PersonMapperTest.java | 2 +- .../examples/type_conversion/TypeConversionTest.java | 2 +- .../dynamic/sql/util/FragmentCollectorTest.java | 2 +- .../where/render/OptionalCriterionRenderTest.java | 12 ++++++------ .../kotlin/spring/canonical/KotlinElementsTest.kt | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/examples/simple/PersonMapperTest.java b/src/test/java/examples/simple/PersonMapperTest.java index ca1e90792..b50e38183 100644 --- a/src/test/java/examples/simple/PersonMapperTest.java +++ b/src/test/java/examples/simple/PersonMapperTest.java @@ -957,6 +957,6 @@ void gh737() { "where id = #{parameters.p2,jdbcType=INTEGER}"; assertThat(updateStatement.getUpdateStatement()).isEqualTo(expected); - assertThat(updateStatement.getParameters()).containsExactly(entry("p1", 4), entry("p2", 5)); + assertThat(updateStatement.getParameters()).containsOnly(entry("p1", 4), entry("p2", 5)); } } diff --git a/src/test/java/examples/type_conversion/TypeConversionTest.java b/src/test/java/examples/type_conversion/TypeConversionTest.java index 90846166e..017014508 100644 --- a/src/test/java/examples/type_conversion/TypeConversionTest.java +++ b/src/test/java/examples/type_conversion/TypeConversionTest.java @@ -93,7 +93,7 @@ void testFunctionInSelect() { .render(RenderingStrategies.MYBATIS3); Map row = mapper.selectOneMappedRow(selectStatement); - assertThat(row).containsExactly(entry("FILE_ID", 1), entry("FILE_CONTENTS", randomBlob)); + assertThat(row).containsOnly(entry("FILE_ID", 1), entry("FILE_CONTENTS", randomBlob)); selectStatement = select(fileId, toBase64(fileContents).as("checksum")) .from(myfiles) diff --git a/src/test/java/org/mybatis/dynamic/sql/util/FragmentCollectorTest.java b/src/test/java/org/mybatis/dynamic/sql/util/FragmentCollectorTest.java index c3f8a5dea..2074b1cc4 100644 --- a/src/test/java/org/mybatis/dynamic/sql/util/FragmentCollectorTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/util/FragmentCollectorTest.java @@ -41,6 +41,6 @@ void testWhereFragmentCollectorMerge() { fc1.merge(fc2); assertThat(fc1.collectFragments(Collectors.joining(","))).isEqualTo(":p1,:p2"); - assertThat(fc1.parameters()).containsExactly(entry("p1", 1), entry("p2", 2)); + assertThat(fc1.parameters()).containsOnly(entry("p1", 1), entry("p2", 2)); } } diff --git a/src/test/java/org/mybatis/dynamic/sql/where/render/OptionalCriterionRenderTest.java b/src/test/java/org/mybatis/dynamic/sql/where/render/OptionalCriterionRenderTest.java index 48bd36755..6ebf93049 100644 --- a/src/test/java/org/mybatis/dynamic/sql/where/render/OptionalCriterionRenderTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/where/render/OptionalCriterionRenderTest.java @@ -143,7 +143,7 @@ void testOverrideFirstConnector() { .render(RenderingStrategies.SPRING_NAMED_PARAMETER); assertThat(whereClause).hasValueSatisfying(wc -> { - assertThat(wc.getParameters()).containsExactly(entry("p1", "fred"), entry("p2", "flintstone")); + assertThat(wc.getParameters()).containsOnly(entry("p1", "fred"), entry("p2", "flintstone")); assertThat(wc.getWhereClause()).isEqualTo("where first_name = :p1 or last_name = :p2"); }); } @@ -185,7 +185,7 @@ void testWhereExistsOr() { "or exists (select * from person where id = :p2)"; assertThat(whereClause).hasValueSatisfying(wc -> { - assertThat(wc.getParameters()).containsExactly(entry("p1", 3), entry("p2", 4)); + assertThat(wc.getParameters()).containsOnly(entry("p1", 3), entry("p2", 4)); assertThat(wc.getWhereClause()).isEqualTo(expected); }); } @@ -217,7 +217,7 @@ void testWhereExistsOrOr() { "or exists (select * from person where id = :p3))"; assertThat(whereClause).hasValueSatisfying(wc -> { - assertThat(wc.getParameters()).containsExactly(entry("p1", 3), entry("p2", 4), entry("p3", 5)); + assertThat(wc.getParameters()).containsOnly(entry("p1", 3), entry("p2", 4), entry("p3", 5)); assertThat(wc.getWhereClause()).isEqualTo(expected); }); } @@ -242,7 +242,7 @@ void testWhereExistsAnd() { "and exists (select * from person where id = :p2)"; assertThat(whereClause).hasValueSatisfying(wc -> { - assertThat(wc.getParameters()).containsExactly(entry("p1", 3), entry("p2", 4)); + assertThat(wc.getParameters()).containsOnly(entry("p1", 3), entry("p2", 4)); assertThat(wc.getWhereClause()).isEqualTo(expected); }); } @@ -274,7 +274,7 @@ void testWhereExistsAndAnd() { "and exists (select * from person where id = :p3))"; assertThat(whereClause).hasValueSatisfying(wc -> { - assertThat(wc.getParameters()).containsExactly(entry("p1", 3), entry("p2", 4), entry("p3", 5)); + assertThat(wc.getParameters()).containsOnly(entry("p1", 3), entry("p2", 4), entry("p3", 5)); assertThat(wc.getWhereClause()).isEqualTo(expected); }); } @@ -315,7 +315,7 @@ void testCollapsingCriteriaGroup3() { String expected = "where first_name = :p1 or first_name = :p2"; assertThat(whereClause).hasValueSatisfying(wc -> { - assertThat(wc.getParameters()).containsExactly(entry("p1", "Fred"), entry("p2", "Betty")); + assertThat(wc.getParameters()).containsOnly(entry("p1", "Fred"), entry("p2", "Betty")); assertThat(wc.getWhereClause()).isEqualTo(expected); }); } diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/KotlinElementsTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/KotlinElementsTest.kt index 74c230200..e1403a0b0 100644 --- a/src/test/kotlin/examples/kotlin/spring/canonical/KotlinElementsTest.kt +++ b/src/test/kotlin/examples/kotlin/spring/canonical/KotlinElementsTest.kt @@ -672,6 +672,6 @@ open class KotlinElementsTest { 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)) + assertThat(updateStatement.parameters).containsOnly(entry("p1", 4), entry("p2", 5)) } } From 5c62e85a6a0516c5470bc0e54299e72b41bef694 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 7 Oct 2025 16:07:04 -0400 Subject: [PATCH 30/68] Update nullability specs IntelliJ has fixed quite a few nullability inspection bugs - updating accordingly --- .../sql/AbstractListValueCondition.java | 5 ++--- .../sql/AbstractSingleValueCondition.java | 5 ++--- .../sql/AbstractTwoValueCondition.java | 11 +++++------ .../org/mybatis/dynamic/sql/SqlBuilder.java | 13 ++++++------- .../mybatis/dynamic/sql/util/Utilities.java | 10 ++++++++++ .../sql/where/condition/AndGatherer.java | 4 +--- .../sql/where/condition/IsBetween.java | 13 ++++++------- .../where/condition/IsBetweenWhenPresent.java | 11 +++++------ .../sql/where/condition/IsEqualTo.java | 5 ++--- .../where/condition/IsEqualToWhenPresent.java | 5 ++--- .../sql/where/condition/IsGreaterThan.java | 5 ++--- .../condition/IsGreaterThanOrEqualTo.java | 5 ++--- .../IsGreaterThanOrEqualToWhenPresent.java | 5 ++--- .../condition/IsGreaterThanWhenPresent.java | 5 ++--- .../dynamic/sql/where/condition/IsIn.java | 5 ++--- .../where/condition/IsInCaseInsensitive.java | 5 ++--- .../IsInCaseInsensitiveWhenPresent.java | 5 ++--- .../sql/where/condition/IsInWhenPresent.java | 11 +++++------ .../sql/where/condition/IsLessThan.java | 5 ++--- .../where/condition/IsLessThanOrEqualTo.java | 5 ++--- .../IsLessThanOrEqualToWhenPresent.java | 5 ++--- .../condition/IsLessThanWhenPresent.java | 5 ++--- .../dynamic/sql/where/condition/IsLike.java | 5 ++--- .../condition/IsLikeCaseInsensitive.java | 5 ++--- .../IsLikeCaseInsensitiveWhenPresent.java | 5 ++--- .../where/condition/IsLikeWhenPresent.java | 5 ++--- .../sql/where/condition/IsNotBetween.java | 11 +++++------ .../condition/IsNotBetweenWhenPresent.java | 11 +++++------ .../sql/where/condition/IsNotEqualTo.java | 5 ++--- .../condition/IsNotEqualToWhenPresent.java | 5 ++--- .../dynamic/sql/where/condition/IsNotIn.java | 5 ++--- .../condition/IsNotInCaseInsensitive.java | 5 ++--- .../IsNotInCaseInsensitiveWhenPresent.java | 5 ++--- .../where/condition/IsNotInWhenPresent.java | 11 +++++------ .../sql/where/condition/IsNotLike.java | 5 ++--- .../condition/IsNotLikeCaseInsensitive.java | 5 ++--- .../IsNotLikeCaseInsensitiveWhenPresent.java | 5 ++--- .../where/condition/IsNotLikeWhenPresent.java | 5 ++--- .../array/NamesTableDynamicSqlSupport.java | 5 +++-- .../java/examples/array/package-info.java | 19 +++++++++++++++++++ .../ColumnComparisonConfiguration.java | 4 +++- .../column/comparison/package-info.java | 19 +++++++++++++++++++ .../examples/complexquery/package-info.java | 19 +++++++++++++++++++ .../always/mybatis/package-info.java | 19 +++++++++++++++++++ .../generated/always/spring/package-info.java | 19 +++++++++++++++++++ .../java/examples/groupby/package-info.java | 19 +++++++++++++++++++ .../java/examples/mariadb/package-info.java | 19 +++++++++++++++++++ .../java/examples/postgres/package-info.java | 19 +++++++++++++++++++ .../springbatch/mapper/package-info.java | 19 +++++++++++++++++++ .../MyFilesDynamicSqlSupport.java | 5 +++-- .../examples/type_conversion/ToBase64.java | 5 +++-- src/test/java/issues/gh100/package-info.java | 19 +++++++++++++++++++ src/test/java/issues/gh105/package-info.java | 19 +++++++++++++++++++ .../java/issues/gh324/ObservableCache.java | 3 ++- src/test/java/issues/gh324/package-info.java | 19 +++++++++++++++++++ src/test/java/issues/lhg142/package-info.java | 19 +++++++++++++++++++ .../dynamic/sql/delete/package-info.java | 19 +++++++++++++++++++ .../dynamic/sql/insert/package-info.java | 19 +++++++++++++++++++ .../sql/insert/render/package-info.java | 19 +++++++++++++++++++ .../dynamic/sql/mybatis3/package-info.java | 19 +++++++++++++++++++ .../org/mybatis/dynamic/sql/package-info.java | 19 +++++++++++++++++++ .../dynamic/sql/select/package-info.java | 19 +++++++++++++++++++ .../dynamic/sql/subselect/package-info.java | 19 +++++++++++++++++++ .../dynamic/sql/update/package-info.java | 19 +++++++++++++++++++ .../dynamic/sql/util/package-info.java | 19 +++++++++++++++++++ .../sql/where/condition/package-info.java | 19 +++++++++++++++++++ .../dynamic/sql/where/package-info.java | 19 +++++++++++++++++++ .../sql/where/render/package-info.java | 19 +++++++++++++++++++ .../kotlin/mybatis3/mariadb/KIsLikeEscape.kt | 2 +- 69 files changed, 599 insertions(+), 146 deletions(-) create mode 100644 src/test/java/examples/array/package-info.java create mode 100644 src/test/java/examples/column/comparison/package-info.java create mode 100644 src/test/java/examples/complexquery/package-info.java create mode 100644 src/test/java/examples/generated/always/mybatis/package-info.java create mode 100644 src/test/java/examples/generated/always/spring/package-info.java create mode 100644 src/test/java/examples/groupby/package-info.java create mode 100644 src/test/java/examples/mariadb/package-info.java create mode 100644 src/test/java/examples/postgres/package-info.java create mode 100644 src/test/java/examples/springbatch/mapper/package-info.java create mode 100644 src/test/java/issues/gh100/package-info.java create mode 100644 src/test/java/issues/gh105/package-info.java create mode 100644 src/test/java/issues/gh324/package-info.java create mode 100644 src/test/java/issues/lhg142/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/delete/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/insert/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/insert/render/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/mybatis3/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/select/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/subselect/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/update/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/util/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/where/condition/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/where/package-info.java create mode 100644 src/test/java/org/mybatis/dynamic/sql/where/render/package-info.java diff --git a/src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java b/src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java index e178c6bf3..41c6a56e2 100644 --- a/src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java +++ b/src/main/java/org/mybatis/dynamic/sql/AbstractListValueCondition.java @@ -23,7 +23,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.render.RenderedParameterInfo; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.FragmentAndParameters; @@ -114,7 +113,7 @@ public interface Filterable { * @return this condition if renderable and the value matches the predicate, otherwise a condition * that will not render. */ - AbstractListValueCondition filter(Predicate predicate); + AbstractListValueCondition filter(Predicate predicate); } /** @@ -139,6 +138,6 @@ public interface Mappable { * @return a new condition with the result of applying the mapper to the value of this condition, * if renderable, otherwise a condition that will not render. */ - AbstractListValueCondition map(Function mapper); + AbstractListValueCondition map(Function mapper); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/AbstractSingleValueCondition.java b/src/main/java/org/mybatis/dynamic/sql/AbstractSingleValueCondition.java index eb56eef39..c16dbf08c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/AbstractSingleValueCondition.java +++ b/src/main/java/org/mybatis/dynamic/sql/AbstractSingleValueCondition.java @@ -21,7 +21,6 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.render.RenderedParameterInfo; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.FragmentAndParameters; @@ -89,7 +88,7 @@ public interface Filterable { * @return this condition if renderable and the value matches the predicate, otherwise a condition * that will not render. */ - AbstractSingleValueCondition filter(Predicate predicate); + AbstractSingleValueCondition filter(Predicate predicate); } /** @@ -114,6 +113,6 @@ public interface Mappable { * @return a new condition with the result of applying the mapper to the value of this condition, * if renderable, otherwise a condition that will not render. */ - AbstractSingleValueCondition map(Function mapper); + AbstractSingleValueCondition map(Function mapper); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/AbstractTwoValueCondition.java b/src/main/java/org/mybatis/dynamic/sql/AbstractTwoValueCondition.java index d409ffbb8..6cceff16e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/AbstractTwoValueCondition.java +++ b/src/main/java/org/mybatis/dynamic/sql/AbstractTwoValueCondition.java @@ -23,7 +23,6 @@ import java.util.function.Predicate; import java.util.function.Supplier; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.render.RenderedParameterInfo; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.FragmentAndParameters; @@ -111,7 +110,7 @@ public interface Filterable { * @return this condition if renderable and the values match the predicate, otherwise a condition * that will not render. */ - AbstractTwoValueCondition filter(BiPredicate predicate); + AbstractTwoValueCondition filter(BiPredicate predicate); /** * If renderable and both values match the predicate, returns this condition. Else returns a condition @@ -122,7 +121,7 @@ public interface Filterable { * @return this condition if renderable and the values match the predicate, otherwise a condition * that will not render. */ - AbstractTwoValueCondition filter(Predicate predicate); + AbstractTwoValueCondition filter(Predicate predicate); } /** @@ -148,8 +147,8 @@ public interface Mappable { * @return a new condition with the result of applying the mappers to the values of this condition, * if renderable, otherwise a condition that will not render. */ - AbstractTwoValueCondition map(Function mapper1, - Function mapper2); + AbstractTwoValueCondition map(Function mapper1, + Function mapper2); /** * If renderable, apply the mapping to both values and return a new condition with the new values. Else return a @@ -160,6 +159,6 @@ AbstractTwoValueCondition map(Function m * @return a new condition with the result of applying the mappers to the values of this condition, * if renderable, otherwise a condition that will not render. */ - AbstractTwoValueCondition map(Function mapper); + AbstractTwoValueCondition map(Function mapper); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java index c8fe5c3ba..2a8243999 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlBuilder.java @@ -21,7 +21,6 @@ import java.util.Objects; import java.util.function.Supplier; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.delete.DeleteDSL; import org.mybatis.dynamic.sql.delete.DeleteModel; @@ -782,11 +781,11 @@ static IsLessThanOrEqualToWhenPresent isLessThanOrEqualToWhenPresent(Supp } @SafeVarargs - static IsIn isIn(@NonNull T... values) { + static IsIn isIn(T... values) { return IsIn.of(values); } - static IsIn isIn(Collection<@NonNull T> values) { + static IsIn isIn(Collection values) { return IsIn.of(values); } @@ -804,11 +803,11 @@ static IsInWhenPresent isInWhenPresent(@Nullable Collection<@Nullable T> } @SafeVarargs - static IsNotIn isNotIn(@NonNull T... values) { + static IsNotIn isNotIn(T... values) { return IsNotIn.of(values); } - static IsNotIn isNotIn(Collection<@NonNull T> values) { + static IsNotIn isNotIn(Collection values) { return IsNotIn.of(values); } @@ -829,7 +828,7 @@ static IsBetween.Builder isBetween(T value1) { return IsBetween.isBetween(value1); } - static IsBetween.Builder isBetween(Supplier<@NonNull T> valueSupplier1) { + static IsBetween.Builder isBetween(Supplier valueSupplier1) { return isBetween(valueSupplier1.get()); } @@ -845,7 +844,7 @@ static IsNotBetween.Builder isNotBetween(T value1) { return IsNotBetween.isNotBetween(value1); } - static IsNotBetween.Builder isNotBetween(Supplier<@NonNull T> valueSupplier1) { + static IsNotBetween.Builder isNotBetween(Supplier valueSupplier1) { return isNotBetween(valueSupplier1.get()); } diff --git a/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java b/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java index 0c3bd188f..507d873c9 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java @@ -15,10 +15,20 @@ */ package org.mybatis.dynamic.sql.util; +import java.util.Collection; +import java.util.Objects; +import java.util.stream.Stream; + import org.jspecify.annotations.Nullable; public interface Utilities { static long safelyUnbox(@Nullable Long l) { return l == null ? 0 : l; } + + static Collection filterNulls(Collection<@Nullable T> values) { + // this method helps IntelliJ understand intended nullability + Stream st = values.stream().filter(Objects::nonNull); + return st.toList(); + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/AndGatherer.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/AndGatherer.java index c9514f3fa..8a587262a 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/AndGatherer.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/AndGatherer.java @@ -17,8 +17,6 @@ import java.util.function.Supplier; -import org.jspecify.annotations.NonNull; - /** * Utility class supporting the "and" part of a between condition. This class supports builders, so it is mutable. * @@ -40,7 +38,7 @@ public R and(T value2) { return build(value2); } - public R and(Supplier<@NonNull T> valueSupplier2) { + public R and(Supplier valueSupplier2) { return and(valueSupplier2.get()); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetween.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetween.java index 0f7fcd66a..ffc801508 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetween.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetween.java @@ -20,10 +20,9 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractTwoValueCondition; -public class IsBetween extends AbstractTwoValueCondition<@NonNull T> +public class IsBetween extends AbstractTwoValueCondition implements AbstractTwoValueCondition.Filterable, AbstractTwoValueCondition.Mappable { private static final IsBetween EMPTY = new IsBetween(-1, -1) { @Override @@ -63,23 +62,23 @@ public String operator2() { } @Override - public IsBetween filter(BiPredicate predicate) { + public IsBetween filter(BiPredicate predicate) { return filterSupport(predicate, IsBetween::empty, this); } @Override - public IsBetween filter(Predicate predicate) { + public IsBetween filter(Predicate predicate) { return filterSupport(predicate, IsBetween::empty, this); } @Override - public IsBetween map(Function mapper1, - Function mapper2) { + public IsBetween map(Function mapper1, + Function mapper2) { return mapSupport(mapper1, mapper2, IsBetween::new, IsBetween::empty); } @Override - public IsBetween map(Function mapper) { + public IsBetween map(Function mapper) { return map(mapper, mapper); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetweenWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetweenWhenPresent.java index bc9c12d37..7b09bcb44 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetweenWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsBetweenWhenPresent.java @@ -20,7 +20,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractTwoValueCondition; @@ -64,23 +63,23 @@ public String operator2() { } @Override - public IsBetweenWhenPresent filter(BiPredicate predicate) { + public IsBetweenWhenPresent filter(BiPredicate predicate) { return filterSupport(predicate, IsBetweenWhenPresent::empty, this); } @Override - public IsBetweenWhenPresent filter(Predicate predicate) { + public IsBetweenWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsBetweenWhenPresent::empty, this); } @Override - public IsBetweenWhenPresent map(Function mapper1, - Function mapper2) { + public IsBetweenWhenPresent map(Function mapper1, + Function mapper2) { return mapSupport(mapper1, mapper2, IsBetweenWhenPresent::of, IsBetweenWhenPresent::empty); } @Override - public IsBetweenWhenPresent map(Function mapper) { + public IsBetweenWhenPresent map(Function mapper) { return map(mapper, mapper); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualTo.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualTo.java index db8548f61..51e6e4d47 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualTo.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualTo.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; public class IsEqualTo extends AbstractSingleValueCondition @@ -57,12 +56,12 @@ public static IsEqualTo of(T value) { } @Override - public IsEqualTo filter(Predicate predicate) { + public IsEqualTo filter(Predicate predicate) { return filterSupport(predicate, IsEqualTo::empty, this); } @Override - public IsEqualTo map(Function mapper) { + public IsEqualTo map(Function mapper) { return mapSupport(mapper, IsEqualTo::new, IsEqualTo::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualToWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualToWhenPresent.java index 2dd8c746d..f06489076 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualToWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsEqualToWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; @@ -62,12 +61,12 @@ public static IsEqualToWhenPresent of(@Nullable T value) { } @Override - public IsEqualToWhenPresent filter(Predicate predicate) { + public IsEqualToWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsEqualToWhenPresent::empty, this); } @Override - public IsEqualToWhenPresent map(Function mapper) { + public IsEqualToWhenPresent map(Function mapper) { return mapSupport(mapper, IsEqualToWhenPresent::of, IsEqualToWhenPresent::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThan.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThan.java index 577a400f2..93be70911 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThan.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThan.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; public class IsGreaterThan extends AbstractSingleValueCondition @@ -56,12 +55,12 @@ public static IsGreaterThan of(T value) { } @Override - public IsGreaterThan filter(Predicate predicate) { + public IsGreaterThan filter(Predicate predicate) { return filterSupport(predicate, IsGreaterThan::empty, this); } @Override - public IsGreaterThan map(Function mapper) { + public IsGreaterThan map(Function mapper) { return mapSupport(mapper, IsGreaterThan::new, IsGreaterThan::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualTo.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualTo.java index 5fb4bd0d4..8373bf352 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualTo.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualTo.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; public class IsGreaterThanOrEqualTo extends AbstractSingleValueCondition @@ -56,12 +55,12 @@ public static IsGreaterThanOrEqualTo of(T value) { } @Override - public IsGreaterThanOrEqualTo filter(Predicate predicate) { + public IsGreaterThanOrEqualTo filter(Predicate predicate) { return filterSupport(predicate, IsGreaterThanOrEqualTo::empty, this); } @Override - public IsGreaterThanOrEqualTo map(Function mapper) { + public IsGreaterThanOrEqualTo map(Function mapper) { return mapSupport(mapper, IsGreaterThanOrEqualTo::new, IsGreaterThanOrEqualTo::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualToWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualToWhenPresent.java index 01f895dc9..ccb868c94 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualToWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualToWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; @@ -62,12 +61,12 @@ public static IsGreaterThanOrEqualToWhenPresent of(@Nullable T value) { } @Override - public IsGreaterThanOrEqualToWhenPresent filter(Predicate predicate) { + public IsGreaterThanOrEqualToWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsGreaterThanOrEqualToWhenPresent::empty, this); } @Override - public IsGreaterThanOrEqualToWhenPresent map(Function mapper) { + public IsGreaterThanOrEqualToWhenPresent map(Function mapper) { return mapSupport(mapper, IsGreaterThanOrEqualToWhenPresent::of, IsGreaterThanOrEqualToWhenPresent::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanWhenPresent.java index 779a15596..175b5fcf6 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; @@ -61,12 +60,12 @@ public static IsGreaterThanWhenPresent of(@Nullable T value) { } @Override - public IsGreaterThanWhenPresent filter(Predicate predicate) { + public IsGreaterThanWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsGreaterThanWhenPresent::empty, this); } @Override - public IsGreaterThanWhenPresent map(Function mapper) { + public IsGreaterThanWhenPresent map(Function mapper) { return mapSupport(mapper, IsGreaterThanWhenPresent::of, IsGreaterThanWhenPresent::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java index 67072dc8a..098db45f1 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsIn.java @@ -21,7 +21,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.Validator; @@ -52,12 +51,12 @@ public String operator() { } @Override - public IsIn filter(Predicate predicate) { + public IsIn filter(Predicate predicate) { return filterSupport(predicate, IsIn::new, this, IsIn::empty); } @Override - public IsIn map(Function mapper) { + public IsIn map(Function mapper) { return mapSupport(mapper, IsIn::new, IsIn::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java index d26a9c30f..3e2a22d2a 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitive.java @@ -21,7 +21,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -54,12 +53,12 @@ public String operator() { } @Override - public IsInCaseInsensitive filter(Predicate predicate) { + public IsInCaseInsensitive filter(Predicate predicate) { return filterSupport(predicate, IsInCaseInsensitive::new, this, IsInCaseInsensitive::empty); } @Override - public IsInCaseInsensitive map(Function mapper) { + public IsInCaseInsensitive map(Function mapper) { return mapSupport(mapper, IsInCaseInsensitive::new, IsInCaseInsensitive::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java index 6b4c1e1cd..fad595dcb 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java @@ -22,7 +22,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -49,13 +48,13 @@ public String operator() { } @Override - public IsInCaseInsensitiveWhenPresent filter(Predicate predicate) { + public IsInCaseInsensitiveWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsInCaseInsensitiveWhenPresent::new, this, IsInCaseInsensitiveWhenPresent::empty); } @Override - public IsInCaseInsensitiveWhenPresent map(Function mapper) { + public IsInCaseInsensitiveWhenPresent map(Function mapper) { return mapSupport(mapper, IsInCaseInsensitiveWhenPresent::new, IsInCaseInsensitiveWhenPresent::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java index abacd2690..45292dbf6 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java @@ -18,13 +18,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; +import org.mybatis.dynamic.sql.util.Utilities; public class IsInWhenPresent extends AbstractListValueCondition implements AbstractListValueCondition.Filterable, AbstractListValueCondition.Mappable { @@ -36,8 +35,8 @@ public static IsInWhenPresent empty() { return t; } - protected IsInWhenPresent(Collection values) { - super(values.stream().filter(Objects::nonNull).toList()); + protected IsInWhenPresent(Collection<@Nullable T> values) { + super(Utilities.filterNulls(values)); } @Override @@ -46,12 +45,12 @@ public String operator() { } @Override - public IsInWhenPresent filter(Predicate predicate) { + public IsInWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsInWhenPresent::new, this, IsInWhenPresent::empty); } @Override - public IsInWhenPresent map(Function mapper) { + public IsInWhenPresent map(Function mapper) { return mapSupport(mapper, IsInWhenPresent::of, IsInWhenPresent::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThan.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThan.java index ffe66bd97..3ed383fbd 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThan.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThan.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; public class IsLessThan extends AbstractSingleValueCondition @@ -57,12 +56,12 @@ public static IsLessThan of(T value) { } @Override - public IsLessThan filter(Predicate predicate) { + public IsLessThan filter(Predicate predicate) { return filterSupport(predicate, IsLessThan::empty, this); } @Override - public IsLessThan map(Function mapper) { + public IsLessThan map(Function mapper) { return mapSupport(mapper, IsLessThan::new, IsLessThan::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualTo.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualTo.java index a73707e3e..1b92e0c40 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualTo.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualTo.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; public class IsLessThanOrEqualTo extends AbstractSingleValueCondition @@ -56,12 +55,12 @@ public static IsLessThanOrEqualTo of(T value) { } @Override - public IsLessThanOrEqualTo filter(Predicate predicate) { + public IsLessThanOrEqualTo filter(Predicate predicate) { return filterSupport(predicate, IsLessThanOrEqualTo::empty, this); } @Override - public IsLessThanOrEqualTo map(Function mapper) { + public IsLessThanOrEqualTo map(Function mapper) { return mapSupport(mapper, IsLessThanOrEqualTo::new, IsLessThanOrEqualTo::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualToWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualToWhenPresent.java index d944b7a45..3cdc8ff39 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualToWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanOrEqualToWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; @@ -61,12 +60,12 @@ public static IsLessThanOrEqualToWhenPresent of(@Nullable T value) { } @Override - public IsLessThanOrEqualToWhenPresent filter(Predicate predicate) { + public IsLessThanOrEqualToWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsLessThanOrEqualToWhenPresent::empty, this); } @Override - public IsLessThanOrEqualToWhenPresent map(Function mapper) { + public IsLessThanOrEqualToWhenPresent map(Function mapper) { return mapSupport(mapper, IsLessThanOrEqualToWhenPresent::of, IsLessThanOrEqualToWhenPresent::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanWhenPresent.java index 830bf788c..78a07f9a2 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLessThanWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; @@ -62,12 +61,12 @@ public static IsLessThanWhenPresent of(@Nullable T value) { } @Override - public IsLessThanWhenPresent filter(Predicate predicate) { + public IsLessThanWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsLessThanWhenPresent::empty, this); } @Override - public IsLessThanWhenPresent map(Function mapper) { + public IsLessThanWhenPresent map(Function mapper) { return mapSupport(mapper, IsLessThanWhenPresent::of, IsLessThanWhenPresent::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLike.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLike.java index f2d2a419a..e738bda4c 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLike.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLike.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; public class IsLike extends AbstractSingleValueCondition @@ -57,12 +56,12 @@ public static IsLike of(T value) { } @Override - public IsLike filter(Predicate predicate) { + public IsLike filter(Predicate predicate) { return filterSupport(predicate, IsLike::empty, this); } @Override - public IsLike map(Function mapper) { + public IsLike map(Function mapper) { return mapSupport(mapper, IsLike::new, IsLike::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java index 43525e287..ffdc2bc7d 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitive.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -54,12 +53,12 @@ public String operator() { } @Override - public IsLikeCaseInsensitive filter(Predicate predicate) { + public IsLikeCaseInsensitive filter(Predicate predicate) { return filterSupport(predicate, IsLikeCaseInsensitive::empty, this); } @Override - public IsLikeCaseInsensitive map(Function mapper) { + public IsLikeCaseInsensitive map(Function mapper) { return mapSupport(mapper, IsLikeCaseInsensitive::new, IsLikeCaseInsensitive::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitiveWhenPresent.java index 60307625f..ae8398aef 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitiveWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitiveWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -56,12 +55,12 @@ public String operator() { } @Override - public IsLikeCaseInsensitiveWhenPresent filter(Predicate predicate) { + public IsLikeCaseInsensitiveWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsLikeCaseInsensitiveWhenPresent::empty, this); } @Override - public IsLikeCaseInsensitiveWhenPresent map(Function mapper) { + public IsLikeCaseInsensitiveWhenPresent map(Function mapper) { return mapSupport(mapper, IsLikeCaseInsensitiveWhenPresent::of, IsLikeCaseInsensitiveWhenPresent::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeWhenPresent.java index fc20722a2..a69e55356 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; @@ -62,12 +61,12 @@ public static IsLikeWhenPresent of(@Nullable T value) { } @Override - public IsLikeWhenPresent filter(Predicate predicate) { + public IsLikeWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsLikeWhenPresent::empty, this); } @Override - public IsLikeWhenPresent map(Function mapper) { + public IsLikeWhenPresent map(Function mapper) { return mapSupport(mapper, IsLikeWhenPresent::of, IsLikeWhenPresent::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetween.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetween.java index b3d0d59ff..836e3c741 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetween.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetween.java @@ -20,7 +20,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractTwoValueCondition; public class IsNotBetween extends AbstractTwoValueCondition @@ -63,23 +62,23 @@ public String operator2() { } @Override - public IsNotBetween filter(BiPredicate predicate) { + public IsNotBetween filter(BiPredicate predicate) { return filterSupport(predicate, IsNotBetween::empty, this); } @Override - public IsNotBetween filter(Predicate predicate) { + public IsNotBetween filter(Predicate predicate) { return filterSupport(predicate, IsNotBetween::empty, this); } @Override - public IsNotBetween map(Function mapper1, - Function mapper2) { + public IsNotBetween map(Function mapper1, + Function mapper2) { return mapSupport(mapper1, mapper2, IsNotBetween::new, IsNotBetween::empty); } @Override - public IsNotBetween map(Function mapper) { + public IsNotBetween map(Function mapper) { return map(mapper, mapper); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetweenWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetweenWhenPresent.java index 3c9c8fc9f..020b651f2 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetweenWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotBetweenWhenPresent.java @@ -20,7 +20,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractTwoValueCondition; @@ -64,23 +63,23 @@ public String operator2() { } @Override - public IsNotBetweenWhenPresent filter(BiPredicate predicate) { + public IsNotBetweenWhenPresent filter(BiPredicate predicate) { return filterSupport(predicate, IsNotBetweenWhenPresent::empty, this); } @Override - public IsNotBetweenWhenPresent filter(Predicate predicate) { + public IsNotBetweenWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsNotBetweenWhenPresent::empty, this); } @Override - public IsNotBetweenWhenPresent map(Function mapper1, - Function mapper2) { + public IsNotBetweenWhenPresent map(Function mapper1, + Function mapper2) { return mapSupport(mapper1, mapper2, IsNotBetweenWhenPresent::of, IsNotBetweenWhenPresent::empty); } @Override - public IsNotBetweenWhenPresent map(Function mapper) { + public IsNotBetweenWhenPresent map(Function mapper) { return map(mapper, mapper); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualTo.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualTo.java index fc0292070..39070c2e8 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualTo.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualTo.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; public class IsNotEqualTo extends AbstractSingleValueCondition @@ -56,12 +55,12 @@ public static IsNotEqualTo of(T value) { } @Override - public IsNotEqualTo filter(Predicate predicate) { + public IsNotEqualTo filter(Predicate predicate) { return filterSupport(predicate, IsNotEqualTo::empty, this); } @Override - public IsNotEqualTo map(Function mapper) { + public IsNotEqualTo map(Function mapper) { return mapSupport(mapper, IsNotEqualTo::new, IsNotEqualTo::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualToWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualToWhenPresent.java index 1fff2d9ba..07ab3f6cf 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualToWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotEqualToWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; @@ -61,12 +60,12 @@ public static IsNotEqualToWhenPresent of(@Nullable T value) { } @Override - public IsNotEqualToWhenPresent filter(Predicate predicate) { + public IsNotEqualToWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsNotEqualToWhenPresent::empty, this); } @Override - public IsNotEqualToWhenPresent map(Function mapper) { + public IsNotEqualToWhenPresent map(Function mapper) { return mapSupport(mapper, IsNotEqualToWhenPresent::of, IsNotEqualToWhenPresent::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java index af6c248f4..e6b408fc8 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotIn.java @@ -21,7 +21,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.Validator; @@ -52,12 +51,12 @@ public String operator() { } @Override - public IsNotIn filter(Predicate predicate) { + public IsNotIn filter(Predicate predicate) { return filterSupport(predicate, IsNotIn::new, this, IsNotIn::empty); } @Override - public IsNotIn map(Function mapper) { + public IsNotIn map(Function mapper) { return mapSupport(mapper, IsNotIn::new, IsNotIn::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java index 98a9ca9db..2bc802ab8 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitive.java @@ -21,7 +21,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -54,12 +53,12 @@ public String operator() { } @Override - public IsNotInCaseInsensitive filter(Predicate predicate) { + public IsNotInCaseInsensitive filter(Predicate predicate) { return filterSupport(predicate, IsNotInCaseInsensitive::new, this, IsNotInCaseInsensitive::empty); } @Override - public IsNotInCaseInsensitive map(Function mapper) { + public IsNotInCaseInsensitive map(Function mapper) { return mapSupport(mapper, IsNotInCaseInsensitive::new, IsNotInCaseInsensitive::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java index 6852c67fd..90f9b0b7a 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java @@ -22,7 +22,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -49,13 +48,13 @@ public String operator() { } @Override - public IsNotInCaseInsensitiveWhenPresent filter(Predicate predicate) { + public IsNotInCaseInsensitiveWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsNotInCaseInsensitiveWhenPresent::new, this, IsNotInCaseInsensitiveWhenPresent::empty); } @Override - public IsNotInCaseInsensitiveWhenPresent map(Function mapper) { + public IsNotInCaseInsensitiveWhenPresent map(Function mapper) { return mapSupport(mapper, IsNotInCaseInsensitiveWhenPresent::new, IsNotInCaseInsensitiveWhenPresent::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java index 33efb1782..18c1a67e6 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java @@ -18,13 +18,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; +import org.mybatis.dynamic.sql.util.Utilities; public class IsNotInWhenPresent extends AbstractListValueCondition implements AbstractListValueCondition.Filterable, AbstractListValueCondition.Mappable { @@ -36,8 +35,8 @@ public static IsNotInWhenPresent empty() { return t; } - protected IsNotInWhenPresent(Collection values) { - super(values.stream().filter(Objects::nonNull).toList()); + protected IsNotInWhenPresent(Collection<@Nullable T> values) { + super(Utilities.filterNulls(values)); } @Override @@ -46,12 +45,12 @@ public String operator() { } @Override - public IsNotInWhenPresent filter(Predicate predicate) { + public IsNotInWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsNotInWhenPresent::new, this, IsNotInWhenPresent::empty); } @Override - public IsNotInWhenPresent map(Function mapper) { + public IsNotInWhenPresent map(Function mapper) { return mapSupport(mapper, IsNotInWhenPresent::new, IsNotInWhenPresent::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLike.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLike.java index b5b82d675..a62dc3e9e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLike.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLike.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; public class IsNotLike extends AbstractSingleValueCondition @@ -56,12 +55,12 @@ public static IsNotLike of(T value) { } @Override - public IsNotLike filter(Predicate predicate) { + public IsNotLike filter(Predicate predicate) { return filterSupport(predicate, IsNotLike::empty, this); } @Override - public IsNotLike map(Function mapper) { + public IsNotLike map(Function mapper) { return mapSupport(mapper, IsNotLike::new, IsNotLike::empty); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java index 1604deb3b..6bd943227 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitive.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -54,12 +53,12 @@ public String operator() { } @Override - public IsNotLikeCaseInsensitive filter(Predicate predicate) { + public IsNotLikeCaseInsensitive filter(Predicate predicate) { return filterSupport(predicate, IsNotLikeCaseInsensitive::empty, this); } @Override - public IsNotLikeCaseInsensitive map(Function mapper) { + public IsNotLikeCaseInsensitive map(Function mapper) { return mapSupport(mapper, IsNotLikeCaseInsensitive::new, IsNotLikeCaseInsensitive::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitiveWhenPresent.java index cc0b04549..df07202f9 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitiveWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitiveWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; @@ -56,12 +55,12 @@ public String operator() { } @Override - public IsNotLikeCaseInsensitiveWhenPresent filter(Predicate predicate) { + public IsNotLikeCaseInsensitiveWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsNotLikeCaseInsensitiveWhenPresent::empty, this); } @Override - public IsNotLikeCaseInsensitiveWhenPresent map(Function mapper) { + public IsNotLikeCaseInsensitiveWhenPresent map(Function mapper) { return mapSupport(mapper, IsNotLikeCaseInsensitiveWhenPresent::of, IsNotLikeCaseInsensitiveWhenPresent::empty); } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeWhenPresent.java index a3571b0ee..d018c9062 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeWhenPresent.java @@ -19,7 +19,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractSingleValueCondition; @@ -61,12 +60,12 @@ public static IsNotLikeWhenPresent of(@Nullable T value) { } @Override - public IsNotLikeWhenPresent filter(Predicate predicate) { + public IsNotLikeWhenPresent filter(Predicate predicate) { return filterSupport(predicate, IsNotLikeWhenPresent::empty, this); } @Override - public IsNotLikeWhenPresent map(Function mapper) { + public IsNotLikeWhenPresent map(Function mapper) { return mapSupport(mapper, IsNotLikeWhenPresent::of, IsNotLikeWhenPresent::empty); } } diff --git a/src/test/java/examples/array/NamesTableDynamicSqlSupport.java b/src/test/java/examples/array/NamesTableDynamicSqlSupport.java index ce3cfbb8f..eb7c0625b 100644 --- a/src/test/java/examples/array/NamesTableDynamicSqlSupport.java +++ b/src/test/java/examples/array/NamesTableDynamicSqlSupport.java @@ -17,13 +17,14 @@ import java.sql.JDBCType; +import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public class NamesTableDynamicSqlSupport { public static final NamesTable namesTable = new NamesTable(); public static final SqlColumn id = namesTable.id; - public static final SqlColumn names = namesTable.names; + public static final SqlColumn names = namesTable.names; public static final class NamesTable extends SqlTable { public NamesTable() { @@ -31,7 +32,7 @@ public NamesTable() { } public final SqlColumn id = column("id", JDBCType.INTEGER); - public final SqlColumn names = column("names", JDBCType.ARRAY, + public final SqlColumn names = column("names", JDBCType.ARRAY, "examples.array.StringArrayTypeHandler"); } } diff --git a/src/test/java/examples/array/package-info.java b/src/test/java/examples/array/package-info.java new file mode 100644 index 000000000..3b3e3a5a3 --- /dev/null +++ b/src/test/java/examples/array/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.array; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/column/comparison/ColumnComparisonConfiguration.java b/src/test/java/examples/column/comparison/ColumnComparisonConfiguration.java index 12bb66cf3..6f364dc10 100644 --- a/src/test/java/examples/column/comparison/ColumnComparisonConfiguration.java +++ b/src/test/java/examples/column/comparison/ColumnComparisonConfiguration.java @@ -25,6 +25,8 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import java.util.Objects; + @Configuration @MapperScan("examples.column.comparison") public class ColumnComparisonConfiguration { @@ -41,6 +43,6 @@ public DataSource datasource() { public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); - return factoryBean.getObject(); + return Objects.requireNonNull(factoryBean.getObject()); } } diff --git a/src/test/java/examples/column/comparison/package-info.java b/src/test/java/examples/column/comparison/package-info.java new file mode 100644 index 000000000..2ec154bb0 --- /dev/null +++ b/src/test/java/examples/column/comparison/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.column.comparison; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/complexquery/package-info.java b/src/test/java/examples/complexquery/package-info.java new file mode 100644 index 000000000..d10955a0a --- /dev/null +++ b/src/test/java/examples/complexquery/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.complexquery; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/generated/always/mybatis/package-info.java b/src/test/java/examples/generated/always/mybatis/package-info.java new file mode 100644 index 000000000..86d24bc7c --- /dev/null +++ b/src/test/java/examples/generated/always/mybatis/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.generated.always.mybatis; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/generated/always/spring/package-info.java b/src/test/java/examples/generated/always/spring/package-info.java new file mode 100644 index 000000000..10d19611c --- /dev/null +++ b/src/test/java/examples/generated/always/spring/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.generated.always.spring; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/groupby/package-info.java b/src/test/java/examples/groupby/package-info.java new file mode 100644 index 000000000..589e995e4 --- /dev/null +++ b/src/test/java/examples/groupby/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.groupby; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/mariadb/package-info.java b/src/test/java/examples/mariadb/package-info.java new file mode 100644 index 000000000..1f939000b --- /dev/null +++ b/src/test/java/examples/mariadb/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.mariadb; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/postgres/package-info.java b/src/test/java/examples/postgres/package-info.java new file mode 100644 index 000000000..ed5684db7 --- /dev/null +++ b/src/test/java/examples/postgres/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.postgres; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/springbatch/mapper/package-info.java b/src/test/java/examples/springbatch/mapper/package-info.java new file mode 100644 index 000000000..3c776ca26 --- /dev/null +++ b/src/test/java/examples/springbatch/mapper/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.springbatch.mapper; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/type_conversion/MyFilesDynamicSqlSupport.java b/src/test/java/examples/type_conversion/MyFilesDynamicSqlSupport.java index 7a58cab44..6bed1ce53 100644 --- a/src/test/java/examples/type_conversion/MyFilesDynamicSqlSupport.java +++ b/src/test/java/examples/type_conversion/MyFilesDynamicSqlSupport.java @@ -17,17 +17,18 @@ import java.sql.JDBCType; +import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; public final class MyFilesDynamicSqlSupport { public static final MyFiles myfiles = new MyFiles(); public static final SqlColumn fileId = myfiles.fileId; - public static final SqlColumn fileContents = myfiles.fileContents; + public static final SqlColumn fileContents = myfiles.fileContents; public static final class MyFiles extends SqlTable { public final SqlColumn fileId = column("file_id", JDBCType.INTEGER); - public final SqlColumn fileContents = column("file_contents", JDBCType.LONGVARBINARY); + public final SqlColumn fileContents = column("file_contents", JDBCType.LONGVARBINARY); public MyFiles() { super("MyFiles"); diff --git a/src/test/java/examples/type_conversion/ToBase64.java b/src/test/java/examples/type_conversion/ToBase64.java index 46cdb26ed..56437bfcb 100644 --- a/src/test/java/examples/type_conversion/ToBase64.java +++ b/src/test/java/examples/type_conversion/ToBase64.java @@ -18,13 +18,14 @@ import java.sql.JDBCType; import java.util.Optional; +import org.jspecify.annotations.NonNull; import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.BindableColumn; import org.mybatis.dynamic.sql.render.RenderingContext; import org.mybatis.dynamic.sql.select.function.AbstractTypeConvertingFunction; import org.mybatis.dynamic.sql.util.FragmentAndParameters; -public class ToBase64 extends AbstractTypeConvertingFunction { +public class ToBase64 extends AbstractTypeConvertingFunction { protected ToBase64(BasicColumn column) { super(column); @@ -46,7 +47,7 @@ protected ToBase64 copy() { return new ToBase64(column); } - public static ToBase64 toBase64(BindableColumn column) { + public static ToBase64 toBase64(BindableColumn column) { return new ToBase64(column); } } diff --git a/src/test/java/issues/gh100/package-info.java b/src/test/java/issues/gh100/package-info.java new file mode 100644 index 000000000..52ab28b7f --- /dev/null +++ b/src/test/java/issues/gh100/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package issues.gh100; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/issues/gh105/package-info.java b/src/test/java/issues/gh105/package-info.java new file mode 100644 index 000000000..a204f10fe --- /dev/null +++ b/src/test/java/issues/gh105/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package issues.gh105; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/issues/gh324/ObservableCache.java b/src/test/java/issues/gh324/ObservableCache.java index d52a2bcd0..ebc074e9b 100644 --- a/src/test/java/issues/gh324/ObservableCache.java +++ b/src/test/java/issues/gh324/ObservableCache.java @@ -16,6 +16,7 @@ package issues.gh324; import org.apache.ibatis.cache.impl.PerpetualCache; +import org.jspecify.annotations.Nullable; public class ObservableCache extends PerpetualCache { @@ -48,7 +49,7 @@ public void putObject(Object key, Object value) { } @Override - public Object getObject(Object key) { + public @Nullable Object getObject(Object key) { Object answer = super.getObject(key); if (key.toString().contains("select id, name from NameTable where id = ?")) { diff --git a/src/test/java/issues/gh324/package-info.java b/src/test/java/issues/gh324/package-info.java new file mode 100644 index 000000000..72fdce9c1 --- /dev/null +++ b/src/test/java/issues/gh324/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package issues.gh324; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/issues/lhg142/package-info.java b/src/test/java/issues/lhg142/package-info.java new file mode 100644 index 000000000..b28d6c387 --- /dev/null +++ b/src/test/java/issues/lhg142/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package issues.lhg142; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/delete/package-info.java b/src/test/java/org/mybatis/dynamic/sql/delete/package-info.java new file mode 100644 index 000000000..a7bfc9d26 --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/delete/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.delete; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/insert/package-info.java b/src/test/java/org/mybatis/dynamic/sql/insert/package-info.java new file mode 100644 index 000000000..5ef9f74ea --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/insert/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.insert; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/insert/render/package-info.java b/src/test/java/org/mybatis/dynamic/sql/insert/render/package-info.java new file mode 100644 index 000000000..02cfd6efa --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/insert/render/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.insert.render; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/mybatis3/package-info.java b/src/test/java/org/mybatis/dynamic/sql/mybatis3/package-info.java new file mode 100644 index 000000000..52eaa038c --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/mybatis3/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.mybatis3; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/package-info.java b/src/test/java/org/mybatis/dynamic/sql/package-info.java new file mode 100644 index 000000000..7555e2e26 --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/select/package-info.java b/src/test/java/org/mybatis/dynamic/sql/select/package-info.java new file mode 100644 index 000000000..7e49fd4ec --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/select/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.select; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/subselect/package-info.java b/src/test/java/org/mybatis/dynamic/sql/subselect/package-info.java new file mode 100644 index 000000000..2195e6c35 --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/subselect/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.subselect; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/update/package-info.java b/src/test/java/org/mybatis/dynamic/sql/update/package-info.java new file mode 100644 index 000000000..b1b75a4f4 --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/update/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.update; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/util/package-info.java b/src/test/java/org/mybatis/dynamic/sql/util/package-info.java new file mode 100644 index 000000000..82bcfdd13 --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/util/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.util; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/where/condition/package-info.java b/src/test/java/org/mybatis/dynamic/sql/where/condition/package-info.java new file mode 100644 index 000000000..3457063de --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/where/condition/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.where.condition; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/where/package-info.java b/src/test/java/org/mybatis/dynamic/sql/where/package-info.java new file mode 100644 index 000000000..194b40e86 --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/where/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.where; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/mybatis/dynamic/sql/where/render/package-info.java b/src/test/java/org/mybatis/dynamic/sql/where/render/package-info.java new file mode 100644 index 000000000..cde1387a3 --- /dev/null +++ b/src/test/java/org/mybatis/dynamic/sql/where/render/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package org.mybatis.dynamic.sql.where.render; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KIsLikeEscape.kt b/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KIsLikeEscape.kt index 6746dc03e..52ddc9e7e 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KIsLikeEscape.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KIsLikeEscape.kt @@ -65,6 +65,6 @@ private class EmptyIsLikeEscape : KIsLikeEscape(-1) { private val EMPTY: KIsLikeEscape = EmptyIsLikeEscape() @Suppress("UNCHECKED_CAST") - internal fun empty(): KIsLikeEscape = EMPTY as KIsLikeEscape + fun empty(): KIsLikeEscape = EMPTY as KIsLikeEscape } } From acf0dc533a91fd2a55e0b477f740b0fecc966095 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 7 Oct 2025 17:20:35 -0400 Subject: [PATCH 31/68] Make null filtering method more general --- src/main/java/org/mybatis/dynamic/sql/util/Utilities.java | 5 ++--- .../sql/where/condition/IsInCaseInsensitiveWhenPresent.java | 4 ++-- .../mybatis/dynamic/sql/where/condition/IsInWhenPresent.java | 2 +- .../where/condition/IsNotInCaseInsensitiveWhenPresent.java | 4 ++-- .../dynamic/sql/where/condition/IsNotInWhenPresent.java | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java b/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java index 507d873c9..b0c27081e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java +++ b/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java @@ -26,9 +26,8 @@ static long safelyUnbox(@Nullable Long l) { return l == null ? 0 : l; } - static Collection filterNulls(Collection<@Nullable T> values) { + static Stream filterNulls(Collection<@Nullable T> values) { // this method helps IntelliJ understand intended nullability - Stream st = values.stream().filter(Objects::nonNull); - return st.toList(); + return values.stream().filter(Objects::nonNull); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java index fad595dcb..3400a8337 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInCaseInsensitiveWhenPresent.java @@ -18,13 +18,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; +import org.mybatis.dynamic.sql.util.Utilities; public class IsInCaseInsensitiveWhenPresent extends AbstractListValueCondition implements CaseInsensitiveRenderableCondition, AbstractListValueCondition.Filterable, @@ -39,7 +39,7 @@ public static IsInCaseInsensitiveWhenPresent empty() { } protected IsInCaseInsensitiveWhenPresent(Collection values) { - super(values.stream().filter(Objects::nonNull).map(StringUtilities::upperCaseIfPossible).toList()); + super(Utilities.filterNulls(values).map(StringUtilities::upperCaseIfPossible).toList()); } @Override diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java index 45292dbf6..246938da0 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsInWhenPresent.java @@ -36,7 +36,7 @@ public static IsInWhenPresent empty() { } protected IsInWhenPresent(Collection<@Nullable T> values) { - super(Utilities.filterNulls(values)); + super(Utilities.filterNulls(values).toList()); } @Override diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java index 90f9b0b7a..2640e062f 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInCaseInsensitiveWhenPresent.java @@ -18,13 +18,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; import org.jspecify.annotations.Nullable; import org.mybatis.dynamic.sql.AbstractListValueCondition; import org.mybatis.dynamic.sql.util.StringUtilities; +import org.mybatis.dynamic.sql.util.Utilities; public class IsNotInCaseInsensitiveWhenPresent extends AbstractListValueCondition implements CaseInsensitiveRenderableCondition, AbstractListValueCondition.Filterable, @@ -39,7 +39,7 @@ public static IsNotInCaseInsensitiveWhenPresent empty() { } protected IsNotInCaseInsensitiveWhenPresent(Collection values) { - super(values.stream().filter(Objects::nonNull).map(StringUtilities::upperCaseIfPossible).toList()); + super(Utilities.filterNulls(values).map(StringUtilities::upperCaseIfPossible).toList()); } @Override diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java index 18c1a67e6..6624a50ad 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotInWhenPresent.java @@ -36,7 +36,7 @@ public static IsNotInWhenPresent empty() { } protected IsNotInWhenPresent(Collection<@Nullable T> values) { - super(Utilities.filterNulls(values)); + super(Utilities.filterNulls(values).toList()); } @Override From 19ac640b4147f2c04c1895b04c16fead48183130 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 7 Oct 2025 17:24:34 -0400 Subject: [PATCH 32/68] MyBatis result classes need Nullable fields --- src/test/java/examples/array/NamesRecord.java | 10 +++--- .../always/mybatis/GeneratedKey.java | 6 ++-- src/test/java/examples/joins/OrderMaster.java | 14 ++++---- .../java/examples/simple/AddressRecord.java | 22 +++++++------ .../examples/simple/PersonWithAddress.java | 32 ++++++++++--------- src/test/java/issues/gh324/NameRecord.java | 10 +++--- .../sql/mybatis3/InsertStatementTest.java | 17 +++++----- 7 files changed, 62 insertions(+), 49 deletions(-) diff --git a/src/test/java/examples/array/NamesRecord.java b/src/test/java/examples/array/NamesRecord.java index 65765ccfe..3a90985d2 100644 --- a/src/test/java/examples/array/NamesRecord.java +++ b/src/test/java/examples/array/NamesRecord.java @@ -15,11 +15,13 @@ */ package examples.array; +import org.jspecify.annotations.Nullable; + public class NamesRecord { - private Integer id; - private String[] names; + private @Nullable Integer id; + private @Nullable String[] names; - public Integer getId() { + public @Nullable Integer getId() { return id; } @@ -27,7 +29,7 @@ public void setId(Integer id) { this.id = id; } - public String[] getNames() { + public @Nullable String[] getNames() { return names; } diff --git a/src/test/java/examples/generated/always/mybatis/GeneratedKey.java b/src/test/java/examples/generated/always/mybatis/GeneratedKey.java index 8a65ac7ec..7509202c5 100644 --- a/src/test/java/examples/generated/always/mybatis/GeneratedKey.java +++ b/src/test/java/examples/generated/always/mybatis/GeneratedKey.java @@ -15,11 +15,13 @@ */ package examples.generated.always.mybatis; +import org.jspecify.annotations.Nullable; + public class GeneratedKey { - private Integer key; + private @Nullable Integer key; - public Integer getKey() { + public @Nullable Integer getKey() { return key; } diff --git a/src/test/java/examples/joins/OrderMaster.java b/src/test/java/examples/joins/OrderMaster.java index 9972b6db9..2d75b4b59 100644 --- a/src/test/java/examples/joins/OrderMaster.java +++ b/src/test/java/examples/joins/OrderMaster.java @@ -18,12 +18,14 @@ import java.util.Date; import java.util.List; +import org.jspecify.annotations.Nullable; + public class OrderMaster { - private Integer id; - private Date orderDate; - private List details; + private @Nullable Integer id; + private @Nullable Date orderDate; + private @Nullable List details; - public Integer getId() { + public @Nullable Integer getId() { return id; } @@ -31,7 +33,7 @@ public void setId(Integer id) { this.id = id; } - public Date getOrderDate() { + public @Nullable Date getOrderDate() { return orderDate; } @@ -39,7 +41,7 @@ public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } - public List getDetails() { + public @Nullable List getDetails() { return details; } diff --git a/src/test/java/examples/simple/AddressRecord.java b/src/test/java/examples/simple/AddressRecord.java index 5af471126..6e72df889 100644 --- a/src/test/java/examples/simple/AddressRecord.java +++ b/src/test/java/examples/simple/AddressRecord.java @@ -15,14 +15,16 @@ */ package examples.simple; +import org.jspecify.annotations.Nullable; + public class AddressRecord { - private Integer id; - private String streetAddress; - private String city; - private String state; - private AddressType addressType; + private @Nullable Integer id; + private @Nullable String streetAddress; + private @Nullable String city; + private @Nullable String state; + private @Nullable AddressType addressType; - public Integer getId() { + public @Nullable Integer getId() { return id; } @@ -30,7 +32,7 @@ public void setId(Integer id) { this.id = id; } - public String getStreetAddress() { + public @Nullable String getStreetAddress() { return streetAddress; } @@ -38,7 +40,7 @@ public void setStreetAddress(String streetAddress) { this.streetAddress = streetAddress; } - public String getCity() { + public @Nullable String getCity() { return city; } @@ -46,7 +48,7 @@ public void setCity(String city) { this.city = city; } - public String getState() { + public @Nullable String getState() { return state; } @@ -54,7 +56,7 @@ public void setState(String state) { this.state = state; } - public AddressType getAddressType() { + public @Nullable AddressType getAddressType() { return addressType; } diff --git a/src/test/java/examples/simple/PersonWithAddress.java b/src/test/java/examples/simple/PersonWithAddress.java index ec38997e5..49ef7e429 100644 --- a/src/test/java/examples/simple/PersonWithAddress.java +++ b/src/test/java/examples/simple/PersonWithAddress.java @@ -15,18 +15,20 @@ */ package examples.simple; +import org.jspecify.annotations.Nullable; + import java.util.Date; public class PersonWithAddress { - private Integer id; - private String firstName; - private LastName lastName; - private Date birthDate; - private Boolean employed; - private String occupation; - private AddressRecord address; - - public Integer getId() { + private @Nullable Integer id; + private @Nullable String firstName; + private @Nullable LastName lastName; + private @Nullable Date birthDate; + private @Nullable Boolean employed; + private @Nullable String occupation; + private @Nullable AddressRecord address; + + public @Nullable Integer getId() { return id; } @@ -34,7 +36,7 @@ public void setId(Integer id) { this.id = id; } - public String getFirstName() { + public @Nullable String getFirstName() { return firstName; } @@ -42,7 +44,7 @@ public void setFirstName(String firstName) { this.firstName = firstName; } - public LastName getLastName() { + public @Nullable LastName getLastName() { return lastName; } @@ -50,7 +52,7 @@ public void setLastName(LastName lastName) { this.lastName = lastName; } - public Date getBirthDate() { + public @Nullable Date getBirthDate() { return birthDate; } @@ -58,7 +60,7 @@ public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } - public String getOccupation() { + public @Nullable String getOccupation() { return occupation; } @@ -66,7 +68,7 @@ public void setOccupation(String occupation) { this.occupation = occupation; } - public Boolean getEmployed() { + public @Nullable Boolean getEmployed() { return employed; } @@ -74,7 +76,7 @@ public void setEmployed(Boolean employed) { this.employed = employed; } - public AddressRecord getAddress() { + public @Nullable AddressRecord getAddress() { return address; } diff --git a/src/test/java/issues/gh324/NameRecord.java b/src/test/java/issues/gh324/NameRecord.java index a00286095..b5de1c844 100644 --- a/src/test/java/issues/gh324/NameRecord.java +++ b/src/test/java/issues/gh324/NameRecord.java @@ -15,13 +15,15 @@ */ package issues.gh324; +import org.jspecify.annotations.Nullable; + import java.io.Serializable; public class NameRecord implements Serializable { - private Integer id; - private String name; + private @Nullable Integer id; + private @Nullable String name; - public Integer getId() { + public @Nullable Integer getId() { return id; } @@ -29,7 +31,7 @@ public void setId(Integer id) { this.id = id; } - public String getName() { + public @Nullable String getName() { return name; } diff --git a/src/test/java/org/mybatis/dynamic/sql/mybatis3/InsertStatementTest.java b/src/test/java/org/mybatis/dynamic/sql/mybatis3/InsertStatementTest.java index 5e0ef7f7b..77b63f65f 100644 --- a/src/test/java/org/mybatis/dynamic/sql/mybatis3/InsertStatementTest.java +++ b/src/test/java/org/mybatis/dynamic/sql/mybatis3/InsertStatementTest.java @@ -20,6 +20,7 @@ import java.sql.JDBCType; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.SqlTable; @@ -77,12 +78,12 @@ void testSelectiveInsertStatementBuilder() { } static class TestRecord { - private Integer id; - private String firstName; - private String lastName; - private String occupation; + private @Nullable Integer id; + private @Nullable String firstName; + private @Nullable String lastName; + private @Nullable String occupation; - Integer getId() { + @Nullable Integer getId() { return id; } @@ -90,7 +91,7 @@ void setId(Integer id) { this.id = id; } - String getFirstName() { + @Nullable String getFirstName() { return firstName; } @@ -98,7 +99,7 @@ void setFirstName(String firstName) { this.firstName = firstName; } - String getLastName() { + @Nullable String getLastName() { return lastName; } @@ -106,7 +107,7 @@ void setLastName(String lastName) { this.lastName = lastName; } - String getOccupation() { + @Nullable String getOccupation() { return occupation; } From e720f1ff621b9afcfd7d3ef69e4cab84cbfecfdb Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 7 Oct 2025 17:47:24 -0400 Subject: [PATCH 33/68] NameRecord can be a record --- src/test/java/issues/gh324/NameRecord.java | 22 +----------------- src/test/java/issues/gh324/NameService.java | 16 ++++--------- .../java/issues/gh324/NameTableMapper.java | 23 +++++++++++-------- src/test/java/issues/gh324/TestUtils.java | 8 +++---- .../gh324/spring/SpringNameService.java | 12 +++------- 5 files changed, 26 insertions(+), 55 deletions(-) diff --git a/src/test/java/issues/gh324/NameRecord.java b/src/test/java/issues/gh324/NameRecord.java index b5de1c844..9b02e327d 100644 --- a/src/test/java/issues/gh324/NameRecord.java +++ b/src/test/java/issues/gh324/NameRecord.java @@ -15,27 +15,7 @@ */ package issues.gh324; -import org.jspecify.annotations.Nullable; - import java.io.Serializable; -public class NameRecord implements Serializable { - private @Nullable Integer id; - private @Nullable String name; - - public @Nullable Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public @Nullable String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } +public record NameRecord (Integer id, String name) implements Serializable { } diff --git a/src/test/java/issues/gh324/NameService.java b/src/test/java/issues/gh324/NameService.java index d7de92076..a303632bc 100644 --- a/src/test/java/issues/gh324/NameService.java +++ b/src/test/java/issues/gh324/NameService.java @@ -60,9 +60,7 @@ public NameService() { public void insertRecord() { try (SqlSession session = sqlSessionFactory.openSession(true)) { NameTableMapper mapper = session.getMapper(NameTableMapper.class); - NameRecord row = new NameRecord(); - row.setId(1); - row.setName("Fred"); + NameRecord row = new NameRecord(1, "Fred"); mapper.insert(row); } } @@ -70,9 +68,7 @@ public void insertRecord() { public void updateRecordWithAutoCommit() { try (SqlSession session = sqlSessionFactory.openSession(true)) { NameTableMapper mapper = session.getMapper(NameTableMapper.class); - NameRecord row = new NameRecord(); - row.setId(1); - row.setName("Barney"); + NameRecord row = new NameRecord(1, "Barney"); mapper.updateByPrimaryKey(row); } } @@ -81,9 +77,7 @@ public void updateRecordWithoutAutoCommitAndNoExplicitCommit() { // this should rollback try (SqlSession session = sqlSessionFactory.openSession()) { NameTableMapper mapper = session.getMapper(NameTableMapper.class); - NameRecord row = new NameRecord(); - row.setId(1); - row.setName("Barney"); + NameRecord row = new NameRecord(1, "Barney"); mapper.updateByPrimaryKey(row); } } @@ -91,9 +85,7 @@ public void updateRecordWithoutAutoCommitAndNoExplicitCommit() { public void updateRecordWithoutAutoCommitAndExplicitCommit() { try (SqlSession session = sqlSessionFactory.openSession()) { NameTableMapper mapper = session.getMapper(NameTableMapper.class); - NameRecord row = new NameRecord(); - row.setId(1); - row.setName("Barney"); + NameRecord row = new NameRecord(1, "Barney"); mapper.updateByPrimaryKey(row); session.commit(); } diff --git a/src/test/java/issues/gh324/NameTableMapper.java b/src/test/java/issues/gh324/NameTableMapper.java index 76723ac73..0bb2ced6c 100644 --- a/src/test/java/issues/gh324/NameTableMapper.java +++ b/src/test/java/issues/gh324/NameTableMapper.java @@ -21,26 +21,31 @@ import java.util.List; import java.util.Optional; -import org.apache.ibatis.annotations.*; +import org.apache.ibatis.annotations.Arg; +import org.apache.ibatis.annotations.CacheNamespace; +import org.apache.ibatis.annotations.SelectProvider; import org.mybatis.dynamic.sql.BasicColumn; import org.mybatis.dynamic.sql.delete.DeleteDSLCompleter; import org.mybatis.dynamic.sql.select.SelectDSLCompleter; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.update.UpdateDSLCompleter; import org.mybatis.dynamic.sql.util.SqlProviderAdapter; -import org.mybatis.dynamic.sql.util.mybatis3.*; +import org.mybatis.dynamic.sql.util.mybatis3.CommonCountMapper; +import org.mybatis.dynamic.sql.util.mybatis3.CommonDeleteMapper; +import org.mybatis.dynamic.sql.util.mybatis3.CommonInsertMapper; +import org.mybatis.dynamic.sql.util.mybatis3.CommonUpdateMapper; +import org.mybatis.dynamic.sql.util.mybatis3.MyBatis3Utils; @CacheNamespace(implementation = ObservableCache.class) public interface NameTableMapper extends CommonCountMapper, CommonDeleteMapper, CommonInsertMapper, CommonUpdateMapper { @SelectProvider(type=SqlProviderAdapter.class, method="select") - @Results(id="NameTableResult", value={ - @Result(column="id", property="id", id=true), - @Result(column="name", property="name") - }) + @Arg(column = "id", javaType = Integer.class, id = true) + @Arg(column = "name", javaType = String.class) List selectMany(SelectStatementProvider selectStatement); @SelectProvider(type=SqlProviderAdapter.class, method="select") - @ResultMap("NameTableResult") + @Arg(column = "id", javaType = Integer.class, id = true) + @Arg(column = "name", javaType = String.class) Optional selectOne(SelectStatementProvider selectStatement); BasicColumn[] selectList = BasicColumn.columnList(id, name); @@ -68,8 +73,8 @@ default int update(UpdateDSLCompleter completer) { default int updateByPrimaryKey(NameRecord row) { return update(c -> - c.set(name).equalTo(row::getName) - .where(id, isEqualTo(row::getId)) + c.set(name).equalTo(row::name) + .where(id, isEqualTo(row::id)) ); } diff --git a/src/test/java/issues/gh324/TestUtils.java b/src/test/java/issues/gh324/TestUtils.java index 23c191686..6c88093e9 100644 --- a/src/test/java/issues/gh324/TestUtils.java +++ b/src/test/java/issues/gh324/TestUtils.java @@ -19,12 +19,12 @@ public class TestUtils { public static void recordIsFred(NameRecord row) { - assertThat(row.getId()).isEqualTo(1); - assertThat(row.getName()).isEqualTo("Fred"); + assertThat(row.id()).isEqualTo(1); + assertThat(row.name()).isEqualTo("Fred"); } public static void recordIsBarney(NameRecord row) { - assertThat(row.getId()).isEqualTo(1); - assertThat(row.getName()).isEqualTo("Barney"); + assertThat(row.id()).isEqualTo(1); + assertThat(row.name()).isEqualTo("Barney"); } } diff --git a/src/test/java/issues/gh324/spring/SpringNameService.java b/src/test/java/issues/gh324/spring/SpringNameService.java index 9fbfdf03f..994bcf310 100644 --- a/src/test/java/issues/gh324/spring/SpringNameService.java +++ b/src/test/java/issues/gh324/spring/SpringNameService.java @@ -37,25 +37,19 @@ public class SpringNameService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void insertRecord() { - NameRecord row = new NameRecord(); - row.setId(1); - row.setName("Fred"); + NameRecord row = new NameRecord(1, "Fred"); mapper.insert(row); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void updateRecordAndCommit() { - NameRecord row = new NameRecord(); - row.setId(1); - row.setName("Barney"); + NameRecord row = new NameRecord(1, "Barney"); mapper.updateByPrimaryKey(row); } public void updateRecordAndRollback() { TransactionStatus txStatus = transactionManager.getTransaction(new DefaultTransactionDefinition()); - NameRecord row = new NameRecord(); - row.setId(1); - row.setName("Barney"); + NameRecord row = new NameRecord(1, "Barney"); mapper.updateByPrimaryKey(row); transactionManager.rollback(txStatus); } From 7edc94c9420ffa91eacdfe1ba5688fcbe36ad1d2 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Wed, 8 Oct 2025 16:41:02 -0400 Subject: [PATCH 34/68] Clear Nullability Warnings --- .../examples/simple/PersonMapperTest.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/test/java/examples/simple/PersonMapperTest.java b/src/test/java/examples/simple/PersonMapperTest.java index b50e38183..4843431f4 100644 --- a/src/test/java/examples/simple/PersonMapperTest.java +++ b/src/test/java/examples/simple/PersonMapperTest.java @@ -626,13 +626,13 @@ void testJoinAllRows() { assertThat(records.get(0).getLastName()).isEqualTo(new LastName("Flintstone")); assertThat(records.get(0).getOccupation()).isEqualTo("Brontosaurus Operator"); assertThat(records.get(0).getBirthDate()).isNotNull(); - assertThat(records.get(0).getAddress().getId()).isEqualTo(1); - assertThat(records.get(0).getAddress().getStreetAddress()).isEqualTo("123 Main Street"); - assertThat(records.get(0).getAddress().getCity()).isEqualTo("Bedrock"); - assertThat(records.get(0).getAddress().getState()).isEqualTo("IN"); - assertThat(records.get(0).getAddress().getAddressType()).isEqualTo(AddressRecord.AddressType.HOME); + assertThat(records.get(0).getAddress()).isNotNull() + .extracting("id", "streetAddress", "city", "state", "addressType") + .containsExactly(1, "123 Main Street", "Bedrock", "IN", AddressRecord.AddressType.HOME); - assertThat(records.get(4).getAddress().getAddressType()).isEqualTo(AddressRecord.AddressType.BUSINESS); + assertThat(records.get(4).getAddress()).isNotNull() + .extracting("addressType") + .isEqualTo(AddressRecord.AddressType.BUSINESS); } } @@ -649,10 +649,9 @@ void testJoinOneRow() { assertThat(records.get(0).getLastName()).isEqualTo(new LastName("Flintstone")); assertThat(records.get(0).getOccupation()).isEqualTo("Brontosaurus Operator"); assertThat(records.get(0).getBirthDate()).isNotNull(); - assertThat(records.get(0).getAddress().getId()).isEqualTo(1); - assertThat(records.get(0).getAddress().getStreetAddress()).isEqualTo("123 Main Street"); - assertThat(records.get(0).getAddress().getCity()).isEqualTo("Bedrock"); - assertThat(records.get(0).getAddress().getState()).isEqualTo("IN"); + assertThat(records.get(0).getAddress()).isNotNull() + .extracting("id", "streetAddress", "city", "state", "addressType") + .containsExactly(1, "123 Main Street", "Bedrock", "IN", AddressRecord.AddressType.HOME); } } @@ -669,10 +668,9 @@ void testJoinPrimaryKey() { assertThat(r.getLastName()).isEqualTo(new LastName("Flintstone")); assertThat(r.getOccupation()).isEqualTo("Brontosaurus Operator"); assertThat(r.getBirthDate()).isNotNull(); - assertThat(r.getAddress().getId()).isEqualTo(1); - assertThat(r.getAddress().getStreetAddress()).isEqualTo("123 Main Street"); - assertThat(r.getAddress().getCity()).isEqualTo("Bedrock"); - assertThat(r.getAddress().getState()).isEqualTo("IN"); + assertThat(r.getAddress()).isNotNull() + .extracting("id", "streetAddress", "city", "state", "addressType") + .containsExactly(1, "123 Main Street", "Bedrock", "IN", AddressRecord.AddressType.HOME); }); } } From 397c14dbd1c1eed8e7d05eca80f3b92854d72014 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Wed, 8 Oct 2025 17:00:19 -0400 Subject: [PATCH 35/68] Upgrade test container versions --- src/test/java/config/TestContainersConfiguration.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/config/TestContainersConfiguration.java b/src/test/java/config/TestContainersConfiguration.java index 77fd866b0..0b39fbb85 100644 --- a/src/test/java/config/TestContainersConfiguration.java +++ b/src/test/java/config/TestContainersConfiguration.java @@ -21,7 +21,8 @@ * Utility interface to hold Docker image tags for the test containers we use */ public interface TestContainersConfiguration { - DockerImageName POSTGRES_LATEST = DockerImageName.parse("postgres:17.2"); - DockerImageName MARIADB_LATEST = DockerImageName.parse("mariadb:11.6.2"); - DockerImageName MYSQL_LATEST = DockerImageName.parse("mysql:9.1.0"); + DockerImageName POSTGRES_LATEST = DockerImageName.parse("postgres:18.0"); + DockerImageName MARIADB_LATEST = DockerImageName.parse("mariadb:12.0.2"); + // Note - Can't go past MySQL:9.2.0 until this is released: https://github.com/testcontainers/testcontainers-java/pull/10185 + DockerImageName MYSQL_LATEST = DockerImageName.parse("mysql:9.2.0"); } From 3b21f0d173d4390254bea3b43b091adeed601d7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 09:53:07 +0000 Subject: [PATCH 36/68] Update dependency org.springframework:spring-jdbc to v6.2.12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c3b07343c..f0b6723b2 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.springframework spring-jdbc - 6.2.11 + 6.2.12 provided true From b42ff449918ed67b17608973255f3b88b7f39d5f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 19 Oct 2025 22:00:47 +0000 Subject: [PATCH 37/68] Update dependency ch.qos.logback:logback-classic to v1.5.20 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f0b6723b2..2a17b66ac 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ ch.qos.logback logback-classic - 1.5.19 + 1.5.20 test From 40aa0bcbfeb70c4a2b644ee2b9a851df863d19df Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Mon, 20 Oct 2025 15:53:57 -0400 Subject: [PATCH 38/68] Add an example of converting a select statement to a count statement --- .../examples/simple/ReusableWhereTest.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/test/java/examples/simple/ReusableWhereTest.java b/src/test/java/examples/simple/ReusableWhereTest.java index f3b6d1d4e..bde7ef1f3 100644 --- a/src/test/java/examples/simple/ReusableWhereTest.java +++ b/src/test/java/examples/simple/ReusableWhereTest.java @@ -17,9 +17,13 @@ import static examples.simple.PersonDynamicSqlSupport.id; 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.isEqualTo; +import static org.mybatis.dynamic.sql.SqlBuilder.isLessThan; import static org.mybatis.dynamic.sql.SqlBuilder.isNull; +import static org.mybatis.dynamic.sql.SqlBuilder.select; import static org.mybatis.dynamic.sql.SqlBuilder.where; import java.io.InputStream; @@ -38,6 +42,12 @@ import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mybatis.dynamic.sql.render.RenderingStrategies; +import org.mybatis.dynamic.sql.select.QueryExpressionModel; +import org.mybatis.dynamic.sql.select.SelectModel; +import org.mybatis.dynamic.sql.select.SubQuery; +import org.mybatis.dynamic.sql.select.aggregate.CountAll; +import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.where.WhereApplier; class ReusableWhereTest { @@ -114,5 +124,64 @@ void testUpdate() { } } + @Test + void testTransformToCount() { + try (SqlSession session = sqlSessionFactory.openSession()) { + PersonMapper mapper = session.getMapper(PersonMapper.class); + + SelectModel selectModel = select(PersonMapper.selectList) + .from(person) + .where(id, isLessThan(5)) + .limit(2) + .build(); + + SelectStatementProvider selectStatement = selectModel.render(RenderingStrategies.MYBATIS3); + + assertThat(selectStatement.getSelectStatement()).isEqualTo( + "select id as A_ID, first_name, last_name, birth_date, employed, occupation, address_id from Person where id < #{parameters.p1,jdbcType=INTEGER} limit #{parameters.p2}"); + assertThat(selectStatement.getParameters()).containsOnly(entry("p1", 5), entry("p2", 2L)); + + SelectModel countModel = toCount(selectModel); + SelectStatementProvider countStatement = countModel.render(RenderingStrategies.MYBATIS3); + + assertThat(countStatement.getSelectStatement()).isEqualTo( + "select count(*) from (select id as A_ID, first_name, last_name, birth_date, employed, occupation, address_id from Person where id < #{parameters.p1,jdbcType=INTEGER})"); + assertThat(countStatement.getParameters()).containsOnly(entry("p1", 5)); + + long count = mapper.count(countStatement); + + assertThat(count).isEqualTo(4); + } + + } + private final WhereApplier commonWhere = where(id, isEqualTo(1)).or(occupation, isNull()).toWhereApplier(); + + /** + * This function transforms a select statement into a count statement by wrapping the select statement into + * a subquery. This can be used to create a single select statement and use it for both selects and counts + * in a paging scenario. This is more appropriate than a reusable where clause if the query is complex. For simple + * queries, a reusable where clause is best. + * + *

This function will strip any paging configuration, waits, order bys, etc. from the top level query. This + * will allow usage of a paging query for selects, and the transformed query for a count of all rows. + * + * @param selectModel the select model to transform + * @return a new select model that is "select count(*) from (subquery)" where subquery is the input select statement + */ + static SelectModel toCount(SelectModel selectModel) { + // remove any paging configuration, order by, wait clause, etc. from the incoming select model + SelectModel strippedSelectModel = SelectModel.withQueryExpressions(selectModel.queryExpressions().toList()) + .withStatementConfiguration(selectModel.statementConfiguration()) + .build(); + + QueryExpressionModel model = QueryExpressionModel + .withSelectList(List.of(new CountAll())) + .withTable(new SubQuery.Builder().withSelectModel(strippedSelectModel).build()) + .build(); + + return SelectModel.withQueryExpressions(List.of(model)) + .withStatementConfiguration(selectModel.statementConfiguration()) + .build(); + } } From 2d8ee0f950c7021a27be4e4f433c7a1f7bd8900a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 20:35:25 +0000 Subject: [PATCH 39/68] Update dependency maven-wrapper to v3.3.4 --- .mvn/wrapper/MavenWrapperDownloader.java | 11 +++++--- .mvn/wrapper/maven-wrapper.properties | 20 ++------------- mvnw | 32 ++++++++++++++---------- mvnw.cmd | 8 +++--- 4 files changed, 32 insertions(+), 39 deletions(-) diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java index bdf0ddfa6..7e9c1e3c9 100644 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -7,7 +7,7 @@ * "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 + * 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 @@ -30,7 +30,7 @@ import java.util.concurrent.ThreadLocalRandom; public final class MavenWrapperDownloader { - private static final String WRAPPER_VERSION = "3.3.2"; + private static final String WRAPPER_VERSION = "3.3.4"; private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); @@ -45,8 +45,11 @@ public static void main(String[] args) { try { log(" - Downloader started"); final URL wrapperUrl = URI.create(args[0]).toURL(); - final String jarPath = args[1].replace("..", ""); // Sanitize path - final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize(); + final Path baseDir = Paths.get(".").toAbsolutePath().normalize(); + final Path wrapperJarPath = baseDir.resolve(args[1]).normalize(); + if (!wrapperJarPath.startsWith(baseDir)) { + throw new IOException("Invalid path: outside of allowed directory"); + } downloadFileFromURL(wrapperUrl, wrapperJarPath); log("Done"); } catch (IOException e) { diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index b06697c61..7bb288288 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,20 +1,4 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. -wrapperVersion=3.3.2 +wrapperVersion=3.3.4 distributionType=source distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar diff --git a/mvnw b/mvnw index 668388825..1ddd97b9e 100755 --- a/mvnw +++ b/mvnw @@ -8,7 +8,7 @@ # "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 +# 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 @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.3.2 +# Apache Maven Wrapper startup batch script, version 3.3.4 # # Required ENV vars: # ------------------ @@ -201,6 +201,14 @@ MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} export MAVEN_PROJECTBASEDIR log "$MAVEN_PROJECTBASEDIR" +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. @@ -212,15 +220,13 @@ else log "Couldn't find $wrapperJarPath, downloading it ..." if [ -n "$MVNW_REPOURL" ]; then - wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" else - wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" fi while IFS="=" read -r key value; do - # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) - safeValue=$(echo "$value" | tr -d '\r') case "$key" in wrapperUrl) - wrapperUrl="$safeValue" + wrapperUrl=$(trim "${value-}") break ;; esac @@ -235,17 +241,17 @@ else log "Found wget ... using wget" [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + wget ${QUIET:+"$QUIET"} "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" else - wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + wget ${QUIET:+"$QUIET"} --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" fi elif command -v curl >/dev/null; then log "Found curl ... using curl" [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + curl ${QUIET:+"$QUIET"} -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" else - curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + curl ${QUIET:+"$QUIET"} --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" fi else log "Falling back to using Java to download" @@ -276,7 +282,7 @@ fi wrapperSha256Sum="" while IFS="=" read -r key value; do case "$key" in wrapperSha256Sum) - wrapperSha256Sum=$value + wrapperSha256Sum=$(trim "${value-}") break ;; esac @@ -284,7 +290,7 @@ done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" if [ -n "$wrapperSha256Sum" ]; then wrapperSha256Result=false if command -v sha256sum >/dev/null; then - if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c - >/dev/null 2>&1; then wrapperSha256Result=true fi elif command -v shasum >/dev/null; then diff --git a/mvnw.cmd b/mvnw.cmd index da4fe4dd9..8366e2170 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -7,7 +7,7 @@ @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM -@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @@ -18,7 +18,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM Apache Maven Wrapper startup batch script, version 3.3.4 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @@ -119,7 +119,7 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B @@ -133,7 +133,7 @@ if exist %WRAPPER_JAR% ( ) ) else ( if not "%MVNW_REPOURL%" == "" ( - SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar" ) if "%MVNW_VERBOSE%" == "true" ( echo Couldn't find %WRAPPER_JAR%, downloading it ... From 8a9b228faa6722c0c07cea40dcfd055fbe6caa39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:47:15 +0000 Subject: [PATCH 40/68] Update spring batch to v5.2.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2a17b66ac..f7d4788a3 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 17 17 6.0.0 - 5.2.3 + 5.2.4 checkstyle-override.xml From 80751ca41bec05262752f38970927d7622bd2b00 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Thu, 23 Oct 2025 13:38:36 -0400 Subject: [PATCH 41/68] Initial attempt at making SqlColumn extendable --- .../org/mybatis/dynamic/sql/SqlColumn.java | 118 ++++++++++-------- .../dynamic/sql/SqlColumnBuilders.java | 16 +++ .../examples/simple/PrimaryKeyColumn.java | 89 +++++++++++++ 3 files changed, 172 insertions(+), 51 deletions(-) create mode 100644 src/main/java/org/mybatis/dynamic/sql/SqlColumnBuilders.java create mode 100644 src/test/java/examples/simple/PrimaryKeyColumn.java diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index 0d3a9ec44..c65e85383 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -25,7 +25,7 @@ import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.StringUtilities; -public class SqlColumn implements BindableColumn, SortSpecification { +public class SqlColumn implements BindableColumn, SortSpecification, SqlColumnBuilders { protected final String name; protected final SqlTable table; @@ -39,7 +39,7 @@ public class SqlColumn implements BindableColumn, SortSpecification { protected final @Nullable Class javaType; protected final @Nullable String javaProperty; - private SqlColumn(Builder builder) { + protected SqlColumn(AbstractBuilder builder) { name = Objects.requireNonNull(builder.name); table = Objects.requireNonNull(builder.table); jdbcType = builder.jdbcType; @@ -91,15 +91,13 @@ public Optional javaProperty() { } @Override - public SortSpecification descending() { - Builder b = copy(); - return b.withDescendingPhrase(" DESC").build(); //$NON-NLS-1$ + public SqlColumn descending() { + return copyBuilder().withDescendingPhrase(" DESC").build(); //$NON-NLS-1$ } @Override public SqlColumn as(String alias) { - Builder b = copy(); - return b.withAlias(alias).build(); + return copyBuilder().withAlias(alias).build(); } /** @@ -110,9 +108,7 @@ public SqlColumn as(String alias) { * @return a new column that will be rendered with the specified table qualifier */ public SqlColumn qualifiedWith(String tableQualifier) { - Builder b = copy(); - b.withTableQualifier(tableQualifier); - return b.build(); + return copyBuilder().withTableQualifier(tableQualifier).build(); } /** @@ -127,8 +123,8 @@ public SqlColumn qualifiedWith(String tableQualifier) { * @return a new column aliased with a camel case version of the column name */ public SqlColumn asCamelCase() { - Builder b = copy(); - return b.withAlias("\"" + StringUtilities.toCamelCase(name) + "\"").build(); //$NON-NLS-1$ //$NON-NLS-2$ + return copyBuilder() + .withAlias("\"" + StringUtilities.toCamelCase(name) + "\"").build(); //$NON-NLS-1$ //$NON-NLS-2$ } @Override @@ -150,29 +146,40 @@ public Optional renderingStrategy() { return Optional.ofNullable(renderingStrategy); } + @Override public SqlColumn withTypeHandler(String typeHandler) { - Builder b = copy(); - return b.withTypeHandler(typeHandler).build(); + return cast(copyBuilder().withTypeHandler(typeHandler).build()); } + @Override public SqlColumn withRenderingStrategy(RenderingStrategy renderingStrategy) { - Builder b = copy(); - return b.withRenderingStrategy(renderingStrategy).build(); + return cast(copyBuilder().withRenderingStrategy(renderingStrategy).build()); } + @Override + @SuppressWarnings("unchecked") public SqlColumn withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { - Builder b = copy(); - return b.withParameterTypeConverter(parameterTypeConverter).build(); + return cast(copyBuilder().withParameterTypeConverter((ParameterTypeConverter) parameterTypeConverter).build()); } + @Override + @SuppressWarnings("unchecked") public SqlColumn withJavaType(Class javaType) { - Builder b = copy(); - return b.withJavaType(javaType).build(); + return cast(copyBuilder().withJavaType((Class) javaType).build()); } + @Override public SqlColumn withJavaProperty(String javaProperty) { - Builder b = copy(); - return b.withJavaProperty(javaProperty).build(); + return cast(copyBuilder().withJavaProperty(javaProperty).build()); + } + + private Builder copyBuilder() { + return populateBaseBuilder(new Builder<>()); + } + + @SuppressWarnings("unchecked") + protected > S cast(SqlColumn column) { + return (S) column; } /** @@ -181,12 +188,12 @@ public SqlColumn withJavaProperty(String javaProperty) { * 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 the type. Will be the same as T for this usage. - * @return a new SqlColumn of type S (S is the same as T) + * @param the concrete builder type + * @return the populated builder */ @SuppressWarnings("unchecked") - private Builder copy() { - return new Builder() + protected > B populateBaseBuilder(B builder) { + return (B) builder .withName(this.name) .withTable(this.table) .withJdbcType(this.jdbcType) @@ -194,9 +201,9 @@ private Builder copy() { .withAlias(this.alias) .withTypeHandler(this.typeHandler) .withRenderingStrategy(this.renderingStrategy) - .withParameterTypeConverter((ParameterTypeConverter) this.parameterTypeConverter) + .withParameterTypeConverter(this.parameterTypeConverter) .withTableQualifier(this.tableQualifier) - .withJavaType((Class) this.javaType) + .withJavaType(this.javaType) .withJavaProperty(this.javaProperty); } @@ -213,7 +220,7 @@ public static SqlColumn of(String name, SqlTable table, JDBCType jdbcType .build(); } - public static class Builder { + public static abstract class AbstractBuilder> { protected @Nullable String name; protected @Nullable SqlTable table; protected @Nullable JDBCType jdbcType; @@ -226,63 +233,72 @@ public static class Builder { protected @Nullable Class javaType; protected @Nullable String javaProperty; - public Builder withName(String name) { + public B withName(String name) { this.name = name; - return this; + return getThis(); } - public Builder withTable(SqlTable table) { + public B withTable(SqlTable table) { this.table = table; - return this; + return getThis(); } - public Builder withJdbcType(@Nullable JDBCType jdbcType) { + public B withJdbcType(@Nullable JDBCType jdbcType) { this.jdbcType = jdbcType; - return this; + return getThis(); } - public Builder withDescendingPhrase(String descendingPhrase) { + public B withDescendingPhrase(String descendingPhrase) { this.descendingPhrase = descendingPhrase; - return this; + return getThis(); } - public Builder withAlias(@Nullable String alias) { + public B withAlias(@Nullable String alias) { this.alias = alias; - return this; + return getThis(); } - public Builder withTypeHandler(@Nullable String typeHandler) { + public B withTypeHandler(@Nullable String typeHandler) { this.typeHandler = typeHandler; - return this; + return getThis(); } - public Builder withRenderingStrategy(@Nullable RenderingStrategy renderingStrategy) { + public B withRenderingStrategy(@Nullable RenderingStrategy renderingStrategy) { this.renderingStrategy = renderingStrategy; - return this; + return getThis(); } - public Builder withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { + public B withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { this.parameterTypeConverter = parameterTypeConverter; - return this; + return getThis(); } - private Builder withTableQualifier(@Nullable String tableQualifier) { + public B withTableQualifier(@Nullable String tableQualifier) { this.tableQualifier = tableQualifier; - return this; + return getThis(); } - public Builder withJavaType(@Nullable Class javaType) { + public B withJavaType(@Nullable Class javaType) { this.javaType = javaType; - return this; + return getThis(); } - public Builder withJavaProperty(@Nullable String javaProperty) { + public B withJavaProperty(@Nullable String javaProperty) { this.javaProperty = javaProperty; - return this; + return getThis(); } + protected abstract B getThis(); + } + + public static class Builder extends AbstractBuilder> { public SqlColumn build() { return new SqlColumn<>(this); } + + @Override + protected Builder getThis() { + return this; + } } } diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumnBuilders.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumnBuilders.java new file mode 100644 index 000000000..a4f20af67 --- /dev/null +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumnBuilders.java @@ -0,0 +1,16 @@ +package org.mybatis.dynamic.sql; + +import org.mybatis.dynamic.sql.render.RenderingStrategy; + +public interface SqlColumnBuilders { + SqlColumnBuilders withTypeHandler(String typeHandler); + + SqlColumnBuilders withRenderingStrategy(RenderingStrategy renderingStrategy); + + SqlColumnBuilders withParameterTypeConverter( + ParameterTypeConverter parameterTypeConverter); + + SqlColumnBuilders withJavaType(Class javaType); + + SqlColumnBuilders withJavaProperty(String javaProperty); +} diff --git a/src/test/java/examples/simple/PrimaryKeyColumn.java b/src/test/java/examples/simple/PrimaryKeyColumn.java new file mode 100644 index 000000000..1cf37b205 --- /dev/null +++ b/src/test/java/examples/simple/PrimaryKeyColumn.java @@ -0,0 +1,89 @@ +package examples.simple; + +import org.mybatis.dynamic.sql.ParameterTypeConverter; +import org.mybatis.dynamic.sql.SqlColumn; +import org.mybatis.dynamic.sql.render.RenderingStrategy; +import org.mybatis.dynamic.sql.util.StringUtilities; + +public class PrimaryKeyColumn extends SqlColumn { + private final boolean isPrimaryKeyColumn; + + private PrimaryKeyColumn(Builder builder) { + super(builder); + isPrimaryKeyColumn = builder.isPrimaryKeyColumn; + } + + public boolean isPrimaryKeyColumn() { + return isPrimaryKeyColumn; + } + + @Override + public PrimaryKeyColumn descending() { + return copyBuilder().withDescendingPhrase(" DESC").build(); //$NON-NLS-1$ + } + + @Override + public PrimaryKeyColumn as(String alias) { + return copyBuilder().withAlias(alias).build(); + } + + @Override + public PrimaryKeyColumn qualifiedWith(String tableQualifier) { + return copyBuilder().withTableQualifier(tableQualifier).build(); + } + + @Override + public PrimaryKeyColumn asCamelCase() { + return copyBuilder() + .withAlias("\"" + StringUtilities.toCamelCase(name) + "\"").build(); //$NON-NLS-1$ //$NON-NLS-2$ + } + + @Override + public PrimaryKeyColumn withTypeHandler(String typeHandler) { + return cast(copyBuilder().withTypeHandler(typeHandler).build()); + } + + @Override + public PrimaryKeyColumn withRenderingStrategy(RenderingStrategy renderingStrategy) { + return cast(copyBuilder().withRenderingStrategy(renderingStrategy).build()); + } + + @Override + @SuppressWarnings("unchecked") + public PrimaryKeyColumn withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { + return cast(copyBuilder().withParameterTypeConverter((ParameterTypeConverter) parameterTypeConverter).build()); + } + + @Override + @SuppressWarnings("unchecked") + public PrimaryKeyColumn withJavaType(Class javaType) { + return cast(copyBuilder().withJavaType((Class) javaType).build()); + } + + @Override + public PrimaryKeyColumn withJavaProperty(String javaProperty) { + return cast(copyBuilder().withJavaProperty(javaProperty).build()); + } + + private Builder copyBuilder() { + return populateBaseBuilder(new Builder<>()); + } + + public static class Builder extends AbstractBuilder> { + private boolean isPrimaryKeyColumn; + + public Builder isPrimaryKeyColumn(boolean isPrimaryKeyColumn) { + this.isPrimaryKeyColumn = isPrimaryKeyColumn; + return this; + } + + public PrimaryKeyColumn build() { + return new PrimaryKeyColumn<>(this); + } + + @Override + protected Builder getThis() { + return this; + } + } +} From db5479c8197c5fc45bc2f1d791e52902cedfa76b Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Thu, 23 Oct 2025 13:48:14 -0400 Subject: [PATCH 42/68] Polishing --- .../java/org/mybatis/dynamic/sql/SqlColumn.java | 7 +------ .../mybatis/dynamic/sql/SqlColumnBuilders.java | 16 ---------------- .../examples/simple/PersonDynamicSqlSupport.java | 10 ++++++++-- 3 files changed, 9 insertions(+), 24 deletions(-) delete mode 100644 src/main/java/org/mybatis/dynamic/sql/SqlColumnBuilders.java diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index c65e85383..042fa699e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -25,7 +25,7 @@ import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.StringUtilities; -public class SqlColumn implements BindableColumn, SortSpecification, SqlColumnBuilders { +public class SqlColumn implements BindableColumn, SortSpecification { protected final String name; protected final SqlTable table; @@ -146,29 +146,24 @@ public Optional renderingStrategy() { return Optional.ofNullable(renderingStrategy); } - @Override public SqlColumn withTypeHandler(String typeHandler) { return cast(copyBuilder().withTypeHandler(typeHandler).build()); } - @Override public SqlColumn withRenderingStrategy(RenderingStrategy renderingStrategy) { return cast(copyBuilder().withRenderingStrategy(renderingStrategy).build()); } - @Override @SuppressWarnings("unchecked") public SqlColumn withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { return cast(copyBuilder().withParameterTypeConverter((ParameterTypeConverter) parameterTypeConverter).build()); } - @Override @SuppressWarnings("unchecked") public SqlColumn withJavaType(Class javaType) { return cast(copyBuilder().withJavaType((Class) javaType).build()); } - @Override public SqlColumn withJavaProperty(String javaProperty) { return cast(copyBuilder().withJavaProperty(javaProperty).build()); } diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumnBuilders.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumnBuilders.java deleted file mode 100644 index a4f20af67..000000000 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumnBuilders.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.mybatis.dynamic.sql; - -import org.mybatis.dynamic.sql.render.RenderingStrategy; - -public interface SqlColumnBuilders { - SqlColumnBuilders withTypeHandler(String typeHandler); - - SqlColumnBuilders withRenderingStrategy(RenderingStrategy renderingStrategy); - - SqlColumnBuilders withParameterTypeConverter( - ParameterTypeConverter parameterTypeConverter); - - SqlColumnBuilders withJavaType(Class javaType); - - SqlColumnBuilders withJavaProperty(String javaProperty); -} diff --git a/src/test/java/examples/simple/PersonDynamicSqlSupport.java b/src/test/java/examples/simple/PersonDynamicSqlSupport.java index ba31eea5d..62d3c0840 100644 --- a/src/test/java/examples/simple/PersonDynamicSqlSupport.java +++ b/src/test/java/examples/simple/PersonDynamicSqlSupport.java @@ -23,7 +23,7 @@ public final class PersonDynamicSqlSupport { public static final Person person = new Person(); - public static final SqlColumn id = person.id; + public static final PrimaryKeyColumn id = person.id; public static final SqlColumn firstName = person.firstName; public static final SqlColumn lastName = person.lastName; public static final SqlColumn birthDate = person.birthDate; @@ -32,7 +32,13 @@ public final class PersonDynamicSqlSupport { public static final SqlColumn addressId = person.addressId; public static final class Person extends SqlTable { - public final SqlColumn id = column("id", JDBCType.INTEGER).withJavaProperty("id"); + public final PrimaryKeyColumn id = new PrimaryKeyColumn.Builder() + .withTable(this) + .withName("id") + .withJdbcType(JDBCType.INTEGER) + .withJavaProperty("id") + .isPrimaryKeyColumn(true) + .build(); public final SqlColumn firstName = column("first_name", JDBCType.VARCHAR) .withJavaProperty("firstName"); public final SqlColumn lastName = From 8d82cfc891293135926b391b00743252e9dee82e Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Thu, 23 Oct 2025 14:33:55 -0400 Subject: [PATCH 43/68] Documentation --- .../org/mybatis/dynamic/sql/SqlColumn.java | 138 +++++++++++++++++- .../examples/simple/PrimaryKeyColumn.java | 2 +- 2 files changed, 132 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index 042fa699e..b0cf8f870 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -25,6 +25,48 @@ import org.mybatis.dynamic.sql.util.FragmentAndParameters; import org.mybatis.dynamic.sql.util.StringUtilities; +/** + * This class represents the definition of a column in a table. + * + *

The class contains many attributes that are helpful for use in MyBatis and Spring runtime + * environments, but the only required attributes are the name of the column and a reference to + * the SqlTable the column is a part of. + * + *

The class can be extended if you wish to associate additional attributes with a column for your + * own purposes. Extending the class involves the following activities: + *

    + *
  1. Create a class that extends {@link SqlColumn}
  2. + *
  3. In your extended class, create a static builder class that extends {@link SqlColumn.AbstractBuilder}
  4. + *
  5. Add your desired attributes to the class and the builder
  6. + *
  7. You MUST override the following methods. These methods are used with regular operations in the library. + * If you do not override these methods, it is likely that your extended attributes will be lost during + * regular usage. For example, if a user calls the {@code as} method to apply an alias, the base + * {@code SqlColumn} class will create a new instance of {@code SqlColumn}, NOT your extended class. + *
      + *
    • {@link SqlColumn#as(String)}
    • + *
    • {@link SqlColumn#asCamelCase()}
    • + *
    • {@link SqlColumn#descending()}
    • + *
    • {@link SqlColumn#qualifiedWith(String)}
    • + *
    + *
  8. + *
  9. You SHOULD override the following methods. These methods can be used to add additional attributes to a + * column by creating a new instance with a specified attribute set. These methods are used during the + * construction of columns. If you do not override these methods, and a user calls them, then a new + * {@code SqlColumn} will be created that does not contain your extended attributes. + *
      + *
    • {@link SqlColumn#withJavaProperty(String)}
    • + *
    • {@link SqlColumn#withRenderingStrategy(RenderingStrategy)}
    • + *
    • {@link SqlColumn#withTypeHandler(String)}
    • + *
    • {@link SqlColumn#withJavaType(Class)}
    • + *
    • {@link SqlColumn#withParameterTypeConverter(ParameterTypeConverter)}
    • + *
    + *
  10. + *
+ * + *

The test code for this library contains an example of a proper extension of this class. + * + * @param the Java type associated with the column + */ public class SqlColumn implements BindableColumn, SortSpecification { protected final String name; @@ -90,11 +132,24 @@ public Optional javaProperty() { return value == null ? null : parameterTypeConverter.convert(value); } + /** + * Create a new column instance that will render as descending when used in an order by phrase. + * + * @return a new column instance that will render as descending when used in an order by phrase + */ @Override public SqlColumn descending() { return copyBuilder().withDescendingPhrase(" DESC").build(); //$NON-NLS-1$ } + /** + * Create a new column instance with the specified alias that will render as "as alias" in a column list. + * + * @param alias + * the column alias to set + * + * @return a new column instance with the specified alias + */ @Override public SqlColumn as(String alias) { return copyBuilder().withAlias(alias).build(); @@ -112,11 +167,11 @@ public SqlColumn qualifiedWith(String tableQualifier) { } /** - * Set an alias with a camel cased string based on the column name. This can be useful for queries using + * Set an alias with a camel-cased string based on the column name. This can be useful for queries using * the {@link org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper} where the columns are placed into * a map based on the column name returned from the database. * - *

A camel case string is mixed case, and most databases do not support unquoted mixed case strings + *

A camel case string is a mixed case string, and most databases do not support unquoted mixed case strings * as identifiers. Therefore, the generated alias will be surrounded by double quotes thereby making it a * quoted identifier. Most databases will respect quoted mixed case identifiers. * @@ -146,24 +201,93 @@ public Optional renderingStrategy() { return Optional.ofNullable(renderingStrategy); } + /** + * Create a new column instance with the specified type handler. + * + *

This method uses a different type (S). This allows it to be chained with the other + * with* methods. Using new types forces the compiler to delay type inference until the end of a call chain. + * Without this different type (for example, if we used T), the compiler would erase the type after the call + * and method chaining would not work. This is a workaround for Java's lack of reification. + * + * @param typeHandler the type handler to set + * @return a new column instance with the specified type handler + * @param the type of the new column (will be the same as T) + */ public SqlColumn withTypeHandler(String typeHandler) { return cast(copyBuilder().withTypeHandler(typeHandler).build()); } + /** + * Create a new column instance with the specified rendering strategy. + * + *

This method uses a different type (S). This allows it to be chained with the other + * with* methods. Using new types forces the compiler to delay type inference until the end of a call chain. + * Without this different type (for example, if we used T), the compiler would erase the type after the call + * and method chaining would not work. This is a workaround for Java's lack of reification. + * + * @param renderingStrategy the rendering strategy to set + * @return a new column instance with the specified type handler + * @param the type of the new column (will be the same as T) + */ public SqlColumn withRenderingStrategy(RenderingStrategy renderingStrategy) { return cast(copyBuilder().withRenderingStrategy(renderingStrategy).build()); } + /** + * Create a new column instance with the specified parameter type converter. + * + *

Parameter type converters are useful with Spring JDBC. Typically, they are not needed for MyBatis. + * + *

This method uses a different type (S). This allows it to be chained with the other + * with* methods. Using new types forces the compiler to delay type inference until the end of a call chain. + * Without this different type (for example, if we used T), the compiler would erase the type after the call + * and method chaining would not work. This is a workaround for Java's lack of reification. + * + * @param parameterTypeConverter the parameter type converter to set + * @return a new column instance with the specified type handler + * @param the type of the new column (will be the same as T) + */ @SuppressWarnings("unchecked") public SqlColumn withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { - return cast(copyBuilder().withParameterTypeConverter((ParameterTypeConverter) parameterTypeConverter).build()); + return cast(copyBuilder().withParameterTypeConverter((ParameterTypeConverter) parameterTypeConverter) + .build()); } + /** + * Create a new column instance with the specified Java type. + * + *

Specifying a Java type will force rendering of the Java type for MyBatis parameters. This can be useful + * with some MyBatis type handlers. + * + *

This method uses a different type (S). This allows it to be chained with the other + * with* methods. Using new types forces the compiler to delay type inference until the end of a call chain. + * Without this different type (for example, if we used T), the compiler would erase the type after the call + * and method chaining would not work. This is a workaround for Java's lack of reification. + * + * @param javaType the Java type to set + * @return a new column instance with the specified type handler + * @param the type of the new column (will be the same as T) + */ @SuppressWarnings("unchecked") public SqlColumn withJavaType(Class javaType) { return cast(copyBuilder().withJavaType((Class) javaType).build()); } + /** + * Create a new column instance with the specified Java property. + * + *

Specifying a Java property in the column will allow usage of the column as a "mapped column" in record-based + * insert statements. + * + *

This method uses a different type (S). This allows it to be chained with the other + * with* methods. Using new types forces the compiler to delay type inference until the end of a call chain. + * Without this different type (for example, if we used T), the compiler would erase the type after the call + * and method chaining would not work. This is a workaround for Java's lack of reification. + * + * @param javaProperty the Java property to set + * @return a new column instance with the specified type handler + * @param the type of the new column (will be the same as T) + */ public SqlColumn withJavaProperty(String javaProperty) { return cast(copyBuilder().withJavaProperty(javaProperty).build()); } @@ -178,10 +302,10 @@ protected > S cast(SqlColumn 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. + * This method will add all current attributes to the specified builder. It is useful when creating + * new class instances that only change one attribute - we set all current attributes, then + * change the one attribute. This utility can be used with the with* methods and other methods that + * create new instances. * * @param the concrete builder type * @return the populated builder diff --git a/src/test/java/examples/simple/PrimaryKeyColumn.java b/src/test/java/examples/simple/PrimaryKeyColumn.java index 1cf37b205..ce1fce08c 100644 --- a/src/test/java/examples/simple/PrimaryKeyColumn.java +++ b/src/test/java/examples/simple/PrimaryKeyColumn.java @@ -66,7 +66,7 @@ public PrimaryKeyColumn withJavaProperty(String javaProperty) { } private Builder copyBuilder() { - return populateBaseBuilder(new Builder<>()); + return populateBaseBuilder(new Builder<>()).isPrimaryKeyColumn(isPrimaryKeyColumn); } public static class Builder extends AbstractBuilder> { From b76c667669f2acd6b22ae0930593825630607280 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Thu, 23 Oct 2025 15:43:56 -0400 Subject: [PATCH 44/68] Tests --- .../org/mybatis/dynamic/sql/SqlColumn.java | 35 ++++- .../examples/simple/ExtendedColumnTest.java | 124 ++++++++++++++++++ .../examples/simple/PrimaryKeyColumn.java | 25 +++- 3 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 src/test/java/examples/simple/ExtendedColumnTest.java diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index b0cf8f870..2f8a5aaff 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -63,7 +63,10 @@ * * * - *

The test code for this library contains an example of a proper extension of this class. + *

The test code for this library contains an example of a proper extension of this class. In most cases, the code + * for the overriding methods can be simply copied from this class and then the return types can be changed to the + * subclass's covariant type (this pre-supposes that you create a {@code copyBuilder} method that returns a new builder + * populated with all current class attributes). * * @param the Java type associated with the column */ @@ -139,7 +142,11 @@ public Optional javaProperty() { */ @Override public SqlColumn descending() { - return copyBuilder().withDescendingPhrase(" DESC").build(); //$NON-NLS-1$ + return setDescending(copyBuilder()).build(); + } + + protected > B setDescending(B builder) { + return cast(builder.withDescendingPhrase(" DESC")); //$NON-NLS-1$ } /** @@ -152,7 +159,11 @@ public SqlColumn descending() { */ @Override public SqlColumn as(String alias) { - return copyBuilder().withAlias(alias).build(); + return setAlias(copyBuilder(), alias).build(); + } + + protected > B setAlias(B builder, String alias) { + return cast(builder.withAlias(alias)); } /** @@ -163,7 +174,11 @@ public SqlColumn as(String alias) { * @return a new column that will be rendered with the specified table qualifier */ public SqlColumn qualifiedWith(String tableQualifier) { - return copyBuilder().withTableQualifier(tableQualifier).build(); + return setTableQualifier(copyBuilder(), tableQualifier).build(); + } + + protected > B setTableQualifier(B builder, String tableQualifier) { + return cast(builder.withTableQualifier(tableQualifier)); } /** @@ -178,8 +193,11 @@ public SqlColumn qualifiedWith(String tableQualifier) { * @return a new column aliased with a camel case version of the column name */ public SqlColumn asCamelCase() { - return copyBuilder() - .withAlias("\"" + StringUtilities.toCamelCase(name) + "\"").build(); //$NON-NLS-1$ //$NON-NLS-2$ + return setCamelCaseAlias(copyBuilder()).build(); + } + + protected > B setCamelCaseAlias(B builder) { + return cast(builder.withAlias("\"" + StringUtilities.toCamelCase(name) + "\"")); //$NON-NLS-1$ //$NON-NLS-2$ } @Override @@ -301,6 +319,11 @@ protected > S cast(SqlColumn column) { return (S) column; } + @SuppressWarnings("unchecked") + protected > B cast(AbstractBuilder builder) { + return (B) builder; + } + /** * This method will add all current attributes to the specified builder. It is useful when creating * new class instances that only change one attribute - we set all current attributes, then diff --git a/src/test/java/examples/simple/ExtendedColumnTest.java b/src/test/java/examples/simple/ExtendedColumnTest.java new file mode 100644 index 000000000..af89355ed --- /dev/null +++ b/src/test/java/examples/simple/ExtendedColumnTest.java @@ -0,0 +1,124 @@ +/* + * Copyright 2016-2025 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 examples.simple; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.mybatis.dynamic.sql.ParameterTypeConverter; +import org.mybatis.dynamic.sql.SqlTable; +import org.mybatis.dynamic.sql.render.RenderingStrategies; + +class ExtendedColumnTest { + + private final SqlTable table = SqlTable.of("foo"); + private final PrimaryKeyColumn bar = new PrimaryKeyColumn.Builder() + .withName("first_name") + .withTable(table) + .isPrimaryKeyColumn(true) + .build(); + private final ParameterTypeConverter ptc = Object::toString; + + @Test + void testPropagatedDescending() { + var baz = bar.descending(); + + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testPropagatedAlias() { + var baz = bar.as("fred"); + + assertThat(baz.alias()).hasValue("fred"); + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testPropagatedQualifiedWith() { + var baz = bar.qualifiedWith("fred"); + + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testPropagatedAsCamelCase() { + var baz = bar.asCamelCase(); + + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testPropagatedWithTypeHandler() { + var baz = bar.withTypeHandler("barney"); + + assertThat(baz.typeHandler()).hasValue("barney"); + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testPropagatedRenderingStrategy() { + var baz = bar.withRenderingStrategy(RenderingStrategies.MYBATIS3); + + assertThat(baz.renderingStrategy()).hasValue(RenderingStrategies.MYBATIS3); + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testPropagatedParameterTypeConverter() { + var baz = bar.withParameterTypeConverter(ptc); + + assertThat(baz.convertParameterType(11)).isEqualTo("11"); + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testPropagatedJavaType() { + var baz = bar.withJavaType(Integer.class); + + assertThat(baz.javaType()).hasValue(Integer.class); + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testPropagatedJavaProperty() { + var baz = bar.withJavaProperty("id"); + + assertThat(baz.javaProperty()).hasValue("id"); + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } + + @Test + void testAll() { + PrimaryKeyColumn baz = bar.descending() + .as("fred") + .qualifiedWith("fred") + .asCamelCase() + .withTypeHandler("barney") + .withRenderingStrategy(RenderingStrategies.MYBATIS3) + .withParameterTypeConverter(ptc) + .withJavaType(Integer.class) + .withJavaProperty("id"); + + assertThat(baz.alias()).hasValue("\"firstName\""); + assertThat(baz.typeHandler()).hasValue("barney"); + assertThat(baz.renderingStrategy()).hasValue(RenderingStrategies.MYBATIS3); + assertThat(baz.convertParameterType(11)).isEqualTo("11"); + assertThat(baz.javaType()).hasValue(Integer.class); + assertThat(baz.javaProperty()).hasValue("id"); + assertThat(baz.isPrimaryKeyColumn()).isTrue(); + } +} diff --git a/src/test/java/examples/simple/PrimaryKeyColumn.java b/src/test/java/examples/simple/PrimaryKeyColumn.java index ce1fce08c..b54bbf26c 100644 --- a/src/test/java/examples/simple/PrimaryKeyColumn.java +++ b/src/test/java/examples/simple/PrimaryKeyColumn.java @@ -1,9 +1,23 @@ +/* + * Copyright 2016-2025 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 examples.simple; import org.mybatis.dynamic.sql.ParameterTypeConverter; import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.render.RenderingStrategy; -import org.mybatis.dynamic.sql.util.StringUtilities; public class PrimaryKeyColumn extends SqlColumn { private final boolean isPrimaryKeyColumn; @@ -19,23 +33,22 @@ public boolean isPrimaryKeyColumn() { @Override public PrimaryKeyColumn descending() { - return copyBuilder().withDescendingPhrase(" DESC").build(); //$NON-NLS-1$ + return setDescending(copyBuilder()).build(); } @Override public PrimaryKeyColumn as(String alias) { - return copyBuilder().withAlias(alias).build(); + return setAlias(copyBuilder(), alias).build(); } @Override public PrimaryKeyColumn qualifiedWith(String tableQualifier) { - return copyBuilder().withTableQualifier(tableQualifier).build(); + return setTableQualifier(copyBuilder(), tableQualifier).build(); } @Override public PrimaryKeyColumn asCamelCase() { - return copyBuilder() - .withAlias("\"" + StringUtilities.toCamelCase(name) + "\"").build(); //$NON-NLS-1$ //$NON-NLS-2$ + return setCamelCaseAlias(copyBuilder()).build(); } @Override From d8b68864265afc90d8ed7d19e344d46c949c4c60 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Thu, 23 Oct 2025 16:33:19 -0400 Subject: [PATCH 45/68] Better extension pattern --- .../org/mybatis/dynamic/sql/SqlColumn.java | 83 ++++++++++--------- .../examples/simple/PrimaryKeyColumn.java | 26 +++--- 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index 2f8a5aaff..ee372794b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -30,18 +30,30 @@ * *

The class contains many attributes that are helpful for use in MyBatis and Spring runtime * environments, but the only required attributes are the name of the column and a reference to - * the SqlTable the column is a part of. + * the {@link SqlTable} the column is a part of. * *

The class can be extended if you wish to associate additional attributes with a column for your - * own purposes. Extending the class involves the following activities: + * own purposes. Extending the class is a bit more challenging than you might expect because you will need to + * handle the covariant types for many methods in {@code SqlColumn}. Additionally, many methods in {@code SqlColumn} + * create new instances of the class in keeping with the library's primary strategy of immutability. You will also + * need to ensure that these methods create instances of your extended class, rather than the base {@code SqlColumn} + * class. We have worked to keep this process as simple as possible. + * + *

Extending the class involves the following activities: *

    *
  1. Create a class that extends {@link SqlColumn}
  2. *
  3. In your extended class, create a static builder class that extends {@link SqlColumn.AbstractBuilder}
  4. *
  5. Add your desired attributes to the class and the builder
  6. + *
  7. In your extended class, override the {@link SqlColumn#copyBuilder()} method and return a new instance of + * your builder with all attributes set. You should call the + * {@link SqlColumn#populateBaseBuilder(AbstractBuilder)} method + * to set the attributes from {@code SqlColumn}, then populate your extended attributes. + *
  8. *
  9. You MUST override the following methods. These methods are used with regular operations in the library. * If you do not override these methods, it is likely that your extended attributes will be lost during - * regular usage. For example, if a user calls the {@code as} method to apply an alias, the base - * {@code SqlColumn} class will create a new instance of {@code SqlColumn}, NOT your extended class. + * regular usage. For example, if you do not override the {@code as} method and a user calls the method to + * apply an alias, then the base {@code SqlColumn} class would create a new instance of {@code SqlColumn}, NOT + * your extended class. *
      *
    • {@link SqlColumn#as(String)}
    • *
    • {@link SqlColumn#asCamelCase()}
    • @@ -63,10 +75,21 @@ * *
* - *

The test code for this library contains an example of a proper extension of this class. In most cases, the code - * for the overriding methods can be simply copied from this class and then the return types can be changed to the - * subclass's covariant type (this pre-supposes that you create a {@code copyBuilder} method that returns a new builder - * populated with all current class attributes). + *

For all overridden methods except {@code copyBuilder()}, the process is to call the superclass + * method and cast the result properly. We provide a {@link SqlColumn#cast(SqlColumn)} method to aid with this + * process. For example, overriding the {@code descending} method could look like this: + * + *

+ *

+ * {@code
+ * @Override
+ * public MyExtendedColumn descending() {
+ *     return cast(super.descending());
+ * }
+ * }
+ * 
+ * + *

The test code for this library contains an example of a proper extension of this class. * * @param the Java type associated with the column */ @@ -84,7 +107,7 @@ public class SqlColumn implements BindableColumn, SortSpecification { protected final @Nullable Class javaType; protected final @Nullable String javaProperty; - protected SqlColumn(AbstractBuilder builder) { + protected SqlColumn(AbstractBuilder builder) { name = Objects.requireNonNull(builder.name); table = Objects.requireNonNull(builder.table); jdbcType = builder.jdbcType; @@ -142,11 +165,7 @@ public Optional javaProperty() { */ @Override public SqlColumn descending() { - return setDescending(copyBuilder()).build(); - } - - protected > B setDescending(B builder) { - return cast(builder.withDescendingPhrase(" DESC")); //$NON-NLS-1$ + return cast(copyBuilder().withDescendingPhrase(" DESC").build()); //$NON-NLS-1$ } /** @@ -159,11 +178,7 @@ public SqlColumn descending() { */ @Override public SqlColumn as(String alias) { - return setAlias(copyBuilder(), alias).build(); - } - - protected > B setAlias(B builder, String alias) { - return cast(builder.withAlias(alias)); + return cast(copyBuilder().withAlias(alias).build()); } /** @@ -174,11 +189,7 @@ public SqlColumn as(String alias) { * @return a new column that will be rendered with the specified table qualifier */ public SqlColumn qualifiedWith(String tableQualifier) { - return setTableQualifier(copyBuilder(), tableQualifier).build(); - } - - protected > B setTableQualifier(B builder, String tableQualifier) { - return cast(builder.withTableQualifier(tableQualifier)); + return cast(copyBuilder().withTableQualifier(tableQualifier).build()); } /** @@ -193,11 +204,9 @@ public SqlColumn qualifiedWith(String tableQualifier) { * @return a new column aliased with a camel case version of the column name */ public SqlColumn asCamelCase() { - return setCamelCaseAlias(copyBuilder()).build(); - } - - protected > B setCamelCaseAlias(B builder) { - return cast(builder.withAlias("\"" + StringUtilities.toCamelCase(name) + "\"")); //$NON-NLS-1$ //$NON-NLS-2$ + return cast(copyBuilder() + .withAlias("\"" + StringUtilities.toCamelCase(name) + "\"") //$NON-NLS-1$ //$NON-NLS-2$ + .build()); } @Override @@ -310,7 +319,7 @@ public SqlColumn withJavaProperty(String javaProperty) { return cast(copyBuilder().withJavaProperty(javaProperty).build()); } - private Builder copyBuilder() { + protected AbstractBuilder copyBuilder() { return populateBaseBuilder(new Builder<>()); } @@ -319,11 +328,6 @@ protected > S cast(SqlColumn column) { return (S) column; } - @SuppressWarnings("unchecked") - protected > B cast(AbstractBuilder builder) { - return (B) builder; - } - /** * This method will add all current attributes to the specified builder. It is useful when creating * new class instances that only change one attribute - we set all current attributes, then @@ -334,7 +338,7 @@ protected > S cast(SqlColumn column) { * @return the populated builder */ @SuppressWarnings("unchecked") - protected > B populateBaseBuilder(B builder) { + protected > B populateBaseBuilder(B builder) { return (B) builder .withName(this.name) .withTable(this.table) @@ -362,7 +366,7 @@ public static SqlColumn of(String name, SqlTable table, JDBCType jdbcType .build(); } - public static abstract class AbstractBuilder> { + public static abstract class AbstractBuilder, B extends AbstractBuilder> { protected @Nullable String name; protected @Nullable SqlTable table; protected @Nullable JDBCType jdbcType; @@ -431,9 +435,12 @@ public B withJavaProperty(@Nullable String javaProperty) { } protected abstract B getThis(); + + public abstract C build(); } - public static class Builder extends AbstractBuilder> { + public static class Builder extends AbstractBuilder, Builder> { + @Override public SqlColumn build() { return new SqlColumn<>(this); } diff --git a/src/test/java/examples/simple/PrimaryKeyColumn.java b/src/test/java/examples/simple/PrimaryKeyColumn.java index b54bbf26c..a12c53556 100644 --- a/src/test/java/examples/simple/PrimaryKeyColumn.java +++ b/src/test/java/examples/simple/PrimaryKeyColumn.java @@ -33,56 +33,55 @@ public boolean isPrimaryKeyColumn() { @Override public PrimaryKeyColumn descending() { - return setDescending(copyBuilder()).build(); + return cast(super.descending()); } @Override public PrimaryKeyColumn as(String alias) { - return setAlias(copyBuilder(), alias).build(); + return cast(super.as(alias)); } @Override public PrimaryKeyColumn qualifiedWith(String tableQualifier) { - return setTableQualifier(copyBuilder(), tableQualifier).build(); + return cast(super.qualifiedWith(tableQualifier)); } @Override public PrimaryKeyColumn asCamelCase() { - return setCamelCaseAlias(copyBuilder()).build(); + return cast(super.asCamelCase()); } @Override public PrimaryKeyColumn withTypeHandler(String typeHandler) { - return cast(copyBuilder().withTypeHandler(typeHandler).build()); + return cast(super.withTypeHandler(typeHandler)); } @Override public PrimaryKeyColumn withRenderingStrategy(RenderingStrategy renderingStrategy) { - return cast(copyBuilder().withRenderingStrategy(renderingStrategy).build()); + return cast(super.withRenderingStrategy(renderingStrategy)); } @Override - @SuppressWarnings("unchecked") public PrimaryKeyColumn withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { - return cast(copyBuilder().withParameterTypeConverter((ParameterTypeConverter) parameterTypeConverter).build()); + return cast(super.withParameterTypeConverter(parameterTypeConverter)); } @Override - @SuppressWarnings("unchecked") public PrimaryKeyColumn withJavaType(Class javaType) { - return cast(copyBuilder().withJavaType((Class) javaType).build()); + return cast(super.withJavaType(javaType)); } @Override public PrimaryKeyColumn withJavaProperty(String javaProperty) { - return cast(copyBuilder().withJavaProperty(javaProperty).build()); + return cast(super.withJavaProperty(javaProperty)); } - private Builder copyBuilder() { + @Override + protected Builder copyBuilder() { return populateBaseBuilder(new Builder<>()).isPrimaryKeyColumn(isPrimaryKeyColumn); } - public static class Builder extends AbstractBuilder> { + public static class Builder extends AbstractBuilder, Builder> { private boolean isPrimaryKeyColumn; public Builder isPrimaryKeyColumn(boolean isPrimaryKeyColumn) { @@ -90,6 +89,7 @@ public Builder isPrimaryKeyColumn(boolean isPrimaryKeyColumn) { return this; } + @Override public PrimaryKeyColumn build() { return new PrimaryKeyColumn<>(this); } From 8a8e7e0c18ae8a5647747b1a18a226d5df3634e4 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Thu, 23 Oct 2025 16:38:10 -0400 Subject: [PATCH 46/68] Documentation --- src/test/java/examples/simple/PrimaryKeyColumn.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/examples/simple/PrimaryKeyColumn.java b/src/test/java/examples/simple/PrimaryKeyColumn.java index a12c53556..30d51f72a 100644 --- a/src/test/java/examples/simple/PrimaryKeyColumn.java +++ b/src/test/java/examples/simple/PrimaryKeyColumn.java @@ -19,6 +19,11 @@ import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.render.RenderingStrategy; +/** + * This class is an example of a properly extended {@link SqlColumn}. + * + * @param the Java type associated with this column + */ public class PrimaryKeyColumn extends SqlColumn { private final boolean isPrimaryKeyColumn; From 3a7b7eaa6fe3b9b973be7260d1216109e73d6219 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Thu, 23 Oct 2025 17:40:40 -0400 Subject: [PATCH 47/68] Checkstyle Updates --- .../org/mybatis/dynamic/sql/SqlColumn.java | 13 ++++++------- .../IsGreaterThanOrEqualToWhenPresent.java | 18 +++++++++--------- .../IsLikeCaseInsensitiveWhenPresent.java | 18 +++++++++--------- .../IsNotLikeCaseInsensitiveWhenPresent.java | 18 +++++++++--------- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index ee372794b..b3dcf9513 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -79,7 +79,6 @@ * method and cast the result properly. We provide a {@link SqlColumn#cast(SqlColumn)} method to aid with this * process. For example, overriding the {@code descending} method could look like this: * - *

*

  * {@code
  * @Override
@@ -237,8 +236,8 @@ public Optional renderingStrategy() {
      * and method chaining would not work. This is a workaround for Java's lack of reification.
      *
      * @param typeHandler the type handler to set
-     * @return a new column instance with the specified type handler
      * @param  the type of the new column (will be the same as T)
+     * @return a new column instance with the specified type handler
      */
     public  SqlColumn withTypeHandler(String typeHandler) {
         return cast(copyBuilder().withTypeHandler(typeHandler).build());
@@ -253,8 +252,8 @@ public  SqlColumn withTypeHandler(String typeHandler) {
      * and method chaining would not work. This is a workaround for Java's lack of reification.
      *
      * @param renderingStrategy the rendering strategy to set
-     * @return a new column instance with the specified type handler
      * @param  the type of the new column (will be the same as T)
+     * @return a new column instance with the specified type handler
      */
     public  SqlColumn withRenderingStrategy(RenderingStrategy renderingStrategy) {
         return cast(copyBuilder().withRenderingStrategy(renderingStrategy).build());
@@ -271,8 +270,8 @@ public  SqlColumn withRenderingStrategy(RenderingStrategy renderingStrateg
      * and method chaining would not work. This is a workaround for Java's lack of reification.
      *
      * @param parameterTypeConverter the parameter type converter to set
-     * @return a new column instance with the specified type handler
      * @param  the type of the new column (will be the same as T)
+     * @return a new column instance with the specified type handler
      */
     @SuppressWarnings("unchecked")
     public  SqlColumn withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) {
@@ -292,8 +291,8 @@ public  SqlColumn withParameterTypeConverter(ParameterTypeConverter
      * and method chaining would not work. This is a workaround for Java's lack of reification.
      *
      * @param javaType the Java type to set
-     * @return a new column instance with the specified type handler
      * @param  the type of the new column (will be the same as T)
+     * @return a new column instance with the specified type handler
      */
     @SuppressWarnings("unchecked")
     public  SqlColumn withJavaType(Class javaType) {
@@ -312,8 +311,8 @@ public  SqlColumn withJavaType(Class javaType) {
      * and method chaining would not work. This is a workaround for Java's lack of reification.
      *
      * @param javaProperty the Java property to set
-     * @return a new column instance with the specified type handler
      * @param  the type of the new column (will be the same as T)
+     * @return a new column instance with the specified type handler
      */
     public  SqlColumn withJavaProperty(String javaProperty) {
         return cast(copyBuilder().withJavaProperty(javaProperty).build());
@@ -366,7 +365,7 @@ public static  SqlColumn of(String name, SqlTable table, JDBCType jdbcType
                 .build();
     }
 
-    public static abstract class AbstractBuilder, B extends AbstractBuilder> {
+    public abstract static class AbstractBuilder, B extends AbstractBuilder> {
         protected @Nullable String name;
         protected @Nullable SqlTable table;
         protected @Nullable JDBCType jdbcType;
diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualToWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualToWhenPresent.java
index ccb868c94..970ef0775 100644
--- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualToWhenPresent.java
+++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsGreaterThanOrEqualToWhenPresent.java
@@ -26,16 +26,16 @@ public class IsGreaterThanOrEqualToWhenPresent extends AbstractSingleValueCon
         implements AbstractSingleValueCondition.Filterable, AbstractSingleValueCondition.Mappable {
     private static final IsGreaterThanOrEqualToWhenPresent EMPTY =
             new IsGreaterThanOrEqualToWhenPresent(-1) {
-        @Override
-        public Object value() {
-            throw new NoSuchElementException("No value present"); //$NON-NLS-1$
-        }
+                @Override
+                public Object value() {
+                    throw new NoSuchElementException("No value present"); //$NON-NLS-1$
+                }
 
-        @Override
-        public boolean isEmpty() {
-            return true;
-        }
-    };
+                @Override
+                public boolean isEmpty() {
+                    return true;
+                }
+            };
 
     public static  IsGreaterThanOrEqualToWhenPresent empty() {
         @SuppressWarnings("unchecked")
diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitiveWhenPresent.java
index ae8398aef..0f06e3311 100644
--- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitiveWhenPresent.java
+++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsLikeCaseInsensitiveWhenPresent.java
@@ -28,16 +28,16 @@ public class IsLikeCaseInsensitiveWhenPresent extends AbstractSingleValueCond
         AbstractSingleValueCondition.Mappable {
     private static final IsLikeCaseInsensitiveWhenPresent EMPTY =
             new IsLikeCaseInsensitiveWhenPresent<>("") { //$NON-NLS-1$
-        @Override
-        public String value() {
-            throw new NoSuchElementException("No value present"); //$NON-NLS-1$
-        }
+                @Override
+                public String value() {
+                    throw new NoSuchElementException("No value present"); //$NON-NLS-1$
+                }
 
-        @Override
-        public boolean isEmpty() {
-            return true;
-        }
-    };
+                @Override
+                public boolean isEmpty() {
+                    return true;
+                }
+            };
 
     public static  IsLikeCaseInsensitiveWhenPresent empty() {
         @SuppressWarnings("unchecked")
diff --git a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitiveWhenPresent.java b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitiveWhenPresent.java
index df07202f9..880b20ab8 100644
--- a/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitiveWhenPresent.java
+++ b/src/main/java/org/mybatis/dynamic/sql/where/condition/IsNotLikeCaseInsensitiveWhenPresent.java
@@ -28,16 +28,16 @@ public class IsNotLikeCaseInsensitiveWhenPresent extends AbstractSingleValueC
         AbstractSingleValueCondition.Mappable {
     private static final IsNotLikeCaseInsensitiveWhenPresent EMPTY =
             new IsNotLikeCaseInsensitiveWhenPresent<>("") { //$NON-NLS-1$
-        @Override
-        public String value() {
-            throw new NoSuchElementException("No value present"); //$NON-NLS-1$
-        }
+                @Override
+                public String value() {
+                    throw new NoSuchElementException("No value present"); //$NON-NLS-1$
+                }
 
-        @Override
-        public boolean isEmpty() {
-            return true;
-        }
-    };
+                @Override
+                public boolean isEmpty() {
+                    return true;
+                }
+            };
 
     public static  IsNotLikeCaseInsensitiveWhenPresent empty() {
         @SuppressWarnings("unchecked")

From 47122b37a47966c6facce7a08a912815622f44a5 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 27 Oct 2025 17:12:00 +0000
Subject: [PATCH 48/68] Update dependency com.mysql:mysql-connector-j to v9.5.0

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index f7d4788a3..a2d5e0b4a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -214,7 +214,7 @@
     
       com.mysql
       mysql-connector-j
-      9.4.0
+      9.5.0
       test
     
   

From c52c52b3ab088a30f2f3d6996e0bff1b9e4b92bb Mon Sep 17 00:00:00 2001
From: Jeff Butler 
Date: Mon, 27 Oct 2025 14:08:34 -0400
Subject: [PATCH 49/68] Update docs for extending SqlColumn

---
 .../org/mybatis/dynamic/sql/SqlColumn.java    | 46 ++++++++++++-------
 1 file changed, 29 insertions(+), 17 deletions(-)

diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java
index b3dcf9513..d8f9dc56e 100644
--- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java
+++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java
@@ -33,7 +33,7 @@
  * the {@link SqlTable} the column is a part of.
  *
  * 

The class can be extended if you wish to associate additional attributes with a column for your - * own purposes. Extending the class is a bit more challenging than you might expect because you will need to + * own purposes. Extending the class is a bit more challenging than you might expect because you may need to * handle the covariant types for many methods in {@code SqlColumn}. Additionally, many methods in {@code SqlColumn} * create new instances of the class in keeping with the library's primary strategy of immutability. You will also * need to ensure that these methods create instances of your extended class, rather than the base {@code SqlColumn} @@ -44,16 +44,18 @@ *

  • Create a class that extends {@link SqlColumn}
  • *
  • In your extended class, create a static builder class that extends {@link SqlColumn.AbstractBuilder}
  • *
  • Add your desired attributes to the class and the builder
  • - *
  • In your extended class, override the {@link SqlColumn#copyBuilder()} method and return a new instance of - * your builder with all attributes set. You should call the + *
  • You MUST override the {@link SqlColumn#copyBuilder()} method and return a new instance of + * your builder with all attributes set. In the overridden method you should call the superclass * {@link SqlColumn#populateBaseBuilder(AbstractBuilder)} method - * to set the attributes from {@code SqlColumn}, then populate your extended attributes. + * to set the attributes from the base {@code SqlColumn}, then populate your extended attributes. During normal + * usage, the library may create additional instances of your class. If you do not override the + * {@link SqlColumn#copyBuilder()} method properly, then your extended attributes will be lost. *
  • - *
  • You MUST override the following methods. These methods are used with regular operations in the library. - * If you do not override these methods, it is likely that your extended attributes will be lost during - * regular usage. For example, if you do not override the {@code as} method and a user calls the method to - * apply an alias, then the base {@code SqlColumn} class would create a new instance of {@code SqlColumn}, NOT - * your extended class. + *
  • You MAY override the following methods. These methods are used with regular operations in the library and + * create new instances of the class. However, these methods are not typically chained, so losing the specific + * type may not be a problem. If you want to preserve the type, then you can override these methods + * to specify the covariant return type. See below for usage of the {@link SqlColumn#cast(SqlColumn)} method + * to make it easier to override these methods. *
      *
    • {@link SqlColumn#as(String)}
    • *
    • {@link SqlColumn#asCamelCase()}
    • @@ -63,8 +65,10 @@ * *
    • You SHOULD override the following methods. These methods can be used to add additional attributes to a * column by creating a new instance with a specified attribute set. These methods are used during the - * construction of columns. If you do not override these methods, and a user calls them, then a new - * {@code SqlColumn} will be created that does not contain your extended attributes. + * construction of columns. If you do not override these methods, and a user calls them, then the specific type + * will be lost. If you want to preserve the type, then you can override these methods + * to specify the covariant return type. See below for usage of the {@link SqlColumn#cast(SqlColumn)} method + * to make it easier to override these methods. *
        *
      • {@link SqlColumn#withJavaProperty(String)}
      • *
      • {@link SqlColumn#withRenderingStrategy(RenderingStrategy)}
      • @@ -88,7 +92,7 @@ * } * * - *

        The test code for this library contains an example of a proper extension of this class. + *

        The test code for this library contains an example of a fully executed extension of this class. * * @param the Java type associated with the column */ @@ -164,7 +168,7 @@ public Optional javaProperty() { */ @Override public SqlColumn descending() { - return cast(copyBuilder().withDescendingPhrase(" DESC").build()); //$NON-NLS-1$ + return copyBuilder().withDescendingPhrase(" DESC").build(); //$NON-NLS-1$ } /** @@ -177,7 +181,7 @@ public SqlColumn descending() { */ @Override public SqlColumn as(String alias) { - return cast(copyBuilder().withAlias(alias).build()); + return copyBuilder().withAlias(alias).build(); } /** @@ -188,7 +192,7 @@ public SqlColumn as(String alias) { * @return a new column that will be rendered with the specified table qualifier */ public SqlColumn qualifiedWith(String tableQualifier) { - return cast(copyBuilder().withTableQualifier(tableQualifier).build()); + return copyBuilder().withTableQualifier(tableQualifier).build(); } /** @@ -203,9 +207,9 @@ public SqlColumn qualifiedWith(String tableQualifier) { * @return a new column aliased with a camel case version of the column name */ public SqlColumn asCamelCase() { - return cast(copyBuilder() + return copyBuilder() .withAlias("\"" + StringUtilities.toCamelCase(name) + "\"") //$NON-NLS-1$ //$NON-NLS-2$ - .build()); + .build(); } @Override @@ -318,6 +322,14 @@ public SqlColumn withJavaProperty(String javaProperty) { return cast(copyBuilder().withJavaProperty(javaProperty).build()); } + /** + * Create a new Builder, then populate all attributes in the builder with current values. + * + *

        This method is used to create copies of the class during normal operations (e.g. when calling the + * {@link SqlColumn#as(String)} method). Any subclass of {@code SqlColumn} MUST override this method. + * + * @return a new Builder instance with all current values populated + */ protected AbstractBuilder copyBuilder() { return populateBaseBuilder(new Builder<>()); } From 95b62b5ddbb1bc32f938c6742ebffcdc76f94163 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Mon, 27 Oct 2025 14:44:16 -0400 Subject: [PATCH 50/68] Upgrade to TestContainers 2 --- pom.xml | 10 +++++----- src/test/java/config/TestContainersConfiguration.java | 3 +-- .../examples/custom_render/CustomRenderingTest.java | 7 +++---- src/test/java/examples/mariadb/MariaDBTest.java | 8 +++----- src/test/java/examples/mariadb/OrderByCaseTest.java | 7 +++---- src/test/java/examples/mysql/MySQLTest.java | 7 +++---- src/test/java/examples/postgres/PostgresTest.java | 7 +++---- src/test/java/issues/gh655/Gh655Test.java | 7 +++---- .../mybatis3/custom/render/KCustomRenderingTest.kt | 2 +- .../examples/kotlin/mybatis3/mariadb/KMariaDBTest.kt | 2 +- 10 files changed, 26 insertions(+), 34 deletions(-) diff --git a/pom.xml b/pom.xml index f7d4788a3..17aeb4966 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ http://localhost:9000 official - 1.21.3 + 2.0.1 org.mybatis.dynamic.sql.*;version=${project.version};-noimport:=true @@ -177,13 +177,13 @@ org.testcontainers - junit-jupiter + testcontainers-junit-jupiter ${test.containers.version} test org.testcontainers - postgresql + testcontainers-postgresql ${test.containers.version} test @@ -195,7 +195,7 @@ org.testcontainers - mariadb + testcontainers-mariadb ${test.containers.version} test @@ -207,7 +207,7 @@ org.testcontainers - mysql + testcontainers-mysql ${test.containers.version} test diff --git a/src/test/java/config/TestContainersConfiguration.java b/src/test/java/config/TestContainersConfiguration.java index 0b39fbb85..f78bd5030 100644 --- a/src/test/java/config/TestContainersConfiguration.java +++ b/src/test/java/config/TestContainersConfiguration.java @@ -23,6 +23,5 @@ public interface TestContainersConfiguration { DockerImageName POSTGRES_LATEST = DockerImageName.parse("postgres:18.0"); DockerImageName MARIADB_LATEST = DockerImageName.parse("mariadb:12.0.2"); - // Note - Can't go past MySQL:9.2.0 until this is released: https://github.com/testcontainers/testcontainers-java/pull/10185 - DockerImageName MYSQL_LATEST = DockerImageName.parse("mysql:9.2.0"); + DockerImageName MYSQL_LATEST = DockerImageName.parse("mysql:9.5.0"); } diff --git a/src/test/java/examples/custom_render/CustomRenderingTest.java b/src/test/java/examples/custom_render/CustomRenderingTest.java index 8b0541c1e..5551b0a95 100644 --- a/src/test/java/examples/custom_render/CustomRenderingTest.java +++ b/src/test/java/examples/custom_render/CustomRenderingTest.java @@ -50,17 +50,16 @@ import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider; import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper; -import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.postgresql.PostgreSQLContainer; @Testcontainers class CustomRenderingTest { - @SuppressWarnings("resource") @Container - private static final PostgreSQLContainer postgres = - new PostgreSQLContainer<>(TestContainersConfiguration.POSTGRES_LATEST) + private static final PostgreSQLContainer postgres = + new PostgreSQLContainer(TestContainersConfiguration.POSTGRES_LATEST) .withInitScript("examples/custom_render/dbInit.sql"); private SqlSessionFactory sqlSessionFactory; diff --git a/src/test/java/examples/mariadb/MariaDBTest.java b/src/test/java/examples/mariadb/MariaDBTest.java index a98b2e326..fed06dc5e 100644 --- a/src/test/java/examples/mariadb/MariaDBTest.java +++ b/src/test/java/examples/mariadb/MariaDBTest.java @@ -49,18 +49,16 @@ import org.mybatis.dynamic.sql.util.mybatis3.CommonDeleteMapper; import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper; import org.mybatis.dynamic.sql.util.mybatis3.CommonUpdateMapper; -import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; - +import org.testcontainers.mariadb.MariaDBContainer; @Testcontainers class MariaDBTest { - @SuppressWarnings("resource") @Container - private static final MariaDBContainer mariadb = - new MariaDBContainer<>(TestContainersConfiguration.MARIADB_LATEST) + private static final MariaDBContainer mariadb = + new MariaDBContainer(TestContainersConfiguration.MARIADB_LATEST) .withInitScript("examples/mariadb/CreateDB.sql"); private static SqlSessionFactory sqlSessionFactory; diff --git a/src/test/java/examples/mariadb/OrderByCaseTest.java b/src/test/java/examples/mariadb/OrderByCaseTest.java index f842ecb1b..d144e442e 100644 --- a/src/test/java/examples/mariadb/OrderByCaseTest.java +++ b/src/test/java/examples/mariadb/OrderByCaseTest.java @@ -39,17 +39,16 @@ import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper; -import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.mariadb.MariaDBContainer; @Testcontainers class OrderByCaseTest { - @SuppressWarnings("resource") @Container - private static final MariaDBContainer mariadb = - new MariaDBContainer<>(TestContainersConfiguration.MARIADB_LATEST) + private static final MariaDBContainer mariadb = + new MariaDBContainer(TestContainersConfiguration.MARIADB_LATEST) .withInitScript("examples/mariadb/CreateDB.sql"); private static SqlSessionFactory sqlSessionFactory; diff --git a/src/test/java/examples/mysql/MySQLTest.java b/src/test/java/examples/mysql/MySQLTest.java index f9c047f77..f1eb82682 100644 --- a/src/test/java/examples/mysql/MySQLTest.java +++ b/src/test/java/examples/mysql/MySQLTest.java @@ -38,17 +38,16 @@ import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper; -import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.mysql.MySQLContainer; @Testcontainers class MySQLTest { - @SuppressWarnings("resource") @Container - private static final MySQLContainer mysql = - new MySQLContainer<>(TestContainersConfiguration.MYSQL_LATEST) + private static final MySQLContainer mysql = + new MySQLContainer(TestContainersConfiguration.MYSQL_LATEST) .withInitScript("examples/mariadb/CreateDB.sql"); private SqlSessionFactory sqlSessionFactory; diff --git a/src/test/java/examples/postgres/PostgresTest.java b/src/test/java/examples/postgres/PostgresTest.java index f8da9c328..368d7ce0c 100644 --- a/src/test/java/examples/postgres/PostgresTest.java +++ b/src/test/java/examples/postgres/PostgresTest.java @@ -35,17 +35,16 @@ import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper; -import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.postgresql.PostgreSQLContainer; @Testcontainers class PostgresTest { - @SuppressWarnings("resource") @Container - private static final PostgreSQLContainer postgres = - new PostgreSQLContainer<>(TestContainersConfiguration.POSTGRES_LATEST) + private static final PostgreSQLContainer postgres = + new PostgreSQLContainer(TestContainersConfiguration.POSTGRES_LATEST) .withInitScript("examples/postgres/dbInit.sql"); private static SqlSessionFactory sqlSessionFactory; diff --git a/src/test/java/issues/gh655/Gh655Test.java b/src/test/java/issues/gh655/Gh655Test.java index e1eb4578f..47615572a 100644 --- a/src/test/java/issues/gh655/Gh655Test.java +++ b/src/test/java/issues/gh655/Gh655Test.java @@ -41,17 +41,16 @@ import org.mybatis.dynamic.sql.select.SelectModel; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper; -import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.mariadb.MariaDBContainer; @Testcontainers class Gh655Test { - @SuppressWarnings("resource") @Container - private static final MariaDBContainer mariadb = - new MariaDBContainer<>(TestContainersConfiguration.MARIADB_LATEST) + private static final MariaDBContainer mariadb = + new MariaDBContainer(TestContainersConfiguration.MARIADB_LATEST) .withInitScript("examples/mariadb/CreateDB.sql"); private static SqlSessionFactory sqlSessionFactory; diff --git a/src/test/kotlin/examples/kotlin/mybatis3/custom/render/KCustomRenderingTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/custom/render/KCustomRenderingTest.kt index 47a6fcaed..72e4d0b81 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/custom/render/KCustomRenderingTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/custom/render/KCustomRenderingTest.kt @@ -35,9 +35,9 @@ import org.mybatis.dynamic.sql.util.kotlin.mybatis3.insertMultiple import org.mybatis.dynamic.sql.util.kotlin.mybatis3.select import org.mybatis.dynamic.sql.util.kotlin.mybatis3.update import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper -import org.testcontainers.containers.PostgreSQLContainer import org.testcontainers.junit.jupiter.Container import org.testcontainers.junit.jupiter.Testcontainers +import org.testcontainers.postgresql.PostgreSQLContainer import java.sql.JDBCType @Testcontainers diff --git a/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KMariaDBTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KMariaDBTest.kt index 28bf98a3b..c90178cba 100644 --- a/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KMariaDBTest.kt +++ b/src/test/kotlin/examples/kotlin/mybatis3/mariadb/KMariaDBTest.kt @@ -35,9 +35,9 @@ import org.mybatis.dynamic.sql.util.kotlin.mybatis3.update import org.mybatis.dynamic.sql.util.mybatis3.CommonDeleteMapper import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper import org.mybatis.dynamic.sql.util.mybatis3.CommonUpdateMapper -import org.testcontainers.containers.MariaDBContainer import org.testcontainers.junit.jupiter.Container import org.testcontainers.junit.jupiter.Testcontainers +import org.testcontainers.mariadb.MariaDBContainer import java.util.Locale @Testcontainers From 61ec5cde4adbdf0a1789ae935fe083a8ca81f632 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 19:53:01 +0000 Subject: [PATCH 51/68] Update junit-framework monorepo to v6.0.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5c9c0da7d..c182f9ed2 100644 --- a/pom.xml +++ b/pom.xml @@ -60,7 +60,7 @@ 17 17 17 - 6.0.0 + 6.0.1 5.2.4 checkstyle-override.xml From 8788747da43d8a4864953f2e4090f6479542e449 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 4 Nov 2025 13:04:49 -0500 Subject: [PATCH 52/68] Remove Kotlin Array-Based Functions The newer Kotlin compiler versions have difficulty distinguishing these methods from the similarly named vararg methods. I don't think they are in wide use - there were only a few tests that used them, and those tests were easily modified. --- .../util/kotlin/GroupingCriteriaCollector.kt | 30 +------------------ .../sql/util/kotlin/elements/SqlElements.kt | 28 ----------------- .../spring/canonical/InfixElementsTest.kt | 20 ++++++------- 3 files changed, 11 insertions(+), 67 deletions(-) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt index be4ba72af..9a2070ea3 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt @@ -317,10 +317,6 @@ open class GroupingCriteriaCollector : SubCriteriaCollector() { fun BindableColumn.isIn(vararg values: T) = isIn(values.asList()) - @JvmName("isInArray") - infix fun BindableColumn.isIn(values: Array) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isIn(values)) - infix fun BindableColumn.isIn(values: Collection) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isIn(values)) @@ -329,19 +325,11 @@ open class GroupingCriteriaCollector : SubCriteriaCollector() { fun BindableColumn.isInWhenPresent(vararg values: T?) = isInWhenPresent(values.asList()) - @JvmName("isInArrayWhenPresent") - infix fun BindableColumn.isInWhenPresent(values: Array?) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isInWhenPresent(values)) - infix fun BindableColumn.isInWhenPresent(values: Collection?) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isInWhenPresent(values)) fun BindableColumn.isNotIn(vararg values: T) = isNotIn(values.asList()) - @JvmName("isNotInArray") - infix fun BindableColumn.isNotIn(values: Array) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotIn(values)) - infix fun BindableColumn.isNotIn(values: Collection) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotIn(values)) @@ -352,7 +340,7 @@ open class GroupingCriteriaCollector : SubCriteriaCollector() { @JvmName("isNotInArrayWhenPresent") infix fun BindableColumn.isNotInWhenPresent(values: Array?) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInWhenPresent(values)) + invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInWhenPresent(values?.asList())) infix fun BindableColumn.isNotInWhenPresent(values: Collection?) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInWhenPresent(values)) @@ -410,40 +398,24 @@ open class GroupingCriteriaCollector : SubCriteriaCollector() { fun BindableColumn.isInCaseInsensitive(vararg values: String) = isInCaseInsensitive(values.asList()) - @JvmName("isInArrayCaseInsensitive") - infix fun BindableColumn.isInCaseInsensitive(values: Array) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isInCaseInsensitive(values)) - infix fun BindableColumn.isInCaseInsensitive(values: Collection) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isInCaseInsensitive(values)) fun BindableColumn.isInCaseInsensitiveWhenPresent(vararg values: String?) = isInCaseInsensitiveWhenPresent(values.asList()) - @JvmName("isInArrayCaseInsensitiveWhenPresent") - infix fun BindableColumn.isInCaseInsensitiveWhenPresent(values: Array?) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isInCaseInsensitiveWhenPresent(values)) - infix fun BindableColumn.isInCaseInsensitiveWhenPresent(values: Collection?) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isInCaseInsensitiveWhenPresent(values)) fun BindableColumn.isNotInCaseInsensitive(vararg values: String) = isNotInCaseInsensitive(values.asList()) - @JvmName("isNotInArrayCaseInsensitive") - infix fun BindableColumn.isNotInCaseInsensitive(values: Array) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInCaseInsensitive(values)) - infix fun BindableColumn.isNotInCaseInsensitive(values: Collection) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInCaseInsensitive(values)) fun BindableColumn.isNotInCaseInsensitiveWhenPresent(vararg values: String?) = isNotInCaseInsensitiveWhenPresent(values.asList()) - @JvmName("isNotInArrayCaseInsensitiveWhenPresent") - infix fun BindableColumn.isNotInCaseInsensitiveWhenPresent(values: Array?) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInCaseInsensitiveWhenPresent(values)) - infix fun BindableColumn.isNotInCaseInsensitiveWhenPresent(values: Collection?) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInCaseInsensitiveWhenPresent(values)) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt index 2002fc75a..1fe9cfb38 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/elements/SqlElements.kt @@ -286,9 +286,6 @@ fun isLessThanOrEqualToWhenPresent(value: T?): IsLessThanOrEqualToWhen fun isIn(vararg values: T): IsIn = isIn(values.asList()) -@JvmName("isInArray") -fun isIn(values: Array): IsIn = SqlBuilder.isIn(values.asList()) - fun isIn(values: Collection): IsIn = SqlBuilder.isIn(values) fun isIn(subQuery: KotlinSubQueryBuilder.() -> Unit): IsInWithSubselect = @@ -296,16 +293,10 @@ fun isIn(subQuery: KotlinSubQueryBuilder.() -> Unit): IsInWithSubselec fun isInWhenPresent(vararg values: T?): IsInWhenPresent = isInWhenPresent(values.asList()) -@JvmName("isInArrayWhenPresent") -fun isInWhenPresent(values: Array?): IsInWhenPresent = SqlBuilder.isInWhenPresent(values?.asList()) - fun isInWhenPresent(values: Collection?): IsInWhenPresent = SqlBuilder.isInWhenPresent(values) fun isNotIn(vararg values: T): IsNotIn = isNotIn(values.asList()) -@JvmName("isNotInArray") -fun isNotIn(values: Array): IsNotIn = SqlBuilder.isNotIn(values.asList()) - fun isNotIn(values: Collection): IsNotIn = SqlBuilder.isNotIn(values) fun isNotIn(subQuery: KotlinSubQueryBuilder.() -> Unit): IsNotInWithSubselect = @@ -313,9 +304,6 @@ fun isNotIn(subQuery: KotlinSubQueryBuilder.() -> Unit): IsNotInWithSu fun isNotInWhenPresent(vararg values: T?): IsNotInWhenPresent = isNotInWhenPresent(values.asList()) -@JvmName("isNotInArrayWhenPresent") -fun isNotInWhenPresent(values: Array?): IsNotInWhenPresent = SqlBuilder.isNotInWhenPresent(values?.asList()) - fun isNotInWhenPresent(values: Collection?): IsNotInWhenPresent = SqlBuilder.isNotInWhenPresent(values) fun isBetween(value1: T): BetweenBuilder = BetweenBuilder(value1) @@ -354,40 +342,24 @@ fun isNotLikeCaseInsensitiveWhenPresent(value: String?): IsNotLikeCaseInsensitiv fun isInCaseInsensitive(vararg values: String): IsInCaseInsensitive = isInCaseInsensitive(values.asList()) -@JvmName("isInArrayCaseInsensitive") -fun isInCaseInsensitive(values: Array): IsInCaseInsensitive = - SqlBuilder.isInCaseInsensitive(values.asList()) - fun isInCaseInsensitive(values: Collection): IsInCaseInsensitive = SqlBuilder.isInCaseInsensitive(values) fun isInCaseInsensitiveWhenPresent(vararg values: String?): IsInCaseInsensitiveWhenPresent = isInCaseInsensitiveWhenPresent(values.asList()) -@JvmName("isInArrayCaseInsensitiveWhenPresent") -fun isInCaseInsensitiveWhenPresent(values: Array?): IsInCaseInsensitiveWhenPresent = - SqlBuilder.isInCaseInsensitiveWhenPresent(values?.asList()) - fun isInCaseInsensitiveWhenPresent(values: Collection?): IsInCaseInsensitiveWhenPresent = SqlBuilder.isInCaseInsensitiveWhenPresent(values) fun isNotInCaseInsensitive(vararg values: String): IsNotInCaseInsensitive = isNotInCaseInsensitive(values.asList()) -@JvmName("isNotInArrayCaseInsensitive") -fun isNotInCaseInsensitive(values: Array): IsNotInCaseInsensitive = - SqlBuilder.isNotInCaseInsensitive(values.asList()) - fun isNotInCaseInsensitive(values: Collection): IsNotInCaseInsensitive = SqlBuilder.isNotInCaseInsensitive(values) fun isNotInCaseInsensitiveWhenPresent(vararg values: String?): IsNotInCaseInsensitiveWhenPresent = isNotInCaseInsensitiveWhenPresent(values.asList()) -@JvmName("isNotInArrayCaseInsensitiveWhenPresent") -fun isNotInCaseInsensitiveWhenPresent(values: Array?): IsNotInCaseInsensitiveWhenPresent = - SqlBuilder.isNotInCaseInsensitiveWhenPresent(values?.asList()) - fun isNotInCaseInsensitiveWhenPresent(values: Collection?): IsNotInCaseInsensitiveWhenPresent = SqlBuilder.isNotInCaseInsensitiveWhenPresent(values) diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt index b1b9791f2..b6968648b 100644 --- a/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt +++ b/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt @@ -1270,7 +1270,7 @@ open class InfixElementsTest { fun search(vararg names: String) { val selectStatement = select(firstName) { from(person) - where { firstName isIn names } + where { firstName isIn names.asList() } orderBy(id) } @@ -1291,7 +1291,7 @@ open class InfixElementsTest { fun search(vararg names: String?) { val selectStatement = select(firstName) { from(person) - where { firstName isInWhenPresent names } + where { firstName isInWhenPresent names.asList() } orderBy(id) } @@ -1313,7 +1313,7 @@ open class InfixElementsTest { from(person) where { id isLessThan 10 - and { firstName isInWhenPresent null as Array? } + and { firstName isInWhenPresent null as List? } } orderBy(id) } @@ -1332,7 +1332,7 @@ open class InfixElementsTest { fun search(vararg names: String) { val selectStatement = select(firstName) { from(person) - where { firstName isNotIn names } + where { firstName isNotIn names.asList() } orderBy(id) } @@ -1394,7 +1394,7 @@ open class InfixElementsTest { fun search(vararg names: String) { val selectStatement = select(firstName) { from(person) - where { firstName isInCaseInsensitive names } + where { firstName isInCaseInsensitive names.asList() } orderBy(id) } @@ -1415,7 +1415,7 @@ open class InfixElementsTest { fun search(vararg names: String?) { val selectStatement = select(firstName) { from(person) - where { firstName isInCaseInsensitiveWhenPresent names } + where { firstName isInCaseInsensitiveWhenPresent names.asList() } orderBy(id) } @@ -1437,7 +1437,7 @@ open class InfixElementsTest { from(person) where { id isLessThan 10 - and { firstName isInCaseInsensitiveWhenPresent null as Array? } + and { firstName isInCaseInsensitiveWhenPresent null as List? } } orderBy(id) } @@ -1456,7 +1456,7 @@ open class InfixElementsTest { fun search(vararg names: String) { val selectStatement = select(firstName) { from(person) - where { firstName isNotInCaseInsensitive names } + where { firstName isNotInCaseInsensitive names.asList() } orderBy(id) } @@ -1477,7 +1477,7 @@ open class InfixElementsTest { fun search(vararg names: String?) { val selectStatement = select(firstName) { from(person) - where { firstName isNotInCaseInsensitiveWhenPresent names } + where { firstName isNotInCaseInsensitiveWhenPresent names.asList() } orderBy(id) } @@ -1499,7 +1499,7 @@ open class InfixElementsTest { from(person) where { id isLessThan 10 - and { firstName isNotInCaseInsensitiveWhenPresent null as Array? } + and { firstName isNotInCaseInsensitiveWhenPresent null as List? } } orderBy(id) } From 9612b18313922c10ba2508ec41088ef5d704c405 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 4 Nov 2025 13:15:27 -0500 Subject: [PATCH 53/68] Disable OpenTelemetry in the MySQL Container The newer Kotlin compiler versions contain a stripped-down implementation of OpenTelemetry that conflicts with the MySQL connector's expectations of what's available. Since this is just for testing, we don't need any OpenTelemetry support, so just turn it off. --- src/test/java/examples/mysql/MySQLTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/examples/mysql/MySQLTest.java b/src/test/java/examples/mysql/MySQLTest.java index f1eb82682..e9fec57db 100644 --- a/src/test/java/examples/mysql/MySQLTest.java +++ b/src/test/java/examples/mysql/MySQLTest.java @@ -48,6 +48,7 @@ class MySQLTest { @Container private static final MySQLContainer mysql = new MySQLContainer(TestContainersConfiguration.MYSQL_LATEST) + .withUrlParam("openTelemetry", "DISABLED") .withInitScript("examples/mariadb/CreateDB.sql"); private SqlSessionFactory sqlSessionFactory; From b22ef5abafbe23f0e057d15a305c9589230d605c Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 4 Nov 2025 13:15:53 -0500 Subject: [PATCH 54/68] Update Kotlin Complier version --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c182f9ed2..646e33fb4 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ org.mybatis.dynamic.sql - 2.1.21 + 2.2.21 17 2.0 2.0 @@ -95,7 +95,7 @@ org.jetbrains.kotlin - kotlin-stdlib-jdk8 + kotlin-stdlib ${kotlin.version} provided true From 176ebf212e63e40bdcc2470642f473b012ad1423 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 4 Nov 2025 13:25:36 -0500 Subject: [PATCH 55/68] Remove another array-based function --- .../dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt | 4 ---- .../examples/kotlin/spring/canonical/InfixElementsTest.kt | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt index 9a2070ea3..a83530154 100644 --- a/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt +++ b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt @@ -338,10 +338,6 @@ open class GroupingCriteriaCollector : SubCriteriaCollector() { fun BindableColumn.isNotInWhenPresent(vararg values: T?) = isNotInWhenPresent(values.asList()) - @JvmName("isNotInArrayWhenPresent") - infix fun BindableColumn.isNotInWhenPresent(values: Array?) = - invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInWhenPresent(values?.asList())) - infix fun BindableColumn.isNotInWhenPresent(values: Collection?) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInWhenPresent(values)) diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt index b6968648b..cd3df1943 100644 --- a/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt +++ b/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt @@ -1353,7 +1353,7 @@ open class InfixElementsTest { fun search(vararg names: String?) { val selectStatement = select(firstName) { from(person) - where { firstName isNotInWhenPresent names } + where { firstName isNotInWhenPresent names.asList() } orderBy(id) } @@ -1375,7 +1375,7 @@ open class InfixElementsTest { from(person) where { id isLessThan 10 - and { firstName isNotInWhenPresent null as Array? } + and { firstName isNotInWhenPresent null as List? } } orderBy(id) } From ef7ac184c7f2f7198f5b13141a7a203b5fb36789 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:13:24 +0000 Subject: [PATCH 56/68] Update dependency ch.qos.logback:logback-classic to v1.5.21 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 646e33fb4..d2217e5d5 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ ch.qos.logback logback-classic - 1.5.20 + 1.5.21 test From 087b1cf6f0b742e8deb285bd372fd5bf48236049 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 10:59:01 +0000 Subject: [PATCH 57/68] Update dependency org.springframework:spring-jdbc to v7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2217e5d5..4beb685a5 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.springframework spring-jdbc - 6.2.12 + 7.0.0 provided true From 7e1cc9fe3cb4c5d115e342f9055949f1a367a3af Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 21:01:10 +0000 Subject: [PATCH 58/68] Update testcontainers-java monorepo to v2.0.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2217e5d5..92bc211e4 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ http://localhost:9000 official - 2.0.1 + 2.0.2 org.mybatis.dynamic.sql.*;version=${project.version};-noimport:=true From e0d6720b92b32f433679345c2c6c44d2ca6a1a40 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 10:14:13 +0000 Subject: [PATCH 59/68] Update dependency org.springframework:spring-jdbc to v7.0.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f61f3ceb4..5bd19e966 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.springframework spring-jdbc - 7.0.0 + 7.0.1 provided true From 4ba77fa24efc2cdd67e9e0493e37cdeba7da3104 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 17:06:35 +0000 Subject: [PATCH 60/68] Update actions/checkout action to v6 --- .github/workflows/ci.yaml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/coveralls.yaml | 2 +- .github/workflows/site.yaml | 2 +- .github/workflows/sonar.yaml | 2 +- .github/workflows/sonatype.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0261021ce..da63dfb28 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: name: Test JDK ${{ matrix.java }}, ${{ matrix.os }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK ${{ matrix.java }} ${{ matrix.distribution }} uses: actions/setup-java@v5 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1d481b2df..0016ded97 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Java uses: actions/setup-java@v5 diff --git a/.github/workflows/coveralls.yaml b/.github/workflows/coveralls.yaml index 09ab9b4a7..de596cfd9 100644 --- a/.github/workflows/coveralls.yaml +++ b/.github/workflows/coveralls.yaml @@ -9,7 +9,7 @@ jobs: if: github.repository_owner == 'mybatis' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK uses: actions/setup-java@v5 with: diff --git a/.github/workflows/site.yaml b/.github/workflows/site.yaml index 17572ee8e..30e5957be 100644 --- a/.github/workflows/site.yaml +++ b/.github/workflows/site.yaml @@ -13,7 +13,7 @@ jobs: if: github.repository_owner == 'mybatis' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK uses: actions/setup-java@v5 with: diff --git a/.github/workflows/sonar.yaml b/.github/workflows/sonar.yaml index a43dd7198..ed0d72515 100644 --- a/.github/workflows/sonar.yaml +++ b/.github/workflows/sonar.yaml @@ -12,7 +12,7 @@ jobs: if: github.repository_owner == 'mybatis' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: # Disabling shallow clone is recommended for improving relevancy of reporting fetch-depth: 0 diff --git a/.github/workflows/sonatype.yaml b/.github/workflows/sonatype.yaml index 9f47d2039..57788412f 100644 --- a/.github/workflows/sonatype.yaml +++ b/.github/workflows/sonatype.yaml @@ -12,7 +12,7 @@ jobs: if: github.repository_owner == 'mybatis' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK uses: actions/setup-java@v5 with: From eddd368c7359042bff53a025b202bbb19d3fd256 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Sun, 30 Nov 2025 13:57:40 -0500 Subject: [PATCH 61/68] Add JSpecify Configuration --- .../springbatch/bulkinsert/package-info.java | 19 +++++++++++++++++++ .../springbatch/common/package-info.java | 19 +++++++++++++++++++ .../springbatch/cursor/package-info.java | 19 +++++++++++++++++++ .../springbatch/paging/package-info.java | 19 +++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 src/test/java/examples/springbatch/bulkinsert/package-info.java create mode 100644 src/test/java/examples/springbatch/common/package-info.java create mode 100644 src/test/java/examples/springbatch/cursor/package-info.java create mode 100644 src/test/java/examples/springbatch/paging/package-info.java diff --git a/src/test/java/examples/springbatch/bulkinsert/package-info.java b/src/test/java/examples/springbatch/bulkinsert/package-info.java new file mode 100644 index 000000000..da485b2da --- /dev/null +++ b/src/test/java/examples/springbatch/bulkinsert/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.springbatch.bulkinsert; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/springbatch/common/package-info.java b/src/test/java/examples/springbatch/common/package-info.java new file mode 100644 index 000000000..cc439710d --- /dev/null +++ b/src/test/java/examples/springbatch/common/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.springbatch.common; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/springbatch/cursor/package-info.java b/src/test/java/examples/springbatch/cursor/package-info.java new file mode 100644 index 000000000..e6a1089c4 --- /dev/null +++ b/src/test/java/examples/springbatch/cursor/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.springbatch.cursor; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/examples/springbatch/paging/package-info.java b/src/test/java/examples/springbatch/paging/package-info.java new file mode 100644 index 000000000..4450729a1 --- /dev/null +++ b/src/test/java/examples/springbatch/paging/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2016-2025 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. + */ +@NullMarked +package examples.springbatch.paging; + +import org.jspecify.annotations.NullMarked; From 8be91b25e2081e1ca650cc01f826e7a19ea3f0fe Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Sun, 30 Nov 2025 16:01:48 -0500 Subject: [PATCH 62/68] Updates for SpringBatch v6 --- pom.xml | 4 +- .../bulkinsert/BulkInsertConfiguration.java | 26 ++++++------- .../bulkinsert/SpringBatchBulkInsertTest.java | 15 ++++--- .../bulkinsert/TestRecordGenerator.java | 18 ++++----- .../springbatch/common/PersonProcessor.java | 16 +++----- .../springbatch/common/PersonRecord.java | 39 +------------------ .../common/UpdateStatementConvertor.java | 8 ++-- .../CursorReaderBatchConfiguration.java | 26 ++++++------- .../cursor/SpringBatchCursorTest.java | 15 ++++--- .../springbatch/mapper/PersonMapper.java | 11 +++--- .../PagingReaderBatchConfiguration.java | 21 +++++----- .../paging/SpringBatchPagingTest.java | 15 ++++--- 12 files changed, 84 insertions(+), 130 deletions(-) diff --git a/pom.xml b/pom.xml index 5bd19e966..317cfc5dc 100644 --- a/pom.xml +++ b/pom.xml @@ -61,7 +61,7 @@ 17 17 6.0.1 - 5.2.4 + 6.0.0 checkstyle-override.xml @@ -142,7 +142,7 @@ org.mybatis mybatis-spring - 3.0.5 + 4.0.0 test diff --git a/src/test/java/examples/springbatch/bulkinsert/BulkInsertConfiguration.java b/src/test/java/examples/springbatch/bulkinsert/BulkInsertConfiguration.java index 60278b02e..81282386a 100644 --- a/src/test/java/examples/springbatch/bulkinsert/BulkInsertConfiguration.java +++ b/src/test/java/examples/springbatch/bulkinsert/BulkInsertConfiguration.java @@ -21,21 +21,26 @@ import javax.sql.DataSource; +import java.util.Objects; + +import examples.springbatch.common.PersonRecord; +import examples.springbatch.mapper.PersonDynamicSqlSupport; +import examples.springbatch.mapper.PersonMapper; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.dynamic.sql.insert.InsertDSL; import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.batch.MyBatisBatchItemWriter; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.job.Job; import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.core.job.parameters.RunIdIncrementer; import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.Step; import org.springframework.batch.core.step.builder.StepBuilder; -import org.springframework.batch.item.ItemProcessor; -import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.infrastructure.item.ItemProcessor; +import org.springframework.batch.infrastructure.item.ItemWriter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -45,10 +50,6 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.transaction.PlatformTransactionManager; -import examples.springbatch.common.PersonRecord; -import examples.springbatch.mapper.PersonDynamicSqlSupport; -import examples.springbatch.mapper.PersonMapper; - @EnableBatchProcessing @Configuration @ComponentScan("examples.springbatch.bulkinsert") @@ -59,9 +60,6 @@ public class BulkInsertConfiguration { @Autowired private JobRepository jobRepository; - @Autowired - private PlatformTransactionManager transactionManager; - @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() @@ -76,7 +74,7 @@ public DataSource dataSource() { public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); - return sessionFactory.getObject(); + return Objects.requireNonNull(sessionFactory.getObject()); } @Bean @@ -104,7 +102,7 @@ public MyBatisBatchItemWriter writer(SqlSessionFactory sqlSessionF @Bean public Step step1(ItemProcessor processor, ItemWriter writer) { return new StepBuilder("step1", jobRepository) - .chunk(10, transactionManager) + .chunk(10) .reader(new TestRecordGenerator()) .processor(processor) .writer(writer) diff --git a/src/test/java/examples/springbatch/bulkinsert/SpringBatchBulkInsertTest.java b/src/test/java/examples/springbatch/bulkinsert/SpringBatchBulkInsertTest.java index a5cf3e2e3..b8d080edf 100644 --- a/src/test/java/examples/springbatch/bulkinsert/SpringBatchBulkInsertTest.java +++ b/src/test/java/examples/springbatch/bulkinsert/SpringBatchBulkInsertTest.java @@ -18,6 +18,7 @@ import static examples.springbatch.mapper.PersonDynamicSqlSupport.*; import static org.assertj.core.api.Assertions.assertThat; +import examples.springbatch.mapper.PersonMapper; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.jupiter.api.Test; @@ -25,22 +26,20 @@ import org.mybatis.dynamic.sql.select.CountDSL; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.springframework.batch.core.ExitStatus; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.StepExecution; -import org.springframework.batch.item.ExecutionContext; -import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.batch.core.job.JobExecution; +import org.springframework.batch.core.step.StepExecution; +import org.springframework.batch.infrastructure.item.ExecutionContext; +import org.springframework.batch.test.JobOperatorTestUtils; import org.springframework.batch.test.context.SpringBatchTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import examples.springbatch.mapper.PersonMapper; - @SpringBatchTest @SpringJUnitConfig(classes = BulkInsertConfiguration.class) class SpringBatchBulkInsertTest { @Autowired - private JobLauncherTestUtils jobLauncherTestUtils; + private JobOperatorTestUtils jobOperatorTestUtils; @Autowired private SqlSessionFactory sqlSessionFactory; @@ -50,7 +49,7 @@ void testThatRowsAreInserted() throws Exception { // starting condition assertThat(rowCount()).isZero(); - JobExecution execution = jobLauncherTestUtils.launchJob(); + JobExecution execution = jobOperatorTestUtils.startJob(); assertThat(execution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); assertThat(numberOfRowsProcessed(execution)).isEqualTo(TestRecordGenerator.recordCount()); diff --git a/src/test/java/examples/springbatch/bulkinsert/TestRecordGenerator.java b/src/test/java/examples/springbatch/bulkinsert/TestRecordGenerator.java index 023e7ee4d..f9dd19447 100644 --- a/src/test/java/examples/springbatch/bulkinsert/TestRecordGenerator.java +++ b/src/test/java/examples/springbatch/bulkinsert/TestRecordGenerator.java @@ -15,25 +15,25 @@ */ package examples.springbatch.bulkinsert; -import org.springframework.batch.item.ItemReader; - import examples.springbatch.common.PersonRecord; +import org.jspecify.annotations.Nullable; +import org.springframework.batch.infrastructure.item.ItemReader; public class TestRecordGenerator implements ItemReader { private int index = 0; private static final PersonRecord[] testRecords = { - new PersonRecord("Fred", "Flintstone"), - new PersonRecord("Wilma", "Flintstone"), - new PersonRecord("Pebbles", "Flintstone"), - new PersonRecord("Barney", "Rubble"), - new PersonRecord("Betty", "Rubble"), - new PersonRecord("Bamm Bamm", "Rubble") + new PersonRecord(null, "Fred", "Flintstone"), + new PersonRecord(null, "Wilma", "Flintstone"), + new PersonRecord(null, "Pebbles", "Flintstone"), + new PersonRecord(null, "Barney", "Rubble"), + new PersonRecord(null, "Betty", "Rubble"), + new PersonRecord(null, "Bamm Bamm", "Rubble") }; @Override - public PersonRecord read() { + public @Nullable PersonRecord read() { if (index < testRecords.length) { return (testRecords[index++]); } else { diff --git a/src/test/java/examples/springbatch/common/PersonProcessor.java b/src/test/java/examples/springbatch/common/PersonProcessor.java index 2a4939cdb..3dbd0b796 100644 --- a/src/test/java/examples/springbatch/common/PersonProcessor.java +++ b/src/test/java/examples/springbatch/common/PersonProcessor.java @@ -15,12 +15,12 @@ */ package examples.springbatch.common; -import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.annotation.BeforeChunk; import org.springframework.batch.core.annotation.BeforeStep; -import org.springframework.batch.core.scope.context.ChunkContext; -import org.springframework.batch.item.ExecutionContext; -import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.core.step.StepExecution; +import org.springframework.batch.infrastructure.item.Chunk; +import org.springframework.batch.infrastructure.item.ExecutionContext; +import org.springframework.batch.infrastructure.item.ItemProcessor; import org.springframework.stereotype.Component; @Component @@ -32,11 +32,7 @@ public class PersonProcessor implements ItemProcessor chunk) { incrementChunkCount(); } diff --git a/src/test/java/examples/springbatch/common/PersonRecord.java b/src/test/java/examples/springbatch/common/PersonRecord.java index edf974f53..24a83de5b 100644 --- a/src/test/java/examples/springbatch/common/PersonRecord.java +++ b/src/test/java/examples/springbatch/common/PersonRecord.java @@ -15,41 +15,6 @@ */ package examples.springbatch.common; -public class PersonRecord { - private Integer id; - private String firstName; - private String lastName; +import org.jspecify.annotations.Nullable; - public PersonRecord() { - super(); - } - - public PersonRecord(String firstName, String lastName) { - this.firstName = firstName; - this.lastName = lastName; - } - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } -} +public record PersonRecord(@Nullable Integer id, String firstName, String lastName) {} diff --git a/src/test/java/examples/springbatch/common/UpdateStatementConvertor.java b/src/test/java/examples/springbatch/common/UpdateStatementConvertor.java index 089486b71..3dc8691ea 100644 --- a/src/test/java/examples/springbatch/common/UpdateStatementConvertor.java +++ b/src/test/java/examples/springbatch/common/UpdateStatementConvertor.java @@ -18,6 +18,8 @@ import static examples.springbatch.mapper.PersonDynamicSqlSupport.*; import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo; +import java.util.Objects; + import org.mybatis.dynamic.sql.render.RenderingStrategies; import org.mybatis.dynamic.sql.update.UpdateDSL; import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider; @@ -30,9 +32,9 @@ public class UpdateStatementConvertor implements Converter writer(SqlSessionFactory sqlSessionF @Bean public Step step1(ItemReader reader, ItemProcessor processor, ItemWriter writer) { return new StepBuilder("step1", jobRepository) - .chunk(10, transactionManager) + .chunk(10) .reader(reader) .processor(processor) .writer(writer) diff --git a/src/test/java/examples/springbatch/cursor/SpringBatchCursorTest.java b/src/test/java/examples/springbatch/cursor/SpringBatchCursorTest.java index 747df2db0..7499aaf8a 100644 --- a/src/test/java/examples/springbatch/cursor/SpringBatchCursorTest.java +++ b/src/test/java/examples/springbatch/cursor/SpringBatchCursorTest.java @@ -20,6 +20,7 @@ import static org.mybatis.dynamic.sql.SqlBuilder.count; import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo; +import examples.springbatch.mapper.PersonMapper; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.jupiter.api.Test; @@ -27,22 +28,20 @@ import org.mybatis.dynamic.sql.select.SelectDSL; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.springframework.batch.core.ExitStatus; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.StepExecution; -import org.springframework.batch.item.ExecutionContext; -import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.batch.core.job.JobExecution; +import org.springframework.batch.core.step.StepExecution; +import org.springframework.batch.infrastructure.item.ExecutionContext; +import org.springframework.batch.test.JobOperatorTestUtils; import org.springframework.batch.test.context.SpringBatchTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import examples.springbatch.mapper.PersonMapper; - @SpringBatchTest @SpringJUnitConfig(classes = CursorReaderBatchConfiguration.class) class SpringBatchCursorTest { @Autowired - private JobLauncherTestUtils jobLauncherTestUtils; + private JobOperatorTestUtils jobOperatorTestUtils; @Autowired private SqlSessionFactory sqlSessionFactory; @@ -52,7 +51,7 @@ void testThatRowsAreTransformedToUpperCase() throws Exception { // starting condition assertThat(upperCaseRowCount()).isZero(); - JobExecution execution = jobLauncherTestUtils.launchJob(); + JobExecution execution = jobOperatorTestUtils.startJob(); assertThat(execution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); assertThat(numberOfRowsProcessed(execution)).isEqualTo(2); diff --git a/src/test/java/examples/springbatch/mapper/PersonMapper.java b/src/test/java/examples/springbatch/mapper/PersonMapper.java index a1db34626..94831f8fb 100644 --- a/src/test/java/examples/springbatch/mapper/PersonMapper.java +++ b/src/test/java/examples/springbatch/mapper/PersonMapper.java @@ -18,22 +18,21 @@ import java.util.List; import java.util.Map; +import examples.springbatch.common.PersonRecord; +import org.apache.ibatis.annotations.Arg; import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.SelectProvider; import org.mybatis.dynamic.sql.util.mybatis3.CommonCountMapper; import org.mybatis.dynamic.sql.util.mybatis3.CommonInsertMapper; import org.mybatis.dynamic.sql.util.mybatis3.CommonUpdateMapper; import org.mybatis.dynamic.sql.util.springbatch.SpringBatchProviderAdapter; -import examples.springbatch.common.PersonRecord; - @Mapper public interface PersonMapper extends CommonCountMapper, CommonInsertMapper, CommonUpdateMapper { @SelectProvider(type=SpringBatchProviderAdapter.class, method="select") - @Result(column="id", property="id", id=true) - @Result(column="first_name", property="firstName") - @Result(column="last_name", property="lastName") + @Arg(column = "id", javaType = Integer.class, id = true) + @Arg(column = "first_name", javaType = String.class) + @Arg(column = "last_name", javaType = String.class) List selectMany(Map parameterValues); } diff --git a/src/test/java/examples/springbatch/paging/PagingReaderBatchConfiguration.java b/src/test/java/examples/springbatch/paging/PagingReaderBatchConfiguration.java index 1da98bb9f..d87a86089 100644 --- a/src/test/java/examples/springbatch/paging/PagingReaderBatchConfiguration.java +++ b/src/test/java/examples/springbatch/paging/PagingReaderBatchConfiguration.java @@ -29,16 +29,16 @@ import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.batch.MyBatisBatchItemWriter; import org.mybatis.spring.batch.MyBatisPagingItemReader; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.job.Job; import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.launch.support.RunIdIncrementer; +import org.springframework.batch.core.job.parameters.RunIdIncrementer; import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.Step; import org.springframework.batch.core.step.builder.StepBuilder; -import org.springframework.batch.item.ItemProcessor; -import org.springframework.batch.item.ItemReader; -import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.infrastructure.item.ItemProcessor; +import org.springframework.batch.infrastructure.item.ItemReader; +import org.springframework.batch.infrastructure.item.ItemWriter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -49,6 +49,8 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.transaction.PlatformTransactionManager; +import java.util.Objects; + import examples.springbatch.common.PersonRecord; import examples.springbatch.mapper.PersonMapper; @@ -61,9 +63,6 @@ public class PagingReaderBatchConfiguration { @Autowired private JobRepository jobRepository; - @Autowired - private PlatformTransactionManager transactionManager; - @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder() @@ -79,7 +78,7 @@ public DataSource dataSource() { public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); - return sessionFactory.getObject(); + return Objects.requireNonNull(sessionFactory.getObject()); } @Bean @@ -119,7 +118,7 @@ public MyBatisBatchItemWriter writer(SqlSessionFactory sqlSessionF @Bean public Step step1(ItemReader reader, ItemProcessor processor, ItemWriter writer) { return new StepBuilder("step1", jobRepository) - .chunk(7, transactionManager) + .chunk(7) .reader(reader) .processor(processor) .writer(writer) diff --git a/src/test/java/examples/springbatch/paging/SpringBatchPagingTest.java b/src/test/java/examples/springbatch/paging/SpringBatchPagingTest.java index c153c8078..a07ea4de2 100644 --- a/src/test/java/examples/springbatch/paging/SpringBatchPagingTest.java +++ b/src/test/java/examples/springbatch/paging/SpringBatchPagingTest.java @@ -20,6 +20,7 @@ import static org.mybatis.dynamic.sql.SqlBuilder.count; import static org.mybatis.dynamic.sql.SqlBuilder.isEqualTo; +import examples.springbatch.mapper.PersonMapper; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.junit.jupiter.api.Test; @@ -27,22 +28,20 @@ import org.mybatis.dynamic.sql.select.SelectDSL; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; import org.springframework.batch.core.ExitStatus; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.StepExecution; -import org.springframework.batch.item.ExecutionContext; -import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.batch.core.job.JobExecution; +import org.springframework.batch.core.step.StepExecution; +import org.springframework.batch.infrastructure.item.ExecutionContext; +import org.springframework.batch.test.JobOperatorTestUtils; import org.springframework.batch.test.context.SpringBatchTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import examples.springbatch.mapper.PersonMapper; - @SpringBatchTest @SpringJUnitConfig(classes = PagingReaderBatchConfiguration.class) class SpringBatchPagingTest { @Autowired - private JobLauncherTestUtils jobLauncherTestUtils; + private JobOperatorTestUtils jobOperatorTestUtils; @Autowired private SqlSessionFactory sqlSessionFactory; @@ -52,7 +51,7 @@ void testThatRowsAreTransformedToUpperCase() throws Exception { // starting condition assertThat(upperCaseRowCount()).isZero(); - JobExecution execution = jobLauncherTestUtils.launchJob(); + JobExecution execution = jobOperatorTestUtils.startJob(); assertThat(execution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); assertThat(numberOfChunks(execution)).isEqualTo(14); assertThat(numberOfRowsProcessed(execution)).isEqualTo(93); From 393ed8cf78ffeb0d366f9345e1469de8fe7d1e6f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 20:47:18 +0000 Subject: [PATCH 63/68] Update dependency ch.qos.logback:logback-classic to v1.5.22 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 317cfc5dc..44387ac41 100644 --- a/pom.xml +++ b/pom.xml @@ -172,7 +172,7 @@ ch.qos.logback logback-classic - 1.5.21 + 1.5.22 test From f4d491670e6cd1e1a5891b8730eeba56ab085cd4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 20:47:22 +0000 Subject: [PATCH 64/68] Update dependency org.springframework:spring-jdbc to v7.0.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 317cfc5dc..6c99b0a87 100644 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org.springframework spring-jdbc - 7.0.1 + 7.0.2 provided true From 47f18a9f0ec60d1dad6ac12c94285855a8c69ff2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 02:12:36 +0000 Subject: [PATCH 65/68] Update testcontainers-java monorepo to v2.0.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e68382110..156d99321 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ http://localhost:9000 official - 2.0.2 + 2.0.3 org.mybatis.dynamic.sql.*;version=${project.version};-noimport:=true From 4cbb21a2cf6f9383f274a1e1f1ce4c122f6a1bdc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:02:28 +0000 Subject: [PATCH 66/68] Update kotlin monorepo to v2.3.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e68382110..d3da0702d 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,7 @@ org.mybatis.dynamic.sql - 2.2.21 + 2.3.0 17 2.0 2.0 From afed235b26ac54cd05992b798087210d574603ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 18:38:19 +0000 Subject: [PATCH 67/68] Update dependency org.mariadb.jdbc:mariadb-java-client to v3.5.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 156d99321..18a05233f 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.mariadb.jdbc mariadb-java-client - 3.5.6 + 3.5.7 test From 29eff097dda1a3ff4841d26fa73fd975fd4a0714 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 23:53:29 +0000 Subject: [PATCH 68/68] Update dependency maven to v3.9.12 --- .mvn/wrapper/maven-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 7bb288288..3b6c5d496 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,4 +1,4 @@ wrapperVersion=3.3.4 distributionType=source -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar