diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 06b1c2163e..fa9e1d17ae 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,24 +2,28 @@ name: Java CI on: [workflow_dispatch, push, pull_request] +permissions: read-all + jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - java: [17, 21, 22-ea] - distribution: ['temurin'] + cache: [maven] + distribution: [temurin] + java: [17, 21, 24, 25-ea] + os: [macos-latest, ubuntu-latest, windows-latest] fail-fast: false max-parallel: 4 name: Test JDK ${{ matrix.java }}, ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - name: Set up JDK ${{ matrix.java }} ${{ matrix.distribution }} - uses: actions/setup-java@v3 + - uses: actions/checkout@v5 + - name: Setup Java ${{ matrix.java }} ${{ matrix.distribution }} + uses: actions/setup-java@v5 with: - java-version: ${{ matrix.java }} + cache: ${{ matrix.cache }} distribution: ${{ matrix.distribution }} + java-version: ${{ matrix.java }} - name: Test with Maven run: ./mvnw test -B -V --no-transfer-progress -D"license.skip=true" diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 0000000000..f620d67e18 --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,49 @@ +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + schedule: + - cron: '37 14 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ java-kotlin ] + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Java + uses: actions/setup-java@v5 + with: + cache: maven + distribution: 'temurin' + java-version: 21 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v4 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/coveralls.yaml b/.github/workflows/coveralls.yaml index 03df1ede11..54044db381 100644 --- a/.github/workflows/coveralls.yaml +++ b/.github/workflows/coveralls.yaml @@ -2,17 +2,20 @@ name: Coveralls on: [push, pull_request] +permissions: read-all + jobs: - build: + coveralls: if: github.repository_owner == 'mybatis' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up JDK - uses: actions/setup-java@v3 + - uses: actions/checkout@v5 + - name: Setup Java + uses: actions/setup-java@v5 with: + cache: maven + distribution: temurin java-version: 21 - distribution: zulu - name: Report Coverage to Coveralls for Pull Requests if: github.event_name == 'pull_request' run: ./mvnw -B -V test jacoco:report coveralls:report -q -Dlicense.skip=true -DrepoToken=$GITHUB_TOKEN -DserviceName=github -DpullRequest=$PR_NUMBER --no-transfer-progress diff --git a/.github/workflows/site.yaml b/.github/workflows/site.yaml new file mode 100644 index 0000000000..cfd7c23c72 --- /dev/null +++ b/.github/workflows/site.yaml @@ -0,0 +1,32 @@ +name: Site + +on: + push: + branches: + - site + +permissions: + contents: write + +jobs: + build: + if: github.repository_owner == 'mybatis' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Setup Java + uses: actions/setup-java@v5 + with: + cache: maven + distribution: temurin + java-version: 21 + - name: Build site + run: ./mvnw site site:stage -DskipTests -Dlicense.skip=true -B -V --no-transfer-progress --settings ./.mvn/settings.xml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NVD_API_KEY: ${{ secrets.NVD_API_KEY }} + - name: Deploy Site to gh-pages + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages + folder: target/staging diff --git a/.github/workflows/sonar.yaml b/.github/workflows/sonar.yaml index 2c986b2f84..25c0b88ec5 100644 --- a/.github/workflows/sonar.yaml +++ b/.github/workflows/sonar.yaml @@ -5,22 +5,25 @@ on: branches: - master +permissions: read-all + jobs: build: if: github.repository_owner == 'mybatis' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: # Disabling shallow clone is recommended for improving relevancy of reporting fetch-depth: 0 - - name: Set up JDK - uses: actions/setup-java@v3 + - name: Setup Java + uses: actions/setup-java@v5 with: + cache: maven + distribution: temurin java-version: 21 - distribution: zulu - name: Analyze with SonarCloud - run: ./mvnw verify jacoco:report sonar:sonar -B -Dsonar.projectKey=mybatis_spring -Dsonar.organization=mybatis -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=$SONAR_TOKEN -Dlicense.skip=true --no-transfer-progress + run: ./mvnw verify jacoco:report sonar:sonar -B -V -Dsonar.projectKey=mybatis_spring -Dsonar.organization=mybatis -Dsonar.host.url=https://sonarcloud.io -Dsonar.token=$SONAR_TOKEN -Dlicense.skip=true --no-transfer-progress -Dsonar.scanner.skipJreProvisioning=true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/sonatype.yaml b/.github/workflows/sonatype.yaml index 46800b489e..23bf66596a 100644 --- a/.github/workflows/sonatype.yaml +++ b/.github/workflows/sonatype.yaml @@ -5,17 +5,20 @@ on: branches: - master +permissions: read-all + jobs: build: if: github.repository_owner == 'mybatis' && ! contains(toJSON(github.event.head_commit.message), '[maven-release-plugin]') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up JDK - uses: actions/setup-java@v3 + - uses: actions/checkout@v5 + - name: Setup Java + uses: actions/setup-java@v5 with: + cache: maven + distribution: temurin java-version: 21 - distribution: zulu - name: Deploy to Sonatype run: ./mvnw deploy -DskipTests -B -V --no-transfer-progress --settings ./.mvn/settings.xml -Dlicense.skip=true env: diff --git a/.gitignore b/.gitignore index 001d888176..908d8660e8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ .mvn/wrapper/maven-wrapper.jar release.properties *.releaseBackup +.github/keys/ diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 5d2499ed32..b6c32576a7 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -1,7 +1,7 @@ + + - ossrh + central ${env.CI_DEPLOY_USERNAME} ${env.CI_DEPLOY_PASSWORD} + gh-pages-scm @@ -32,10 +35,18 @@ gh-pages + + github - ${env.CI_DEPLOY_USERNAME} ${env.GITHUB_TOKEN} + + + + nvd + ${env.NVD_API_KEY} + + diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java index f57fd86fbf..9439f285a5 100644 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -21,77 +21,71 @@ import java.io.InputStream; import java.net.Authenticator; import java.net.PasswordAuthentication; +import java.net.URI; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.util.concurrent.ThreadLocalRandom; -public final class MavenWrapperDownloader -{ - private static final String WRAPPER_VERSION = "3.2.0"; +public final class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "3.3.2"; - private static final boolean VERBOSE = Boolean.parseBoolean( System.getenv( "MVNW_VERBOSE" ) ); + private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); - public static void main( String[] args ) - { - log( "Apache Maven Wrapper Downloader " + WRAPPER_VERSION ); + public static void main(String[] args) { + log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); - if ( args.length != 2 ) - { - System.err.println( " - ERROR wrapperUrl or wrapperJarPath parameter missing" ); - System.exit( 1 ); + if (args.length != 2) { + System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); + System.exit(1); } - try - { - log( " - Downloader started" ); - final URL wrapperUrl = new URL( args[0] ); - final String jarPath = args[1].replace( "..", "" ); // Sanitize path - final Path wrapperJarPath = Paths.get( jarPath ).toAbsolutePath().normalize(); - downloadFileFromURL( wrapperUrl, wrapperJarPath ); - log( "Done" ); - } - catch ( IOException e ) - { - System.err.println( "- Error downloading: " + e.getMessage() ); - if ( VERBOSE ) - { + try { + log(" - Downloader started"); + final URL wrapperUrl = URI.create(args[0]).toURL(); + final String jarPath = args[1].replace("..", ""); // Sanitize path + final Path wrapperJarPath = Path.of(jarPath).toAbsolutePath().normalize(); + downloadFileFromURL(wrapperUrl, wrapperJarPath); + log("Done"); + } catch (IOException e) { + System.err.println("- Error downloading: " + e.getMessage()); + if (VERBOSE) { e.printStackTrace(); } - System.exit( 1 ); + System.exit(1); } } - private static void downloadFileFromURL( URL wrapperUrl, Path wrapperJarPath ) - throws IOException - { - log( " - Downloading to: " + wrapperJarPath ); - if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null ) - { - final String username = System.getenv( "MVNW_USERNAME" ); - final char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray(); - Authenticator.setDefault( new Authenticator() - { + private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) + throws IOException { + log(" - Downloading to: " + wrapperJarPath); + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + final String username = System.getenv("MVNW_USERNAME"); + final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { @Override - protected PasswordAuthentication getPasswordAuthentication() - { - return new PasswordAuthentication( username, password ); + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); } - } ); + }); } - try ( InputStream inStream = wrapperUrl.openStream() ) - { - Files.copy( inStream, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING ); + Path temp = wrapperJarPath + .getParent() + .resolve(wrapperJarPath.getFileName() + "." + + Long.toUnsignedString(ThreadLocalRandom.current().nextLong()) + ".tmp"); + try (InputStream inStream = wrapperUrl.openStream()) { + Files.copy(inStream, temp, StandardCopyOption.REPLACE_EXISTING); + Files.move(temp, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); + } finally { + Files.deleteIfExists(temp); } - log( " - Downloader complete" ); + log(" - Downloader complete"); } - private static void log( String msg ) - { - if ( VERBOSE ) - { - System.out.println( msg ); + private static void log(String msg) { + if (VERBOSE) { + System.out.println(msg); } } diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 39a584eb21..b06697c613 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -14,5 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar +wrapperVersion=3.3.2 +distributionType=source +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar diff --git a/LICENSE b/LICENSE index 57bc88a15a..7e835b2fa9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -192,7 +192,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + 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, diff --git a/README.md b/README.md index 00df4dc767..1108087d39 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Supported Versions ------------------ - master - Support for Spring 6 and Spring Batch 5 -- 2.1.x - Enhance and maintenance for Spring 5 and Spring Batch 4 +- 2.1.x - Maintenance for Spring 5 and Spring Batch 4 Essentials ---------- diff --git a/mvnw b/mvnw index 66df285428..6683888259 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.2.0 +# Apache Maven Wrapper startup batch script, version 3.3.2 # # Required ENV vars: # ------------------ @@ -33,75 +33,84 @@ # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- -if [ -z "$MAVEN_SKIP_RC" ] ; then +if [ -z "$MAVEN_SKIP_RC" ]; then - if [ -f /usr/local/etc/mavenrc ] ; then + if [ -f /usr/local/etc/mavenrc ]; then . /usr/local/etc/mavenrc fi - if [ -f /etc/mavenrc ] ; then + if [ -f /etc/mavenrc ]; then . /etc/mavenrc fi - if [ -f "$HOME/.mavenrc" ] ; then + if [ -f "$HOME/.mavenrc" ]; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; +cygwin=false +darwin=false mingw=false case "$(uname)" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME - else - JAVA_HOME="/Library/Java/Home"; export JAVA_HOME - fi +CYGWIN*) cygwin=true ;; +MINGW*) mingw=true ;; +Darwin*) + darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)" + export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home" + export JAVA_HOME fi - ;; + fi + ;; esac -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then +if [ -z "$JAVA_HOME" ]; then + if [ -r /etc/gentoo-release ]; then JAVA_HOME=$(java-config --jre-home) fi fi # For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=$(cygpath --unix "$JAVA_HOME") - [ -n "$CLASSPATH" ] && - CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +if $cygwin; then + [ -n "$JAVA_HOME" ] \ + && JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] \ + && CLASSPATH=$(cygpath --path --unix "$CLASSPATH") fi # For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && - JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +if $mingw; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \ + && JAVA_HOME="$( + cd "$JAVA_HOME" || ( + echo "cannot cd into $JAVA_HOME." >&2 + exit 1 + ) + pwd + )" fi if [ -z "$JAVA_HOME" ]; then javaExecutable="$(which javac)" - if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=$(which readlink) if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then - if $darwin ; then - javaHome="$(dirname "\"$javaExecutable\"")" - javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + if $darwin; then + javaHome="$(dirname "$javaExecutable")" + javaExecutable="$(cd "$javaHome" && pwd -P)/javac" else - javaExecutable="$(readlink -f "\"$javaExecutable\"")" + javaExecutable="$(readlink -f "$javaExecutable")" fi - javaHome="$(dirname "\"$javaExecutable\"")" + javaHome="$(dirname "$javaExecutable")" javaHome=$(expr "$javaHome" : '\(.*\)/bin') JAVA_HOME="$javaHome" export JAVA_HOME @@ -109,52 +118,60 @@ if [ -z "$JAVA_HOME" ]; then fi fi -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then +if [ -z "$JAVACMD" ]; then + if [ -n "$JAVA_HOME" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else - JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + JAVACMD="$( + \unset -f command 2>/dev/null + \command -v java + )" fi fi -if [ ! -x "$JAVACMD" ] ; then +if [ ! -x "$JAVACMD" ]; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." +if [ -z "$JAVA_HOME" ]; then + echo "Warning: JAVA_HOME environment variable is not set." >&2 fi # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" + if [ -z "$1" ]; then + echo "Path not specified to find_maven_basedir" >&2 return 1 fi basedir="$1" wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then + while [ "$wdir" != '/' ]; do + if [ -d "$wdir"/.mvn ]; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then - wdir=$(cd "$wdir/.." || exit 1; pwd) + wdir=$( + cd "$wdir/.." || exit 1 + pwd + ) fi # end of workaround done - printf '%s' "$(cd "$basedir" || exit 1; pwd)" + printf '%s' "$( + cd "$basedir" || exit 1 + pwd + )" } # concatenates all lines of a file @@ -165,7 +182,7 @@ concat_lines() { # enabled. Otherwise, we may read lines that are delimited with # \r\n and produce $'-Xarg\r' rather than -Xarg due to word # splitting rules. - tr -s '\r\n' ' ' < "$1" + tr -s '\r\n' ' ' <"$1" fi } @@ -177,10 +194,11 @@ log() { BASE_DIR=$(find_maven_basedir "$(dirname "$0")") if [ -z "$BASE_DIR" ]; then - exit 1; + exit 1 fi -MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +export MAVEN_PROJECTBASEDIR log "$MAVEN_PROJECTBASEDIR" ########################################################################################## @@ -189,63 +207,66 @@ log "$MAVEN_PROJECTBASEDIR" ########################################################################################## wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" if [ -r "$wrapperJarPath" ]; then - log "Found $wrapperJarPath" + log "Found $wrapperJarPath" else - log "Couldn't find $wrapperJarPath, downloading it ..." + log "Couldn't find $wrapperJarPath, downloading it ..." - if [ -n "$MVNW_REPOURL" ]; then - wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.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" + break + ;; + esac + done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget >/dev/null; then + 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" else - wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 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"; break ;; - esac - done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" - log "Downloading from: $wrapperUrl" - + 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" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac if $cygwin; then - wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") fi - - if command -v wget > /dev/null; then - 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" - else - wget $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" - else - curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" - fi - else - log "Falling back to using Java to download" - javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" - javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaSource=$(cygpath --path --windows "$javaSource") - javaClass=$(cygpath --path --windows "$javaClass") - fi - if [ -e "$javaSource" ]; then - if [ ! -e "$javaClass" ]; then - log " - Compiling MavenWrapperDownloader.java ..." - ("$JAVA_HOME/bin/javac" "$javaSource") - fi - if [ -e "$javaClass" ]; then - log " - Running MavenWrapperDownloader.java ..." - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" - fi - fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi fi + fi fi ########################################################################################## # End of extension @@ -254,22 +275,25 @@ fi # If specified, validate the SHA-256 sum of the Maven wrapper jar file wrapperSha256Sum="" while IFS="=" read -r key value; do - case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + case "$key" in wrapperSha256Sum) + wrapperSha256Sum=$value + break + ;; esac -done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +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 command -v sha256sum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then wrapperSha256Result=true fi - elif command -v shasum > /dev/null; then - if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + elif command -v shasum >/dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then wrapperSha256Result=true fi else - echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." - echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2 exit 1 fi if [ $wrapperSha256Result = false ]; then @@ -284,12 +308,12 @@ MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") - [ -n "$CLASSPATH" ] && - CLASSPATH=$(cygpath --path --windows "$CLASSPATH") - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") + [ -n "$JAVA_HOME" ] \ + && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] \ + && CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] \ + && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") fi # Provide a "standardized" way to retrieve the CLI args that will diff --git a/mvnw.cmd b/mvnw.cmd index 95ba6f54ac..da4fe4dd93 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -18,7 +18,7 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM Apache Maven Wrapper startup batch script, version 3.3.2 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @@ -59,22 +59,22 @@ set ERROR_CODE=0 @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome -echo. +echo. >&2 echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 -echo. +echo. >&2 goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init -echo. +echo. >&2 echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 -echo. +echo. >&2 goto error @REM ==== END VALIDATION ==== @@ -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.2.0/maven-wrapper-3.2.0.jar" +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.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.2.0/maven-wrapper-3.2.0.jar" + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar" ) if "%MVNW_VERBOSE%" == "true" ( echo Couldn't find %WRAPPER_JAR%, downloading it ... @@ -160,11 +160,12 @@ FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapp ) IF NOT %WRAPPER_SHA_256_SUM%=="" ( powershell -Command "&{"^ + "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ - " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ - " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ - " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ " exit 1;"^ "}"^ "}" diff --git a/pom.xml b/pom.xml index 93670a43fb..628f39e533 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ + 4.0.0 org.mybatis mybatis-parent - 40 + 51 + org.mybatis mybatis-spring - 3.0.3 - jar + 3.0.6-SNAPSHOT mybatis-spring An easy-to-use Spring bridge for MyBatis sql mapping framework. - http://www.mybatis.org/spring/ + https://www.mybatis.org/spring/ @@ -79,8 +80,8 @@ scm:git:ssh://git@github.com/mybatis/spring.git scm:git:ssh://git@github.com/mybatis/spring.git - mybatis-spring-3.0.3 - http://github.com/mybatis/spring + HEAD + https://github.com/mybatis/spring/ GitHub Issue Management @@ -108,17 +109,20 @@ 17 17 + 17 + 17 10.17.1.0 - 3.5.14 - 6.1.0 - 5.0.3 + 3.5.19 + 6.2.11 + 3.5.6 + 5.2.3 org.mybatis.spring - 5.10.1 + 6.0.0 - 1700476303 + 1749492577 true @@ -153,6 +157,14 @@ provided + + org.aspectj + aspectjweaver + 1.9.24 + compile + true + + @@ -179,7 +191,7 @@ org.jboss.byteman byteman-bmunit - 4.0.22 + 4.0.25 test @@ -217,31 +229,38 @@ + + org.springframework.boot + spring-boot-autoconfigure + ${spring-boot.version} + test + + org.hsqldb hsqldb - 2.7.2 + 2.7.4 test org.slf4j slf4j-simple - 2.0.9 + 2.0.17 test org.mockito mockito-core - 5.7.0 + 5.20.0 test org.assertj assertj-core - 3.24.2 + 3.27.6 test @@ -267,10 +286,6 @@ nekohtml nekohtml - - junit - junit - @@ -311,20 +326,20 @@ jakarta.servlet jakarta.servlet-api - 6.0.0 + 6.1.0 test net.bytebuddy byte-buddy - 1.14.9 + 1.17.8 test net.bytebuddy byte-buddy-agent - 1.14.9 + 1.17.8 test @@ -387,20 +402,12 @@ ${project.basedir}/src/main/resources - - - ${project.build.testSourceDirectory} - - **/*.java - - - org.apache.maven.plugins maven-surefire-plugin - + derby.stream.error.file ${project.build.directory}/derby.log @@ -409,7 +416,7 @@ com.atomikos.icatch.log_base_dir ${project.build.directory} - + @@ -427,7 +434,7 @@ ${project.build.directory}/site-src - src/site + ${project.basedir}/src/site true @@ -439,7 +446,7 @@ org.apache.maven.plugins maven-site-plugin - en,es,zh_CN,ja,ko + default,es,zh_CN,ja,ko ${project.build.directory}/site-src @@ -459,23 +466,14 @@ - - - pre16 - - (,16) - - - 10.15.2.0 - - + 17 [17,) - 10.16.1.1 + 10.16.1.1 @@ -484,7 +482,7 @@ [19,) - 10.17.1.0 + 10.17.1.0 diff --git a/renovate.json b/renovate.json index 39a2b6e9a5..5db72dd6a9 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base" + "config:recommended" ] } diff --git a/src/main/java/org/mybatis/spring/MyBatisExceptionTranslator.java b/src/main/java/org/mybatis/spring/MyBatisExceptionTranslator.java index 2fdc030060..cfcdefe202 100644 --- a/src/main/java/org/mybatis/spring/MyBatisExceptionTranslator.java +++ b/src/main/java/org/mybatis/spring/MyBatisExceptionTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -16,6 +16,7 @@ package org.mybatis.spring; import java.sql.SQLException; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import javax.sql.DataSource; @@ -41,6 +42,7 @@ public class MyBatisExceptionTranslator implements PersistenceExceptionTranslato private final Supplier exceptionTranslatorSupplier; private SQLExceptionTranslator exceptionTranslator; + private ReentrantLock lock = new ReentrantLock(); /** * Creates a new {@code PersistenceExceptionTranslator} instance with {@code SQLErrorCodeSQLExceptionTranslator}. @@ -74,27 +76,29 @@ public MyBatisExceptionTranslator(Supplier exceptionTran } } - /** - * {@inheritDoc} - */ @Override public DataAccessException translateExceptionIfPossible(RuntimeException e) { if (e instanceof PersistenceException) { // Batch exceptions come inside another PersistenceException // recursion has a risk of infinite loop so better make another if + var msg = e.getMessage(); if (e.getCause() instanceof PersistenceException) { e = (PersistenceException) e.getCause(); + if (msg == null) { + msg = e.getMessage(); + } } if (e.getCause() instanceof SQLException) { this.initExceptionTranslator(); - String task = e.getMessage() + "\n"; - SQLException se = (SQLException) e.getCause(); - DataAccessException dae = this.exceptionTranslator.translate(task, null, se); + var task = e.getMessage() + "\n"; + var se = (SQLException) e.getCause(); + var dae = this.exceptionTranslator.translate(task, null, se); return dae != null ? dae : new UncategorizedSQLException(task, null, se); - } else if (e.getCause() instanceof TransactionException) { + } + if (e.getCause() instanceof TransactionException) { throw (TransactionException) e.getCause(); } - return new MyBatisSystemException(e); + return new MyBatisSystemException(msg, e); } return null; } @@ -102,9 +106,14 @@ public DataAccessException translateExceptionIfPossible(RuntimeException e) { /** * Initializes the internal translator reference. */ - private synchronized void initExceptionTranslator() { - if (this.exceptionTranslator == null) { - this.exceptionTranslator = exceptionTranslatorSupplier.get(); + private void initExceptionTranslator() { + lock.lock(); + try { + if (this.exceptionTranslator == null) { + this.exceptionTranslator = exceptionTranslatorSupplier.get(); + } + } finally { + lock.unlock(); } } diff --git a/src/main/java/org/mybatis/spring/MyBatisSystemException.java b/src/main/java/org/mybatis/spring/MyBatisSystemException.java index 566cf9ba62..a40d25c4a6 100644 --- a/src/main/java/org/mybatis/spring/MyBatisSystemException.java +++ b/src/main/java/org/mybatis/spring/MyBatisSystemException.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2023 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. @@ -31,8 +31,13 @@ public class MyBatisSystemException extends UncategorizedDataAccessException { private static final long serialVersionUID = -5284728621670758939L; + @Deprecated(since = "3.0.4", forRemoval = true) public MyBatisSystemException(Throwable cause) { - super(null, cause); + this(cause.getMessage(), cause); + } + + public MyBatisSystemException(String msg, Throwable cause) { + super(msg, cause); } } diff --git a/src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java b/src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java index c4032f142c..6a268ced8f 100644 --- a/src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java +++ b/src/main/java/org/mybatis/spring/SqlSessionFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the original author or authors. + * Copyright 2010-2024 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. @@ -64,7 +64,6 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; @@ -553,24 +552,20 @@ public void addTypeAliases(Class... typeAliases) { private T[] appendArrays(T[] oldArrays, T[] newArrays, IntFunction generator) { if (oldArrays == null) { return newArrays; - } else { - if (newArrays == null) { - return oldArrays; - } else { - List newList = new ArrayList<>(Arrays.asList(oldArrays)); - newList.addAll(Arrays.asList(newArrays)); - return newList.toArray(generator.apply(0)); - } } + if (newArrays == null) { + return oldArrays; + } + List newList = new ArrayList<>(Arrays.asList(oldArrays)); + newList.addAll(Arrays.asList(newArrays)); + return newList.toArray(generator.apply(0)); } - /** - * {@inheritDoc} - */ @Override public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); + // TODO Review this statement as it seems off! state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); @@ -693,8 +688,8 @@ protected SqlSessionFactory buildSqlSessionFactory() throws Exception { continue; } try { - XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), - targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments()); + var xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, + mapperLocation.toString(), targetConfiguration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new IOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); @@ -711,9 +706,6 @@ protected SqlSessionFactory buildSqlSessionFactory() throws Exception { return this.sqlSessionFactoryBuilder.build(targetConfiguration); } - /** - * {@inheritDoc} - */ @Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { @@ -723,25 +715,16 @@ public SqlSessionFactory getObject() throws Exception { return this.sqlSessionFactory; } - /** - * {@inheritDoc} - */ @Override public Class getObjectType() { return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass(); } - /** - * {@inheritDoc} - */ @Override public boolean isSingleton() { return true; } - /** - * {@inheritDoc} - */ @Override public void onApplicationEvent(ContextRefreshedEvent event) { if (failFast) { @@ -752,14 +735,14 @@ public void onApplicationEvent(ContextRefreshedEvent event) { private Set> scanClasses(String packagePatterns, Class assignableType) throws IOException { Set> classes = new HashSet<>(); - String[] packagePatternArray = tokenizeToStringArray(packagePatterns, + var packagePatternArray = tokenizeToStringArray(packagePatterns, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); for (String packagePattern : packagePatternArray) { - Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + var resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class"); for (Resource resource : resources) { try { - ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata(); + var classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata(); Class clazz = Resources.classForName(classMetadata.getClassName()); if (assignableType == null || assignableType.isAssignableFrom(clazz)) { classes.add(clazz); diff --git a/src/main/java/org/mybatis/spring/SqlSessionTemplate.java b/src/main/java/org/mybatis/spring/SqlSessionTemplate.java index cb6eaf4bd6..ca1a6405b5 100644 --- a/src/main/java/org/mybatis/spring/SqlSessionTemplate.java +++ b/src/main/java/org/mybatis/spring/SqlSessionTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -144,243 +144,151 @@ public PersistenceExceptionTranslator getPersistenceExceptionTranslator() { return this.exceptionTranslator; } - /** - * {@inheritDoc} - */ @Override public T selectOne(String statement) { return this.sqlSessionProxy.selectOne(statement); } - /** - * {@inheritDoc} - */ @Override public T selectOne(String statement, Object parameter) { return this.sqlSessionProxy.selectOne(statement, parameter); } - /** - * {@inheritDoc} - */ @Override public Map selectMap(String statement, String mapKey) { return this.sqlSessionProxy.selectMap(statement, mapKey); } - /** - * {@inheritDoc} - */ @Override public Map selectMap(String statement, Object parameter, String mapKey) { return this.sqlSessionProxy.selectMap(statement, parameter, mapKey); } - /** - * {@inheritDoc} - */ @Override public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { return this.sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds); } - /** - * {@inheritDoc} - */ @Override public Cursor selectCursor(String statement) { return this.sqlSessionProxy.selectCursor(statement); } - /** - * {@inheritDoc} - */ @Override public Cursor selectCursor(String statement, Object parameter) { return this.sqlSessionProxy.selectCursor(statement, parameter); } - /** - * {@inheritDoc} - */ @Override public Cursor selectCursor(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds); } - /** - * {@inheritDoc} - */ @Override public List selectList(String statement) { return this.sqlSessionProxy.selectList(statement); } - /** - * {@inheritDoc} - */ @Override public List selectList(String statement, Object parameter) { return this.sqlSessionProxy.selectList(statement, parameter); } - /** - * {@inheritDoc} - */ @Override public List selectList(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy.selectList(statement, parameter, rowBounds); } - /** - * {@inheritDoc} - */ @Override public void select(String statement, ResultHandler handler) { this.sqlSessionProxy.select(statement, handler); } - /** - * {@inheritDoc} - */ @Override public void select(String statement, Object parameter, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, handler); } - /** - * {@inheritDoc} - */ @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { this.sqlSessionProxy.select(statement, parameter, rowBounds, handler); } - /** - * {@inheritDoc} - */ @Override public int insert(String statement) { return this.sqlSessionProxy.insert(statement); } - /** - * {@inheritDoc} - */ @Override public int insert(String statement, Object parameter) { return this.sqlSessionProxy.insert(statement, parameter); } - /** - * {@inheritDoc} - */ @Override public int update(String statement) { return this.sqlSessionProxy.update(statement); } - /** - * {@inheritDoc} - */ @Override public int update(String statement, Object parameter) { return this.sqlSessionProxy.update(statement, parameter); } - /** - * {@inheritDoc} - */ @Override public int delete(String statement) { return this.sqlSessionProxy.delete(statement); } - /** - * {@inheritDoc} - */ @Override public int delete(String statement, Object parameter) { return this.sqlSessionProxy.delete(statement, parameter); } - /** - * {@inheritDoc} - */ @Override public T getMapper(Class type) { return getConfiguration().getMapper(type, this); } - /** - * {@inheritDoc} - */ @Override public void commit() { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } - /** - * {@inheritDoc} - */ @Override public void commit(boolean force) { throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession"); } - /** - * {@inheritDoc} - */ @Override public void rollback() { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } - /** - * {@inheritDoc} - */ @Override public void rollback(boolean force) { throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession"); } - /** - * {@inheritDoc} - */ @Override public void close() { throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession"); } - /** - * {@inheritDoc} - */ @Override public void clearCache() { this.sqlSessionProxy.clearCache(); } - /** - * {@inheritDoc} - */ @Override public Configuration getConfiguration() { return this.sqlSessionFactory.getConfiguration(); } - /** - * {@inheritDoc} - */ @Override public Connection getConnection() { return this.sqlSessionProxy.getConnection(); } - /** - * {@inheritDoc} - * - * @since 1.0.2 - */ @Override public List flushStatements() { return this.sqlSessionProxy.flushStatements(); @@ -419,10 +327,10 @@ public void destroy() throws Exception { private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, - SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); + var sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, + SqlSessionTemplate.this.exceptionTranslator); try { - Object result = method.invoke(sqlSession, args); + var result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() @@ -430,7 +338,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } return result; } catch (Throwable t) { - Throwable unwrapped = unwrapThrowable(t); + var unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); diff --git a/src/main/java/org/mybatis/spring/SqlSessionUtils.java b/src/main/java/org/mybatis/spring/SqlSessionUtils.java index f36272f0d2..bc629960d9 100644 --- a/src/main/java/org/mybatis/spring/SqlSessionUtils.java +++ b/src/main/java/org/mybatis/spring/SqlSessionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the original author or authors. + * Copyright 2010-2024 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. @@ -18,14 +18,12 @@ import static org.springframework.util.Assert.notNull; import org.apache.ibatis.exceptions.PersistenceException; -import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.logging.Logger; import org.mybatis.logging.LoggerFactory; import org.mybatis.spring.transaction.SpringManagedTransactionFactory; -import org.springframework.dao.DataAccessException; import org.springframework.dao.TransientDataAccessResourceException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.jdbc.datasource.DataSourceUtils; @@ -68,7 +66,7 @@ private SqlSessionUtils() { * {@code SpringManagedTransactionFactory} */ public static SqlSession getSqlSession(SqlSessionFactory sessionFactory) { - ExecutorType executorType = sessionFactory.getConfiguration().getDefaultExecutorType(); + var executorType = sessionFactory.getConfiguration().getDefaultExecutorType(); return getSqlSession(sessionFactory, executorType, null); } @@ -99,9 +97,9 @@ public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, Executo notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED); - SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); + var holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); - SqlSession session = sessionHolder(executorType, holder); + var session = sessionHolder(executorType, holder); if (session != null) { return session; } @@ -134,7 +132,7 @@ private static void registerSessionHolder(SqlSessionFactory sessionFactory, Exec PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { SqlSessionHolder holder; if (TransactionSynchronizationManager.isSynchronizationActive()) { - Environment environment = sessionFactory.getConfiguration().getEnvironment(); + var environment = sessionFactory.getConfiguration().getEnvironment(); if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]"); @@ -145,14 +143,12 @@ private static void registerSessionHolder(SqlSessionFactory sessionFactory, Exec .registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); holder.setSynchronizedWithTransaction(true); holder.requested(); + } else if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { + LOGGER.debug(() -> "SqlSession [" + session + + "] was not registered for synchronization because DataSource is not transactional"); } else { - if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { - LOGGER.debug(() -> "SqlSession [" + session - + "] was not registered for synchronization because DataSource is not transactional"); - } else { - throw new TransientDataAccessResourceException( - "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"); - } + throw new TransientDataAccessResourceException( + "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"); } } else { LOGGER.debug(() -> "SqlSession [" + session @@ -191,8 +187,8 @@ public static void closeSqlSession(SqlSession session, SqlSessionFactory session notNull(session, NO_SQL_SESSION_SPECIFIED); notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); - SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); - if ((holder != null) && (holder.getSqlSession() == session)) { + var holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); + if (holder != null && holder.getSqlSession() == session) { LOGGER.debug(() -> "Releasing transactional SqlSession [" + session + "]"); holder.released(); } else { @@ -215,9 +211,9 @@ public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFa notNull(session, NO_SQL_SESSION_SPECIFIED); notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); - SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); + var holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); - return (holder != null) && (holder.getSqlSession() == session); + return holder != null && holder.getSqlSession() == session; } /** @@ -241,18 +237,12 @@ public SqlSessionSynchronization(SqlSessionHolder holder, SqlSessionFactory sess this.sessionFactory = sessionFactory; } - /** - * {@inheritDoc} - */ @Override public int getOrder() { // order right before any Connection synchronization return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 1; } - /** - * {@inheritDoc} - */ @Override public void suspend() { if (this.holderActive) { @@ -261,9 +251,6 @@ public void suspend() { } } - /** - * {@inheritDoc} - */ @Override public void resume() { if (this.holderActive) { @@ -272,9 +259,6 @@ public void resume() { } } - /** - * {@inheritDoc} - */ @Override public void beforeCommit(boolean readOnly) { // Connection commit or rollback will be handled by ConnectionSynchronization or @@ -289,8 +273,7 @@ public void beforeCommit(boolean readOnly) { this.holder.getSqlSession().commit(); } catch (PersistenceException p) { if (this.holder.getPersistenceExceptionTranslator() != null) { - DataAccessException translated = this.holder.getPersistenceExceptionTranslator() - .translateExceptionIfPossible(p); + var translated = this.holder.getPersistenceExceptionTranslator().translateExceptionIfPossible(p); if (translated != null) { throw translated; } @@ -300,9 +283,6 @@ public void beforeCommit(boolean readOnly) { } } - /** - * {@inheritDoc} - */ @Override public void beforeCompletion() { // Issue #18 Close SqlSession and deregister it now @@ -317,9 +297,6 @@ public void beforeCompletion() { } } - /** - * {@inheritDoc} - */ @Override public void afterCompletion(int status) { if (this.holderActive) { diff --git a/src/main/java/org/mybatis/spring/annotation/MapperScan.java b/src/main/java/org/mybatis/spring/annotation/MapperScan.java index 9d51b7ce42..e6e244ed2d 100644 --- a/src/main/java/org/mybatis/spring/annotation/MapperScan.java +++ b/src/main/java/org/mybatis/spring/annotation/MapperScan.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the original author or authors. + * Copyright 2010-2024 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. @@ -27,6 +27,7 @@ import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.core.annotation.AliasFor; @@ -196,4 +197,13 @@ */ boolean processPropertyPlaceHolders() default true; + /** + * Specifies which types are not eligible for mapper scanning. + * + * @since 3.0.4 + * + * @return array of customized mapper excludeFilter + */ + ComponentScan.Filter[] excludeFilters() default {}; + } diff --git a/src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java b/src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java index fb8f1f94f6..81ecd2f1ff 100644 --- a/src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java +++ b/src/main/java/org/mybatis/spring/annotation/MapperScannerRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the original author or authors. + * Copyright 2010-2024 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. @@ -18,7 +18,9 @@ import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import org.mybatis.spring.mapper.ClassPathMapperScanner; @@ -31,10 +33,15 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.ResourceLoaderAware; +import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -54,23 +61,17 @@ */ public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { - /** - * {@inheritDoc} - * - * @deprecated Since 2.0.2, this method not used never. - */ + // Note: Do not move resourceLoader via cleanup + private ResourceLoader resourceLoader; + @Override - @Deprecated public void setResourceLoader(ResourceLoader resourceLoader) { - // NOP + this.resourceLoader = resourceLoader; } - /** - * {@inheritDoc} - */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { - AnnotationAttributes mapperScanAttrs = AnnotationAttributes + var mapperScanAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); if (mapperScanAttrs != null) { registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, @@ -81,7 +82,7 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); + var builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", annoAttrs.getBoolean("processPropertyPlaceHolders")); Class annotationClass = annoAttrs.getClass("annotationClass"); @@ -104,20 +105,18 @@ void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes a builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass); } - String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef"); + var sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef"); if (StringUtils.hasText(sqlSessionTemplateRef)) { builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef")); } - String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef"); + var sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef"); if (StringUtils.hasText(sqlSessionFactoryRef)) { builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef")); } - List basePackages = new ArrayList<>(); - - basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText) - .collect(Collectors.toList())); + List basePackages = new ArrayList<>(Arrays.stream(annoAttrs.getStringArray("basePackages")) + .filter(StringUtils::hasText).collect(Collectors.toList())); basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName) .collect(Collectors.toList())); @@ -126,12 +125,28 @@ void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes a basePackages.add(getDefaultBasePackage(annoMeta)); } - String lazyInitialization = annoAttrs.getString("lazyInitialization"); + var excludeFilterArray = annoAttrs.getAnnotationArray("excludeFilters"); + if (excludeFilterArray.length > 0) { + List typeFilters = new ArrayList<>(); + List> rawTypeFilters = new ArrayList<>(); + for (AnnotationAttributes excludeFilters : excludeFilterArray) { + if (excludeFilters.getStringArray("pattern").length > 0) { + // in oder to apply placeholder resolver + rawTypeFilters.addAll(parseFiltersHasPatterns(excludeFilters)); + } else { + typeFilters.addAll(typeFiltersFor(excludeFilters)); + } + } + builder.addPropertyValue("excludeFilters", typeFilters); + builder.addPropertyValue("rawExcludeFilters", rawTypeFilters); + } + + var lazyInitialization = annoAttrs.getString("lazyInitialization"); if (StringUtils.hasText(lazyInitialization)) { builder.addPropertyValue("lazyInitialization", lazyInitialization); } - String defaultScope = annoAttrs.getString("defaultScope"); + var defaultScope = annoAttrs.getString("defaultScope"); if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) { builder.addPropertyValue("defaultScope", defaultScope); } @@ -145,6 +160,74 @@ void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes a } + /** + * Parse excludeFilters which FilterType is REGEX or ASPECTJ + * + * @param filterAttributes + * AnnotationAttributes of excludeFilters + * + * @since 3.0.4 + */ + private List> parseFiltersHasPatterns(AnnotationAttributes filterAttributes) { + + List> rawTypeFilters = new ArrayList<>(); + FilterType filterType = filterAttributes.getEnum("type"); + var expressionArray = filterAttributes.getStringArray("pattern"); + for (String expression : expressionArray) { + switch (filterType) { + case REGEX: + case ASPECTJ: + Map typeFilter = new HashMap<>(16); + typeFilter.put("type", filterType.name().toLowerCase()); + typeFilter.put("expression", expression); + rawTypeFilters.add(typeFilter); + break; + default: + throw new IllegalArgumentException("Cannot specify the 'pattern' attribute if use the " + filterType + + " FilterType in exclude filter of @MapperScan"); + } + } + return rawTypeFilters; + } + + /** + * Parse excludeFilters which FilterType is ANNOTATION ASSIGNABLE or CUSTOM + * + * @param filterAttributes + * AnnotationAttributes of excludeFilters + * + * @since 3.0.4 + */ + private List typeFiltersFor(AnnotationAttributes filterAttributes) { + + List typeFilters = new ArrayList<>(); + FilterType filterType = filterAttributes.getEnum("type"); + + for (Class filterClass : filterAttributes.getClassArray("value")) { + switch (filterType) { + case ANNOTATION: + Assert.isAssignable(Annotation.class, filterClass, + "Specified an unsupported type in 'ANNOTATION' exclude filter of @MapperScan"); + @SuppressWarnings("unchecked") + var annoClass = (Class) filterClass; + typeFilters.add(new AnnotationTypeFilter(annoClass)); + break; + case ASSIGNABLE_TYPE: + typeFilters.add(new AssignableTypeFilter(filterClass)); + break; + case CUSTOM: + Assert.isAssignable(TypeFilter.class, filterClass, + "An error occured when processing a @ComponentScan " + "CUSTOM type filter: "); + typeFilters.add(BeanUtils.instantiateClass(filterClass, TypeFilter.class)); + break; + default: + throw new IllegalArgumentException("Cannot specify the 'value' or 'classes' attribute if use the " + + filterType + " FilterType in exclude filter of @MapperScan"); + } + } + return typeFilters; + } + private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) { return importingClassMetadata.getClassName() + "#" + MapperScannerRegistrar.class.getSimpleName() + "#" + index; } @@ -159,16 +242,13 @@ private static String getDefaultBasePackage(AnnotationMetadata importingClassMet * @since 2.0.0 */ static class RepeatingRegistrar extends MapperScannerRegistrar { - /** - * {@inheritDoc} - */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { - AnnotationAttributes mapperScansAttrs = AnnotationAttributes + var mapperScansAttrs = AnnotationAttributes .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName())); if (mapperScansAttrs != null) { - AnnotationAttributes[] annotations = mapperScansAttrs.getAnnotationArray("value"); - for (int i = 0; i < annotations.length; i++) { + var annotations = mapperScansAttrs.getAnnotationArray("value"); + for (var i = 0; i < annotations.length; i++) { registerBeanDefinitions(importingClassMetadata, annotations[i], registry, generateBaseBeanName(importingClassMetadata, i)); } diff --git a/src/main/java/org/mybatis/spring/batch/MyBatisBatchItemWriter.java b/src/main/java/org/mybatis/spring/batch/MyBatisBatchItemWriter.java index 1da81f0547..9999275ba2 100644 --- a/src/main/java/org/mybatis/spring/batch/MyBatisBatchItemWriter.java +++ b/src/main/java/org/mybatis/spring/batch/MyBatisBatchItemWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -18,9 +18,6 @@ import static org.springframework.util.Assert.isTrue; import static org.springframework.util.Assert.notNull; -import java.util.List; - -import org.apache.ibatis.executor.BatchResult; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; @@ -133,9 +130,6 @@ public void afterPropertiesSet() { notNull(itemToParameterConverter, "A itemToParameterConverter is required."); } - /** - * {@inheritDoc} - */ @Override public void write(final Chunk items) { @@ -146,7 +140,7 @@ public void write(final Chunk items) { sqlSessionTemplate.update(statementId, itemToParameterConverter.convert(item)); } - List results = sqlSessionTemplate.flushStatements(); + var results = sqlSessionTemplate.flushStatements(); if (assertUpdates) { if (results.size() != 1) { @@ -154,10 +148,10 @@ public void write(final Chunk items) { + "Expected 1 but number of BatchResult objects returned was " + results.size()); } - int[] updateCounts = results.get(0).getUpdateCounts(); + var updateCounts = results.get(0).getUpdateCounts(); - for (int i = 0; i < updateCounts.length; i++) { - int value = updateCounts[i]; + for (var i = 0; i < updateCounts.length; i++) { + var value = updateCounts[i]; if (value == 0) { throw new EmptyResultDataAccessException("Item " + i + " of " + updateCounts.length + " did not update any rows: [" + items.getItems().get(i) + "]", 1); diff --git a/src/main/java/org/mybatis/spring/batch/builder/MyBatisBatchItemWriterBuilder.java b/src/main/java/org/mybatis/spring/batch/builder/MyBatisBatchItemWriterBuilder.java index f833454b84..6270175fb4 100644 --- a/src/main/java/org/mybatis/spring/batch/builder/MyBatisBatchItemWriterBuilder.java +++ b/src/main/java/org/mybatis/spring/batch/builder/MyBatisBatchItemWriterBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -120,7 +120,7 @@ public MyBatisBatchItemWriterBuilder itemToParameterConverter(Converter * @return the writer */ public MyBatisBatchItemWriter build() { - MyBatisBatchItemWriter writer = new MyBatisBatchItemWriter<>(); + var writer = new MyBatisBatchItemWriter(); writer.setSqlSessionTemplate(this.sqlSessionTemplate); writer.setSqlSessionFactory(this.sqlSessionFactory); writer.setStatementId(this.statementId); diff --git a/src/main/java/org/mybatis/spring/batch/builder/MyBatisCursorItemReaderBuilder.java b/src/main/java/org/mybatis/spring/batch/builder/MyBatisCursorItemReaderBuilder.java index da81b4e227..11d0eec554 100644 --- a/src/main/java/org/mybatis/spring/batch/builder/MyBatisCursorItemReaderBuilder.java +++ b/src/main/java/org/mybatis/spring/batch/builder/MyBatisCursorItemReaderBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -140,7 +140,7 @@ public MyBatisCursorItemReaderBuilder maxItemCount(int maxItemCount) { * @return the reader */ public MyBatisCursorItemReader build() { - MyBatisCursorItemReader reader = new MyBatisCursorItemReader<>(); + var reader = new MyBatisCursorItemReader(); reader.setSqlSessionFactory(this.sqlSessionFactory); reader.setQueryId(this.queryId); reader.setParameterValues(this.parameterValues); diff --git a/src/main/java/org/mybatis/spring/batch/builder/MyBatisPagingItemReaderBuilder.java b/src/main/java/org/mybatis/spring/batch/builder/MyBatisPagingItemReaderBuilder.java index 4428318c79..e4504b6559 100644 --- a/src/main/java/org/mybatis/spring/batch/builder/MyBatisPagingItemReaderBuilder.java +++ b/src/main/java/org/mybatis/spring/batch/builder/MyBatisPagingItemReaderBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -156,7 +156,7 @@ public MyBatisPagingItemReaderBuilder maxItemCount(int maxItemCount) { * @return the reader */ public MyBatisPagingItemReader build() { - MyBatisPagingItemReader reader = new MyBatisPagingItemReader<>(); + var reader = new MyBatisPagingItemReader(); reader.setSqlSessionFactory(this.sqlSessionFactory); reader.setQueryId(this.queryId); reader.setParameterValues(this.parameterValues); diff --git a/src/main/java/org/mybatis/spring/config/MapperScannerBeanDefinitionParser.java b/src/main/java/org/mybatis/spring/config/MapperScannerBeanDefinitionParser.java index 927cd627d6..3ad4365cfd 100644 --- a/src/main/java/org/mybatis/spring/config/MapperScannerBeanDefinitionParser.java +++ b/src/main/java/org/mybatis/spring/config/MapperScannerBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the original author or authors. + * Copyright 2010-2024 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. @@ -16,6 +16,10 @@ package org.mybatis.spring.config; import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.mybatis.spring.mapper.ClassPathMapperScanner; import org.mybatis.spring.mapper.MapperFactoryBean; @@ -27,10 +31,10 @@ import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.beans.factory.xml.XmlReaderContext; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.w3c.dom.Element; +import org.w3c.dom.Node; /** * A {#code BeanDefinitionParser} that handles the element scan of the MyBatis. namespace @@ -44,7 +48,6 @@ * @see ClassPathMapperScanner * @see MapperScannerConfigurer */ - public class MapperScannerBeanDefinitionParser extends AbstractBeanDefinitionParser { private static final String ATTRIBUTE_BASE_PACKAGE = "base-package"; @@ -57,49 +60,52 @@ public class MapperScannerBeanDefinitionParser extends AbstractBeanDefinitionPar private static final String ATTRIBUTE_LAZY_INITIALIZATION = "lazy-initialization"; private static final String ATTRIBUTE_DEFAULT_SCOPE = "default-scope"; private static final String ATTRIBUTE_PROCESS_PROPERTY_PLACEHOLDERS = "process-property-placeholders"; + private static final String ATTRIBUTE_EXCLUDE_FILTER = "exclude-filter"; - /** - * {@inheritDoc} - * - * @since 2.0.2 - */ @Override protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); + var builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); - ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); + var classLoader = ClassUtils.getDefaultClassLoader(); - String processPropertyPlaceHolders = element.getAttribute(ATTRIBUTE_PROCESS_PROPERTY_PLACEHOLDERS); + var processPropertyPlaceHolders = element.getAttribute(ATTRIBUTE_PROCESS_PROPERTY_PLACEHOLDERS); builder.addPropertyValue("processPropertyPlaceHolders", !StringUtils.hasText(processPropertyPlaceHolders) || Boolean.parseBoolean(processPropertyPlaceHolders)); try { - String annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION); + var annotationClassName = element.getAttribute(ATTRIBUTE_ANNOTATION); if (StringUtils.hasText(annotationClassName)) { @SuppressWarnings("unchecked") Class annotationClass = (Class) classLoader .loadClass(annotationClassName); builder.addPropertyValue("annotationClass", annotationClass); } - String markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE); + var markerInterfaceClassName = element.getAttribute(ATTRIBUTE_MARKER_INTERFACE); if (StringUtils.hasText(markerInterfaceClassName)) { Class markerInterface = classLoader.loadClass(markerInterfaceClassName); builder.addPropertyValue("markerInterface", markerInterface); } - String nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR); + var nameGeneratorClassName = element.getAttribute(ATTRIBUTE_NAME_GENERATOR); if (StringUtils.hasText(nameGeneratorClassName)) { Class nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName); - BeanNameGenerator nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class); + var nameGenerator = BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class); builder.addPropertyValue("nameGenerator", nameGenerator); } - String mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS); + var mapperFactoryBeanClassName = element.getAttribute(ATTRIBUTE_MAPPER_FACTORY_BEAN_CLASS); if (StringUtils.hasText(mapperFactoryBeanClassName)) { @SuppressWarnings("unchecked") Class mapperFactoryBeanClass = (Class) classLoader .loadClass(mapperFactoryBeanClassName); builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass); } + + // parse raw exclude-filter in + var rawExcludeFilters = parseScanTypeFilters(element, parserContext); + if (!rawExcludeFilters.isEmpty()) { + builder.addPropertyValue("rawExcludeFilters", rawExcludeFilters); + } + } catch (Exception ex) { - XmlReaderContext readerContext = parserContext.getReaderContext(); + var readerContext = parserContext.getReaderContext(); readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause()); } @@ -115,11 +121,24 @@ protected AbstractBeanDefinition parseInternal(Element element, ParserContext pa return builder.getBeanDefinition(); } - /** - * {@inheritDoc} - * - * @since 2.0.2 - */ + private List> parseScanTypeFilters(Element element, ParserContext parserContext) { + List> typeFilters = new ArrayList<>(); + var nodeList = element.getChildNodes(); + for (var i = 0; i < nodeList.getLength(); i++) { + var node = nodeList.item(i); + if (Node.ELEMENT_NODE == node.getNodeType()) { + var localName = parserContext.getDelegate().getLocalName(node); + if (ATTRIBUTE_EXCLUDE_FILTER.equals(localName)) { + Map filter = new HashMap<>(16); + filter.put("type", ((Element) node).getAttribute("type")); + filter.put("expression", ((Element) node).getAttribute("expression")); + typeFilters.add(filter); + } + } + } + return typeFilters; + } + @Override protected boolean shouldGenerateIdAsFallback() { return true; diff --git a/src/main/java/org/mybatis/spring/config/NamespaceHandler.java b/src/main/java/org/mybatis/spring/config/NamespaceHandler.java index e3e777f5d2..12912c8dc6 100644 --- a/src/main/java/org/mybatis/spring/config/NamespaceHandler.java +++ b/src/main/java/org/mybatis/spring/config/NamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -28,9 +28,6 @@ */ public class NamespaceHandler extends NamespaceHandlerSupport { - /** - * {@inheritDoc} - */ @Override public void init() { registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser()); diff --git a/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java b/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java index fab1c66ac5..62955111e1 100644 --- a/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java +++ b/src/main/java/org/mybatis/spring/mapper/ClassPathMapperScanner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2023 the original author or authors. + * Copyright 2010-2024 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. @@ -17,6 +17,7 @@ import java.lang.annotation.Annotation; import java.util.Arrays; +import java.util.List; import java.util.Optional; import java.util.Set; @@ -38,8 +39,10 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.core.NativeDetector; +import org.springframework.core.env.Environment; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.TypeFilter; import org.springframework.util.StringUtils; /** @@ -85,7 +88,18 @@ public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { private Class mapperFactoryBeanClass = MapperFactoryBean.class; private String defaultScope; + private List excludeFilters; + public ClassPathMapperScanner(BeanDefinitionRegistry registry, Environment environment) { + super(registry, false, environment); + setIncludeAnnotationConfig(!AotDetector.useGeneratedArtifacts()); + setPrintWarnLogIfNotFoundMappers(!NativeDetector.inNativeImage()); + } + + /** + * @deprecated Please use the {@link #ClassPathMapperScanner(BeanDefinitionRegistry, Environment)}. + */ + @Deprecated(since = "3.0.4", forRemoval = true) public ClassPathMapperScanner(BeanDefinitionRegistry registry) { super(registry, false); setIncludeAnnotationConfig(!AotDetector.useGeneratedArtifacts()); @@ -134,6 +148,10 @@ public void setMarkerInterface(Class markerInterface) { this.markerInterface = markerInterface; } + public void setExcludeFilters(List excludeFilters) { + this.excludeFilters = excludeFilters; + } + public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @@ -190,7 +208,7 @@ public void setDefaultScope(String defaultScope) { * that extends a markerInterface or/and those annotated with the annotationClass */ public void registerFilters() { - boolean acceptAllInterfaces = true; + var acceptAllInterfaces = true; // if specified, use the given annotation and / or marker interface if (this.annotationClass != null) { @@ -216,9 +234,16 @@ protected boolean matchClassName(String className) { // exclude package-info.java addExcludeFilter((metadataReader, metadataReaderFactory) -> { - String className = metadataReader.getClassMetadata().getClassName(); + var className = metadataReader.getClassMetadata().getClassName(); return className.endsWith("package-info"); }); + + // exclude types declared by MapperScan.excludeFilters + if (excludeFilters != null && excludeFilters.size() > 0) { + for (TypeFilter excludeFilter : excludeFilters) { + addExcludeFilter(excludeFilter); + } + } } /** @@ -227,7 +252,7 @@ protected boolean matchClassName(String className) { */ @Override public Set doScan(String... basePackages) { - Set beanDefinitions = super.doScan(basePackages); + var beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { if (printWarnLogIfNotFoundMappers) { @@ -243,10 +268,10 @@ public Set doScan(String... basePackages) { private void processBeanDefinitions(Set beanDefinitions) { AbstractBeanDefinition definition; - BeanDefinitionRegistry registry = getRegistry(); + var registry = getRegistry(); for (BeanDefinitionHolder holder : beanDefinitions) { definition = (AbstractBeanDefinition) holder.getBeanDefinition(); - boolean scopedProxy = false; + var scopedProxy = false; if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) { definition = (AbstractBeanDefinition) Optional .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition()) @@ -254,7 +279,7 @@ private void processBeanDefinitions(Set beanDefinitions) { "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]")); scopedProxy = true; } - String beanClassName = definition.getBeanClassName(); + var beanClassName = definition.getBeanClassName(); LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface"); @@ -276,7 +301,7 @@ private void processBeanDefinitions(Set beanDefinitions) { definition.getPropertyValues().add("addToConfig", this.addToConfig); - boolean explicitFactoryUsed = false; + var explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); @@ -319,7 +344,7 @@ private void processBeanDefinitions(Set beanDefinitions) { } if (!definition.isSingleton()) { - BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true); + var proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true); if (registry.containsBeanDefinition(proxyHolder.getBeanName())) { registry.removeBeanDefinition(proxyHolder.getBeanName()); } @@ -329,26 +354,19 @@ private void processBeanDefinitions(Set beanDefinitions) { } } - /** - * {@inheritDoc} - */ @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent(); } - /** - * {@inheritDoc} - */ @Override protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) { if (super.checkCandidate(beanName, beanDefinition)) { return true; - } else { - LOGGER.warn(() -> "Skipping MapperFactoryBean with name '" + beanName + "' and '" - + beanDefinition.getBeanClassName() + "' mapperInterface" + ". Bean already defined with the same name!"); - return false; } + LOGGER.warn(() -> "Skipping MapperFactoryBean with name '" + beanName + "' and '" + + beanDefinition.getBeanClassName() + "' mapperInterface" + ". Bean already defined with the same name!"); + return false; } } diff --git a/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java b/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java index 89e4e1e415..f95b721314 100644 --- a/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java +++ b/src/main/java/org/mybatis/spring/mapper/MapperFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -18,7 +18,6 @@ import static org.springframework.util.Assert.notNull; import org.apache.ibatis.executor.ErrorContext; -import org.apache.ibatis.session.Configuration; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.support.SqlSessionDaoSupport; import org.springframework.beans.factory.FactoryBean; @@ -65,16 +64,13 @@ public MapperFactoryBean(Class mapperInterface) { this.mapperInterface = mapperInterface; } - /** - * {@inheritDoc} - */ @Override protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); - Configuration configuration = getSqlSession().getConfiguration(); + var configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); @@ -87,25 +83,16 @@ protected void checkDaoConfig() { } } - /** - * {@inheritDoc} - */ @Override public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } - /** - * {@inheritDoc} - */ @Override public Class getObjectType() { return this.mapperInterface; } - /** - * {@inheritDoc} - */ @Override public boolean isSingleton() { return true; diff --git a/src/main/java/org/mybatis/spring/mapper/MapperScannerConfigurer.java b/src/main/java/org/mybatis/spring/mapper/MapperScannerConfigurer.java index 5b10b71192..f220ce4dee 100644 --- a/src/main/java/org/mybatis/spring/mapper/MapperScannerConfigurer.java +++ b/src/main/java/org/mybatis/spring/mapper/MapperScannerConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -18,16 +18,18 @@ import static org.springframework.util.Assert.notNull; import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.regex.Pattern; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionTemplate; -import org.springframework.beans.PropertyValue; +import org.springframework.beans.BeanUtils; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PropertyResourceConfigurer; import org.springframework.beans.factory.config.TypedStringValue; @@ -39,6 +41,13 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.Environment; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.core.type.filter.AspectJTypeFilter; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.RegexPatternTypeFilter; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.lang.Nullable; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -109,6 +118,10 @@ public class MapperScannerConfigurer private Class markerInterface; + private List excludeFilters; + + private List> rawExcludeFilters; + private Class mapperFactoryBeanClass; private ApplicationContext applicationContext; @@ -191,6 +204,34 @@ public void setMarkerInterface(Class superClass) { this.markerInterface = superClass; } + /** + * Specifies which types are not eligible for the mapper scanner. + *

+ * The scanner will exclude types that define with excludeFilters. + * + * @since 3.0.4 + * + * @param excludeFilters + * list of TypeFilter + */ + public void setExcludeFilters(List excludeFilters) { + this.excludeFilters = excludeFilters; + } + + /** + * In order to support process PropertyPlaceHolders. + *

+ * After parsed, it will be added to excludeFilters. + * + * @since 3.0.4 + * + * @param rawExcludeFilters + * list of rawExcludeFilter + */ + public void setRawExcludeFilters(List> rawExcludeFilters) { + this.rawExcludeFilters = rawExcludeFilters; + } + /** * Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context. * Usually this is only needed when you have more than one datasource. @@ -279,17 +320,11 @@ public void setMapperFactoryBeanClass(Class mapperF this.mapperFactoryBeanClass = mapperFactoryBeanClass; } - /** - * {@inheritDoc} - */ @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } - /** - * {@inheritDoc} - */ @Override public void setBeanName(String name) { this.beanName = name; @@ -333,37 +368,27 @@ public void setDefaultScope(String defaultScope) { this.defaultScope = defaultScope; } - /** - * {@inheritDoc} - */ @Override public void afterPropertiesSet() throws Exception { notNull(this.basePackage, "Property 'basePackage' is required"); } - /** - * {@inheritDoc} - */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // left intentionally blank } - /** - * {@inheritDoc} - * - * @since 1.0.2 - */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { if (this.processPropertyPlaceHolders) { processPropertyPlaceHolders(); } - ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); + var scanner = new ClassPathMapperScanner(registry, getEnvironment()); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); + scanner.setExcludeFilters(this.excludeFilters = mergeExcludeFilters()); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); @@ -372,7 +397,7 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { scanner.setBeanNameGenerator(this.nameGenerator); scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass); if (StringUtils.hasText(lazyInitialization)) { - scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization)); + scanner.setLazyInitialization(Boolean.parseBoolean(lazyInitialization)); } if (StringUtils.hasText(defaultScope)) { scanner.setDefaultScope(defaultScope); @@ -393,13 +418,13 @@ private void processPropertyPlaceHolders() { false, false); if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) { - BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext).getBeanFactory() + var mapperScannerBean = ((ConfigurableApplicationContext) applicationContext).getBeanFactory() .getBeanDefinition(beanName); // PropertyResourceConfigurer does not expose any methods to explicitly perform // property placeholder substitution. Instead, create a BeanFactory that just // contains this mapper scanner and post process the factory. - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + var factory = new DefaultListableBeanFactory(); factory.registerBeanDefinition(beanName, mapperScannerBean); for (PropertyResourceConfigurer prc : prcs.values()) { @@ -413,6 +438,7 @@ private void processPropertyPlaceHolders() { this.sqlSessionTemplateBeanName = getPropertyValue("sqlSessionTemplateBeanName", values); this.lazyInitialization = getPropertyValue("lazyInitialization", values); this.defaultScope = getPropertyValue("defaultScope", values); + this.rawExcludeFilters = getPropertyValueForTypeFilter("rawExcludeFilters", values); } this.basePackage = Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null); this.sqlSessionFactoryBeanName = Optional.ofNullable(this.sqlSessionFactoryBeanName) @@ -429,23 +455,88 @@ private Environment getEnvironment() { } private String getPropertyValue(String propertyName, PropertyValues values) { - PropertyValue property = values.getPropertyValue(propertyName); + var property = values.getPropertyValue(propertyName); if (property == null) { return null; } - Object value = property.getValue(); + var value = property.getValue(); if (value == null) { return null; - } else if (value instanceof String) { + } + if (value instanceof String) { return value.toString(); - } else if (value instanceof TypedStringValue) { + } + if (value instanceof TypedStringValue) { return ((TypedStringValue) value).getValue(); - } else { + } + return null; + } + + @SuppressWarnings("unchecked") + private List> getPropertyValueForTypeFilter(String propertyName, PropertyValues values) { + var property = values.getPropertyValue(propertyName); + Object value; + if (property == null || (value = property.getValue()) == null || !(value instanceof List)) { return null; } + return (List>) value; + } + + private List mergeExcludeFilters() { + List typeFilters = new ArrayList<>(); + if (this.rawExcludeFilters == null || this.rawExcludeFilters.isEmpty()) { + return this.excludeFilters; + } + if (this.excludeFilters != null && !this.excludeFilters.isEmpty()) { + typeFilters.addAll(this.excludeFilters); + } + try { + for (Map typeFilter : this.rawExcludeFilters) { + typeFilters.add( + createTypeFilter(typeFilter.get("type"), typeFilter.get("expression"), this.getClass().getClassLoader())); + } + } catch (ClassNotFoundException exception) { + throw new RuntimeException("ClassNotFoundException occur when to load the Specified excludeFilter classes.", + exception); + } + return typeFilters; + } + + @SuppressWarnings("unchecked") + private TypeFilter createTypeFilter(String filterType, String expression, @Nullable ClassLoader classLoader) + throws ClassNotFoundException { + + if (this.processPropertyPlaceHolders) { + expression = this.getEnvironment().resolvePlaceholders(expression); + } + + switch (filterType) { + case "annotation": + Class filterAnno = ClassUtils.forName(expression, classLoader); + if (!Annotation.class.isAssignableFrom(filterAnno)) { + throw new IllegalArgumentException( + "Class is not assignable to [" + Annotation.class.getName() + "]: " + expression); + } + return new AnnotationTypeFilter((Class) filterAnno); + case "custom": + Class filterClass = ClassUtils.forName(expression, classLoader); + if (!TypeFilter.class.isAssignableFrom(filterClass)) { + throw new IllegalArgumentException( + "Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression); + } + return (TypeFilter) BeanUtils.instantiateClass(filterClass); + case "assignable": + return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader)); + case "regex": + return new RegexPatternTypeFilter(Pattern.compile(expression)); + case "aspectj": + return new AspectJTypeFilter(expression, classLoader); + default: + throw new IllegalArgumentException("Unsupported filter type: " + filterType); + } } } diff --git a/src/main/java/org/mybatis/spring/support/SqlSessionDaoSupport.java b/src/main/java/org/mybatis/spring/support/SqlSessionDaoSupport.java index 0c312105be..0eeeab17c6 100644 --- a/src/main/java/org/mybatis/spring/support/SqlSessionDaoSupport.java +++ b/src/main/java/org/mybatis/spring/support/SqlSessionDaoSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -78,7 +78,7 @@ protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessi * @return a factory of SqlSession */ public final SqlSessionFactory getSqlSessionFactory() { - return (this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null); + return this.sqlSessionTemplate != null ? this.sqlSessionTemplate.getSqlSessionFactory() : null; } /** @@ -117,9 +117,6 @@ public SqlSessionTemplate getSqlSessionTemplate() { return this.sqlSessionTemplate; } - /** - * {@inheritDoc} - */ @Override protected void checkDaoConfig() { notNull(this.sqlSessionTemplate, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"); diff --git a/src/main/java/org/mybatis/spring/transaction/SpringManagedTransaction.java b/src/main/java/org/mybatis/spring/transaction/SpringManagedTransaction.java index 5bc038b51f..1b6cd04832 100644 --- a/src/main/java/org/mybatis/spring/transaction/SpringManagedTransaction.java +++ b/src/main/java/org/mybatis/spring/transaction/SpringManagedTransaction.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -58,9 +58,6 @@ public SpringManagedTransaction(DataSource dataSource) { this.dataSource = dataSource; } - /** - * {@inheritDoc} - */ @Override public Connection getConnection() throws SQLException { if (this.connection == null) { @@ -85,9 +82,6 @@ private void openConnection() throws SQLException { + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring"); } - /** - * {@inheritDoc} - */ @Override public void commit() throws SQLException { if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) { @@ -96,9 +90,6 @@ public void commit() throws SQLException { } } - /** - * {@inheritDoc} - */ @Override public void rollback() throws SQLException { if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) { @@ -107,20 +98,14 @@ public void rollback() throws SQLException { } } - /** - * {@inheritDoc} - */ @Override public void close() throws SQLException { DataSourceUtils.releaseConnection(this.connection, this.dataSource); } - /** - * {@inheritDoc} - */ @Override public Integer getTimeout() throws SQLException { - ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); + var holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (holder != null && holder.hasTimeout()) { return holder.getTimeToLiveInSeconds(); } diff --git a/src/main/java/org/mybatis/spring/transaction/SpringManagedTransactionFactory.java b/src/main/java/org/mybatis/spring/transaction/SpringManagedTransactionFactory.java index 32efd490f5..371c574d54 100644 --- a/src/main/java/org/mybatis/spring/transaction/SpringManagedTransactionFactory.java +++ b/src/main/java/org/mybatis/spring/transaction/SpringManagedTransactionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2022 the original author or authors. + * Copyright 2010-2024 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. @@ -31,25 +31,16 @@ */ public class SpringManagedTransactionFactory implements TransactionFactory { - /** - * {@inheritDoc} - */ @Override public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) { return new SpringManagedTransaction(dataSource); } - /** - * {@inheritDoc} - */ @Override public Transaction newTransaction(Connection conn) { throw new UnsupportedOperationException("New Spring transactions require a DataSource"); } - /** - * {@inheritDoc} - */ @Override public void setProperties(Properties props) { // not needed in this version diff --git a/src/main/resources/org/mybatis/spring/config/mybatis-spring.xsd b/src/main/resources/org/mybatis/spring/config/mybatis-spring.xsd index 5366717e4d..196a6fe35b 100644 --- a/src/main/resources/org/mybatis/spring/config/mybatis-spring.xsd +++ b/src/main/resources/org/mybatis/spring/config/mybatis-spring.xsd @@ -33,6 +33,16 @@ + + + + + + + @@ -160,4 +170,39 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/site/es/markdown/index.md b/src/site/es/markdown/index.md index 52a789bfd8..25d8c9331b 100644 --- a/src/site/es/markdown/index.md +++ b/src/site/es/markdown/index.md @@ -51,7 +51,7 @@ Users can read about MyBatis-Spring in the following translations:

  • Español
  • 日本語
  • 한국어
  • -
  • 简体中文
  • +
  • 简体中文
  • Do you want to read about MyBatis in your own native language? Fill an issue providing patches with your mother tongue documentation! diff --git a/src/site/es/markdown/mappers.md b/src/site/es/markdown/mappers.md index 5b10210fa8..01e3bce019 100644 --- a/src/site/es/markdown/mappers.md +++ b/src/site/es/markdown/mappers.md @@ -141,7 +141,7 @@ Si se indican ambas se añadirán todos los mappers que cumplan **cualquier** cr Los mappers descubiertos serán nombrados usando la estratégia de nombres por defecto de Spring para los componentes autodetectados (see [the Spring reference document(Core Technologies -Naming autodetected components-](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-scanning-name-generator)). Es decir, si no se encuentra ninguna anotación, se usará el nombre no cualificado sin capitalizar del mapper. Pero si se encuentra una anotación `@Component` o JSR-330 `@Named` se obtendrá el nombre de dicha anotación. -Fíjate que puedes usar como valor de la `annotation` el valor `org.springframework.stereotype.Component`, `javax.inject.Named` (if you have JSE 6) o una anotación propia (que debe ser a su vez anotada) de forma que la anotación hará las veces de localizador y de proveedor de nombre. +Fíjate que puedes usar como valor de la `annotation` el valor `org.springframework.stereotype.Component`, `jakarta.inject.Named` (if you have Jakarta EE) o una anotación propia (que debe ser a su vez anotada) de forma que la anotación hará las veces de localizador y de proveedor de nombre. NOTE `` no puede encontrar y registrar mappers. Los mappers son interfaces y, para poderlos registrar en Spring, el scanner deben conocer cómo crear un `MapperFactoryBean` para cada interfaz encontrado. diff --git a/src/site/ja/markdown/index.md b/src/site/ja/markdown/index.md index 1bd7b7408b..74d84347e5 100644 --- a/src/site/ja/markdown/index.md +++ b/src/site/ja/markdown/index.md @@ -48,7 +48,7 @@ MyBatis-Spring は以下の言語の翻訳を用意しています。
  • Español
  • 日本語
  • 한국어
  • -
  • 简体中文
  • +
  • 简体中文
  • 母国語でMyBatis Springのリファレンスを読んでみませんか? ぜひドキュメントを母国語へ翻訳するためのIssue(パッチ)を作成してください! diff --git a/src/site/ja/markdown/mappers.md b/src/site/ja/markdown/mappers.md index 03dcb250a1..9d9a7f09a6 100644 --- a/src/site/ja/markdown/mappers.md +++ b/src/site/ja/markdown/mappers.md @@ -139,7 +139,7 @@ XML 設定の例: 検出された Mapper は、Spring の自動検出コンポーネントに対するデフォルト命名規則によって Bean 名が決められます[the Spring reference document(Core Technologies -Naming autodetected components-](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-scanning-name-generator) を参照してください。 アノテーションによる指定がない場合はクラス名の先頭を小文字にした文字列が Bean 名となりますが、`@Component` あるいは JSR-330 の `@Named` アノテーションを使って Bean 名を明示的に指定することもできます。 -先に説明した `annotation` 属性で `org.springframework.stereotype.Component` や `javax.inject.Named` (Java 6 以降を利用している場合のみ)を指定すれば、検出時のマーカーと Bean 名の指定を1つのアノテーションで兼ねることができます。 +先に説明した `annotation` 属性で `org.springframework.stereotype.Component` や `jakarta.inject.Named` (Jakarta EE 以降を利用している場合のみ)を指定すれば、検出時のマーカーと Bean 名の指定を1つのアノテーションで兼ねることができます。 同じ目的で独自に定義したアノテーションを使うこともできますが、このアノテーション自体に `@Component` か `@Named` を付加しておく必要があります。 NOTE diff --git a/src/site/ko/markdown/README.md b/src/site/ko/markdown/README.md index 4e31a121bc..03cf1d3fa2 100644 --- a/src/site/ko/markdown/README.md +++ b/src/site/ko/markdown/README.md @@ -1,6 +1,6 @@ # 목차 -이 페이지는 GitHub에서 인덱스를 렌더링하기위한 것입니다. +이 페이지는 GitHub에서 인덱스를 렌더링하기 위한 것입니다. > **NOTE:** > diff --git a/src/site/ko/markdown/batch.md b/src/site/ko/markdown/batch.md index 87b11c2600..cdcd9d0b5b 100644 --- a/src/site/ko/markdown/batch.md +++ b/src/site/ko/markdown/batch.md @@ -1,15 +1,15 @@ # Spring Batch -마이바티스 스프링 연동모듈의 1.1.0버전에서는 스프링 배치 애플리케이션을 만들기 위해 세개의 빈을 제공한다. -세개의 빈은 `MyBatisPagingItemReader` 와 `MyBatisCursorItemReader` 와 MyBatisBatchItemWriter이다. +마이바티스 스프링 연동 모듈의 1.1.0버전에서는 스프링 배치 애플리케이션을 만들기 위해 세 개의 빈을 제공한다. +세 개의 빈은 `MyBatisPagingItemReader` 와 `MyBatisCursorItemReader` 와 MyBatisBatchItemWriter이다. 또한 2.0.0 버전에서는 Java Configuration 을 지원하는 다음의 세 가지 Builder class 를 제공한다. `MyBatisPagingItemReaderBuilder`, `MyBatisCursorItemReaderBuilder` 그리고 `MyBatisBatchItemWriterBuilder` 이다. 중요 이 문서는 [스프링 배치](http://static.springsource.org/spring-batch/) 에 대한 것으로 마이바티스 배치 `SqlSession` 을 다루지는 않는다. -배치 세션에 대해서는 [SqlSession 사용](sqlsession.html) 에서 좀더 다루었다. +배치 세션에 대해서는 [SqlSession 사용](sqlsession.html) 에서 좀 더 다루었다. # MyBatisPagingItemReader @@ -17,15 +17,15 @@ 요청된 데이터를 가져오기 위해 `setQueryId` 프로퍼티에 명시된 쿼리를 실행한다. 쿼리는 `setPageSize` 프로퍼티에 명시된 크기만큼 데이터를 가져오도록 실행된다. -`read()` 메서드를 사용하면 필요할 때 현재 위치에서 정해진 수 만큼 더 추가 데이터를 가져온다. -reader는 몇가지의 표준적인 쿼리 파라미터를 제공하고 명명된 쿼리의 SQL은 요청된 크기만큼의 데이터를 만들기 위해 파라미터의 일부 혹은 모두 사용한다. -여기서 사용가능한 파라미터이다. +`read()` 메서드를 사용하면 필요할 때 현재 위치에서 정해진 수만큼 더 추가 데이터를 가져온다. +reader는 몇 가지의 표준적인 쿼리 파라미터를 제공하고 명명된 쿼리의 SQL은 요청된 크기만큼의 데이터를 만들기 위해 파라미터의 일부 혹은 모두 사용한다. +여기서 사용 가능한 파라미터이다. * `_page`: 읽을 페이지 수(0부터 시작) * `_pagesize`: 페이지의 크기, 이를테면 리턴하는 로우 수 * `_skiprows`: `_page` 와 `_pagesize`를 곱한 결과 -각각의 파라미터는 selet구문에서 다음처럼 매핑될 수 있다. +각각의 파라미터는 selet구문에서 다음처럼 매핑 될 수 있다. ```xml