diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 66bba3618..da63dfb28 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,9 +18,9 @@ jobs: name: Test JDK ${{ matrix.java }}, ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - 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 8e5c9c4b6..0016ded97 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -25,25 +25,25 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: cache: maven distribution: 'temurin' 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 }}" diff --git a/.github/workflows/coveralls.yaml b/.github/workflows/coveralls.yaml index b4aaaa991..de596cfd9 100644 --- a/.github/workflows/coveralls.yaml +++ b/.github/workflows/coveralls.yaml @@ -9,9 +9,9 @@ jobs: if: github.repository_owner == 'mybatis' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - 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 de1babe41..30e5957be 100644 --- a/.github/workflows/site.yaml +++ b/.github/workflows/site.yaml @@ -13,9 +13,9 @@ 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@v6 - 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 33c70609c..ed0d72515 100644 --- a/.github/workflows/sonar.yaml +++ b/.github/workflows/sonar.yaml @@ -12,12 +12,12 @@ jobs: if: github.repository_owner == 'mybatis' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: # 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 c922f382c..57788412f 100644 --- a/.github/workflows/sonatype.yaml +++ b/.github/workflows/sonatype.yaml @@ -12,9 +12,9 @@ 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@v6 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: cache: maven distribution: temurin 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 0d35f6dbb..3b6c5d496 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.10/apache-maven-3.9.10-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar +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 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/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 ... diff --git a/pom.xml b/pom.xml index 9e41feb33..b3c3b4a6b 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ org.mybatis mybatis-parent - 50 + 51 org.mybatis.dynamic-sql @@ -60,8 +60,8 @@ 17 17 17 - 5.13.3 - 5.2.2 + 6.0.1 + 6.0.0 checkstyle-override.xml @@ -69,7 +69,7 @@ org.mybatis.dynamic.sql - 2.1.21 + 2.3.0 17 2.0 2.0 @@ -80,7 +80,7 @@ http://localhost:9000 official - 1.21.3 + 2.0.3 org.mybatis.dynamic.sql.*;version=${project.version};-noimport:=true @@ -95,7 +95,7 @@ org.jetbrains.kotlin - kotlin-stdlib-jdk8 + kotlin-stdlib ${kotlin.version} provided true @@ -103,7 +103,7 @@ org.springframework spring-jdbc - 6.2.8 + 7.0.2 provided true @@ -136,13 +136,13 @@ org.assertj assertj-core - 3.27.3 + 3.27.6 test org.mybatis mybatis-spring - 3.0.5 + 4.0.0 test @@ -172,49 +172,49 @@ ch.qos.logback logback-classic - 1.5.18 + 1.5.22 test org.testcontainers - junit-jupiter + testcontainers-junit-jupiter ${test.containers.version} test org.testcontainers - postgresql + testcontainers-postgresql ${test.containers.version} test org.postgresql postgresql - 42.7.7 + 42.7.8 test org.testcontainers - mariadb + testcontainers-mariadb ${test.containers.version} test org.mariadb.jdbc mariadb-java-client - 3.5.4 + 3.5.7 test org.testcontainers - mysql + testcontainers-mysql ${test.containers.version} test com.mysql mysql-connector-j - 9.3.0 + 9.5.0 test 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/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index 4c841d86b..d8f9dc56e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -25,6 +25,77 @@ 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 {@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 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} + * 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. 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 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. + *
  8. + *
  9. 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()}
    • + *
    • {@link SqlColumn#descending()}
    • + *
    • {@link SqlColumn#qualifiedWith(String)}
    • + *
    + *
  10. + *
  11. 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 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)}
    • + *
    • {@link SqlColumn#withTypeHandler(String)}
    • + *
    • {@link SqlColumn#withJavaType(Class)}
    • + *
    • {@link SqlColumn#withParameterTypeConverter(ParameterTypeConverter)}
    • + *
    + *
  12. + *
+ * + *

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 fully executed extension of this class. + * + * @param the Java type associated with the column + */ public class SqlColumn implements BindableColumn, SortSpecification { protected final String name; @@ -37,8 +108,9 @@ 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) { + protected SqlColumn(AbstractBuilder builder) { name = Objects.requireNonNull(builder.name); table = Objects.requireNonNull(builder.table); jdbcType = builder.jdbcType; @@ -49,6 +121,7 @@ private SqlColumn(Builder builder) { parameterTypeConverter = Objects.requireNonNull(builder.parameterTypeConverter); tableQualifier = builder.tableQualifier; javaType = builder.javaType; + javaProperty = builder.javaProperty; } public String name() { @@ -79,21 +152,36 @@ 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); } + /** + * 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 SortSpecification descending() { - Builder b = copy(); - return b.withDescendingPhrase(" DESC").build(); //$NON-NLS-1$ + 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) { - Builder b = copy(); - return b.withAlias(alias).build(); + return copyBuilder().withAlias(alias).build(); } /** @@ -104,25 +192,24 @@ 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(); } /** - * 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. * * @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) + "\"") //$NON-NLS-1$ //$NON-NLS-2$ + .build(); } @Override @@ -144,38 +231,126 @@ 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 + * @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) { - Builder b = copy(); - return b.withTypeHandler(typeHandler).build(); + 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 + * @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) { - Builder b = copy(); - return b.withRenderingStrategy(renderingStrategy).build(); + 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 + * @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) { - Builder b = copy(); - return b.withParameterTypeConverter(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 + * @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) { - Builder b = copy(); - return b.withJavaType(javaType).build(); + 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 + * @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()); + } + + /** + * 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<>()); + } + + @SuppressWarnings("unchecked") + protected > S cast(SqlColumn column) { + return (S) 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 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) @@ -183,9 +358,10 @@ 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); } public static SqlColumn of(String name, SqlTable table) { @@ -201,7 +377,7 @@ public static SqlColumn of(String name, SqlTable table, JDBCType jdbcType .build(); } - public static class Builder { + public abstract static class AbstractBuilder, B extends AbstractBuilder> { protected @Nullable String name; protected @Nullable SqlTable table; protected @Nullable JDBCType jdbcType; @@ -212,59 +388,77 @@ 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) { + 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 B withJavaProperty(@Nullable String javaProperty) { + this.javaProperty = javaProperty; + return getThis(); } + protected abstract B getThis(); + + public abstract C build(); + } + + public static class Builder extends AbstractBuilder, Builder> { + @Override public SqlColumn build() { return new SqlColumn<>(this); } + + @Override + protected Builder getThis() { + return this; + } } } 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/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/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 ba2d2ffa5..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 @@ -18,6 +18,7 @@ import org.mybatis.dynamic.sql.SqlColumn; 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.MultiRowInsertMappingVisitor; import org.mybatis.dynamic.sql.util.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; @@ -69,6 +70,16 @@ public FieldAndValueAndParameters visit(RowMapping mapping) { .build(); } + @Override + public FieldAndValueAndParameters visit(MappedColumnMapping mapping) { + return FieldAndValueAndParameters.withFieldName(mapping.columnName()) + .withValuePhrase(calculateJdbcPlaceholder( + mapping.column(), + InsertRenderingUtilities.getMappedPropertyName(mapping.column())) + ) + .build(); + } + 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..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 @@ -21,6 +21,8 @@ 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.NullMapping; import org.mybatis.dynamic.sql.util.PropertyMapping; import org.mybatis.dynamic.sql.util.PropertyWhenPresentMapping; @@ -80,6 +82,25 @@ public Optional visit(RowMapping mapping) { .buildOptional(); } + @Override + public Optional visit(MappedColumnMapping mapping) { + return FieldAndValueAndParameters.withFieldName(mapping.columnName()) + .withValuePhrase(calculateJdbcPlaceholder( + mapping.column(), + InsertRenderingUtilities.getMappedPropertyName(mapping.column())) + ) + .buildOptional(); + } + + @Override + public Optional visit(MappedColumnWhenPresentMapping mapping) { + if (mapping.shouldRender()) { + return visit((MappedColumnMapping) mapping); + } else { + return Optional.empty(); + } + } + 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..268cae396 --- /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 java.util.Objects; +import java.util.function.Supplier; + +import org.mybatis.dynamic.sql.SqlColumn; + +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/java/org/mybatis/dynamic/sql/util/Utilities.java b/src/main/java/org/mybatis/dynamic/sql/util/Utilities.java index 0c3bd188f..b0c27081e 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,19 @@ */ 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 Stream filterNulls(Collection<@Nullable T> values) { + // this method helps IntelliJ understand intended nullability + return values.stream().filter(Objects::nonNull); + } } 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..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 @@ -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; @@ -27,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") @@ -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..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,14 +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.NonNull; 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, @@ -40,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 @@ -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..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 @@ -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).toList()); } @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..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 @@ -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; @@ -29,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") @@ -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..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,14 +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.NonNull; 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, @@ -40,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 @@ -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..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 @@ -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).toList()); } @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..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 @@ -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; @@ -29,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") @@ -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/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt b/src/main/kotlin/org/mybatis/dynamic/sql/util/kotlin/GroupingCriteriaCollector.kt index be4ba72af..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 @@ -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)) @@ -350,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)) - infix fun BindableColumn.isNotInWhenPresent(values: Collection?) = invoke(org.mybatis.dynamic.sql.util.kotlin.elements.isNotInWhenPresent(values)) @@ -410,40 +394,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/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/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/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/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/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/config/TestContainersConfiguration.java b/src/test/java/config/TestContainersConfiguration.java index 77fd866b0..f78bd5030 100644 --- a/src/test/java/config/TestContainersConfiguration.java +++ b/src/test/java/config/TestContainersConfiguration.java @@ -21,7 +21,7 @@ * 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"); + DockerImageName MYSQL_LATEST = DockerImageName.parse("mysql:9.5.0"); } 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/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/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/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/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/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); 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/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/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/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/mysql/MySQLTest.java b/src/test/java/examples/mysql/MySQLTest.java index f9c047f77..e9fec57db 100644 --- a/src/test/java/examples/mysql/MySQLTest.java +++ b/src/test/java/examples/mysql/MySQLTest.java @@ -38,17 +38,17 @@ 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) + .withUrlParam("openTelemetry", "DISABLED") .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/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/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/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/PersonDynamicSqlSupport.java b/src/test/java/examples/simple/PersonDynamicSqlSupport.java index 215f8a909..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,13 +32,27 @@ 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 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 = + 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/simple/PersonMapperTest.java b/src/test/java/examples/simple/PersonMapperTest.java index b02d52c4c..4843431f4 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()) { @@ -604,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); } } @@ -627,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); } } @@ -647,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); }); } } @@ -935,6 +955,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/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/examples/simple/PrimaryKeyColumn.java b/src/test/java/examples/simple/PrimaryKeyColumn.java new file mode 100644 index 000000000..30d51f72a --- /dev/null +++ b/src/test/java/examples/simple/PrimaryKeyColumn.java @@ -0,0 +1,107 @@ +/* + * 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; + +/** + * 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; + + private PrimaryKeyColumn(Builder builder) { + super(builder); + isPrimaryKeyColumn = builder.isPrimaryKeyColumn; + } + + public boolean isPrimaryKeyColumn() { + return isPrimaryKeyColumn; + } + + @Override + public PrimaryKeyColumn descending() { + return cast(super.descending()); + } + + @Override + public PrimaryKeyColumn as(String alias) { + return cast(super.as(alias)); + } + + @Override + public PrimaryKeyColumn qualifiedWith(String tableQualifier) { + return cast(super.qualifiedWith(tableQualifier)); + } + + @Override + public PrimaryKeyColumn asCamelCase() { + return cast(super.asCamelCase()); + } + + @Override + public PrimaryKeyColumn withTypeHandler(String typeHandler) { + return cast(super.withTypeHandler(typeHandler)); + } + + @Override + public PrimaryKeyColumn withRenderingStrategy(RenderingStrategy renderingStrategy) { + return cast(super.withRenderingStrategy(renderingStrategy)); + } + + @Override + public PrimaryKeyColumn withParameterTypeConverter(ParameterTypeConverter parameterTypeConverter) { + return cast(super.withParameterTypeConverter(parameterTypeConverter)); + } + + @Override + public PrimaryKeyColumn withJavaType(Class javaType) { + return cast(super.withJavaType(javaType)); + } + + @Override + public PrimaryKeyColumn withJavaProperty(String javaProperty) { + return cast(super.withJavaProperty(javaProperty)); + } + + @Override + protected Builder copyBuilder() { + return populateBaseBuilder(new Builder<>()).isPrimaryKeyColumn(isPrimaryKeyColumn); + } + + public static class Builder extends AbstractBuilder, Builder> { + private boolean isPrimaryKeyColumn; + + public Builder isPrimaryKeyColumn(boolean isPrimaryKeyColumn) { + this.isPrimaryKeyColumn = isPrimaryKeyColumn; + return this; + } + + @Override + public PrimaryKeyColumn build() { + return new PrimaryKeyColumn<>(this); + } + + @Override + protected Builder getThis() { + return this; + } + } +} 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(); + } } 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/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/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/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/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/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/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/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); 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; 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/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/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/NameRecord.java b/src/test/java/issues/gh324/NameRecord.java index a00286095..9b02e327d 100644 --- a/src/test/java/issues/gh324/NameRecord.java +++ b/src/test/java/issues/gh324/NameRecord.java @@ -17,23 +17,5 @@ import java.io.Serializable; -public class NameRecord implements Serializable { - private Integer id; - private String name; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public 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/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/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/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/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); } 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/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/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/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/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; } 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/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"; + } } } 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/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/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/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/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) 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/joins/DeprecatedJoinMapperTest.kt b/src/test/kotlin/examples/kotlin/mybatis3/joins/DeprecatedJoinMapperTest.kt index d712c46c6..4586f72d3 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"), @@ -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"), 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"), ) 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 } } 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 diff --git a/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt b/src/test/kotlin/examples/kotlin/spring/canonical/InfixElementsTest.kt index b1b9791f2..cd3df1943 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) } @@ -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) } @@ -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) } 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)) } }