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 extends SqlSessionFactory> 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 extends Annotation> 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 extends T> items) { @@ -146,7 +140,7 @@ public void write(final Chunk extends T> 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 extends T> 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 extends Annotation> annotationClass = (Class extends Annotation>) 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 extends MapperFactoryBean> mapperFactoryBeanClass = (Class extends MapperFactoryBean>) 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 extends MapperFactoryBean> 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 extends MapperFactoryBean> 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 extends MapperFactoryBean> 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 @@ -55,7 +55,7 @@ public class BatchAppConfig { } ``` -**좀더 복잡한 예제를 보자.** +**좀 더 복잡한 예제를 보자.** ```xml @@ -246,16 +246,16 @@ public class BatchAppConfig { ``` -**여러개의 테이블에 데이터를 쓰려면 한꺼번에 처리할 수 있도록 만든 writer(몇가지 규칙을 가지고)를 사용하자.** +**여러 개의 테이블에 데이터를 쓰려면 한꺼번에 처리할 수 있도록 만든 writer(몇 가지 규칙을 가지고)를 사용하자.** 이 기능은 마이바티스 3.2이상에서만 사용할 수 있다. 이전의 버전에서는 예상과 다르게 동작하는데 그 내용은 [이슈](http://code.google.com/p/mybatis/issues/detail?id=741)를 참고하면 된다. -배치가 관계를 가지는 데이터나 여러개의 데이터베이스를 다루는 것처럼 복잡한 데이터를 작성할때 필요하다면 insert구문이 한개에 테이블에만 데이터를 넣을수 있다는 사실만 피하면 가능하기도 하다. +배치가 관계를 가지는 데이터나 여러 개의 데이터베이스를 다루는 것처럼 복잡한 데이터를 작성할 때 필요하다면 insert구문이 한 개에 테이블에만 데이터를 넣을 수 있다는 사실만 피하면 가능하기도 하다. 이런 복잡한 데이터를 처리하기 위해 writer가 작성하는 아이템(Item)을 준비해야 한다. 다음의 기술을 사용하면 단순한 관계를 가진 데이터나 관계가 없는 테이블을 처리하는 아이템에서 사용할 수 있다. -이러한 방법으로 스프링 배치 아이템은 모든 레코드를 *다룰것이다*. -여기에는 1:1 *관계*를 가지는 *InteractionMetadata*, 관계가 없는 두개의 로우는 *VisitorInteraction* 와 *CustomerInteraction*이 있다. -이 각각의 객체는 다음과 같이 볼수 있다. +이러한 방법으로 스프링 배치 아이템은 모든 레코드를 *다룰 것이다*. +여기에는 1:1 *관계*를 가지는 *InteractionMetadata*, 관계가 없는 두 개의 로우는 *VisitorInteraction* 와 *CustomerInteraction*이 있다. +이 각각의 객체는 다음과 같이 볼 수 있다. ```java public class InteractionRecordToWriteInMultipleTables { @@ -271,7 +271,7 @@ public class Interaction { } ``` -그리고 스프링 설정에는 각각의 레코드를 처리하기위해 특별히 설정된 전용(delegates) writer를 사용하는 `CompositeItemWriter`가 있다. +그리고 스프링 설정에는 각각의 레코드를 처리하기 위해 특별히 설정된 전용(delegates) writer를 사용하는 `CompositeItemWriter`가 있다. ```xml @@ -304,7 +304,7 @@ public class BatchAppConfig { } ``` -각각의 전용(delegate) writer는 필요할 만큼 설정할 수 있다. 예를들면 *Interaction* 과 *InteractionMetadata*를 위한 writer가 있다. +각각의 전용(delegate) writer는 필요할 만큼 설정할 수 있다. 예를 들면 *Interaction* 과 *InteractionMetadata*를 위한 writer가 있다. ```xml ``` -reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 가리킬수 있다. +reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 가리킬 수 있다. 매퍼 파일에서 구문은 다음과 같은 방법으로 각각의 레코드를 위해 만들어져있다. @@ -345,9 +345,9 @@ reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 ``` -먼저 `insertInteractionMetadata`가 호출될것이고 update구문은 jdbc드라이버에 의해(`keyProperty` 와 `keyColumn`) 생성된 아이디들을 리턴하기 위해 설정되었다. -`InteractionMetadata` 객체가 이 쿼리에 의해 업데이트되면 다음의 쿼리는 `insertInteraction`를 통해 상위객체인 `Interaction`를 작성하기 위해 사용될수 있다. +먼저 `insertInteractionMetadata`가 호출될 것이고 update구문은 jdbc드라이버에 의해(`keyProperty` 와 `keyColumn`) 생성된 아이디들을 리턴하기 위해 설정되었다. +`InteractionMetadata` 객체가 이 쿼리에 의해 업데이트되면 다음의 쿼리는 `insertInteraction`를 통해 상위객체인 `Interaction`를 작성하기 위해 사용될 수 있다. -***방금 언급한 내용에 관련하여 JDBC드라이버가 똑같이 동작하지 않을수 있다. +***방금 언급한 내용에 관련하여 JDBC드라이버가 똑같이 동작하지 않을 수 있다. 이 글을 쓰는 시점에 H2 드라이버 1.3.168버전(`org.h2.jdbc.JdbcStatement#getGeneratedKeys`를 보라)만 배치모드에서 마지막 인덱스를 리턴한다. 반면에 MySQL 드라이버는 기대한 것과 동일하게 동작하고 모든 아이디를 리턴한다.*** diff --git a/src/site/ko/markdown/factorybean.md b/src/site/ko/markdown/factorybean.md index e0a79081ad..4c8cff57b4 100644 --- a/src/site/ko/markdown/factorybean.md +++ b/src/site/ko/markdown/factorybean.md @@ -2,11 +2,11 @@ # SqlSessionFactoryBean 마이바티스만 사용하면, `SqlSessionFactory`는 `SqlSessionFactoryBuilder`를 사용해서 생성한다. -마이바티스 스프링 연동모듈에서는, `SqlSessionFactoryBean`가 대신 사용된다. +마이바티스 스프링 연동 모듈에서는, `SqlSessionFactoryBean`가 대신 사용된다. ## 설정 -팩토리 빈을 생성하기 위해, 스프링 XML설정파일에 다음설정을 추가하자. +팩토리 빈을 생성하기 위해, 스프링 XML 설정 파일에 다음 설정을 추가하자. ```xml @@ -31,23 +31,23 @@ public class MyBatisConfig { ``` 일반적인 마이바티스 스프링 사용법에서는, `SqlSessionFactoryBean`이나 관련된 `SqlSessionFactory`를 직접 사용할 필요가 없다. -대신 세션 팩토리가 `MapperFactoryBean`나 `SqlSessionDaoSupport`를 확장하는 다른 DAO에 주입될것이다. +대신 세션 팩토리가 `MapperFactoryBean`나 `SqlSessionDaoSupport`를 확장하는 다른 DAO에 주입될 것이다. ## 속성 `SqlSessionFactory`는 JDBC `DataSource`의 필수 프로퍼티가 필요하다. 어떤 `DataSource`라도 상관없고 다른 스프링 데이터베이스 연결처럼 설정되어야만 한다. -하나의 공통적인 프로퍼티는 마이바티스 XML설정파일의 위치를 지정하기 위해 사용되는 `configLocation`이다. 이 프로퍼티를 설정하는 것은 디폴트 설정을 가진 마이바티스 설정을 변경해야 할 경우 뿐이다. +하나의 공통적인 프로퍼티는 마이바티스 XML 설정 파일의 위치를 지정하기 위해 사용되는 `configLocation`이다. 이 프로퍼티를 설정하는 것은 디폴트 설정을 가진 마이바티스 설정을 변경해야 할 경우 뿐이다. 대개는 ``과 `` 섹션을 변경하는 경우이다. -설정파일이 마이바티스 설정을 완전히 다룰 필요는 없다. 어떤 환경, 어떤 데이터소스 그리고 마이바티스 트랜잭션 관리자가 **무시**될수도 있다. +설정 파일이 마이바티스 설정을 완전히 다룰 필요는 없다. 어떤 환경, 어떤 데이터소스 그리고 마이바티스 트랜잭션 관리자가 **무시**될 수도 있다. `SqlSessionFactoryBean` 는 필요에 따라 이 값들을 설정하여 자체적인 MyBatis `Environment` 를 만든다. -설정파일이 필요한 다른 이유는 마이바티스 XML파일이 매퍼 클래스와 동일한 클래스패스에 있지 않은 경우이다. 이 설정을 사용하면 두가지 옵션이 있다. -첫번째는 마이바티스 설정파일에 `` 섹션을 사용해서 XML파일의 클래스패스를 지정하는 것이다. 두번째는 팩토리 빈의 `mapperLocations` 프로퍼티를 사용하는 것이다. +설정 파일이 필요한 다른 이유는 마이바티스 XML파일이 매퍼 클래스와 동일한 클래스패스에 있지 않은 경우이다. 이 설정을 사용하면 두 가지 옵션이 있다. +첫 번째는 마이바티스 설정파일에 `` 섹션을 사용해서 XML파일의 클래스패스를 지정하는 것이다. 두 번째는 팩토리 빈의 `mapperLocations` 프로퍼티를 사용하는 것이다. `mapperLocations` 프로퍼티는 매퍼에 관련된 자원의 위치를 나열한다. 이 프로퍼티는 마이바티스의 XML매퍼 파일들의 위치를 지정하기 위해 사용될 수 있다. -디렉터리 아래 모든 파일을 로드하기 위해 앤트(Ant) 스타일의 패턴을 사용할수도 있고 가장 상위 위치를 지정하는 것으로 재귀적으로 하위 경로를 찾도록 할수도 있다. 예를 들어보면 다음과 같다. +디렉터리 아래 모든 파일을 로드하기 위해 앤트(Ant) 스타일의 패턴을 사용할 수도 있고 가장 상위 위치를 지정하는 것으로 재귀적으로 하위 경로를 찾도록 할 수도 있다. 예를 들어보면 다음과 같다. ```xml @@ -58,7 +58,7 @@ public class MyBatisConfig { 이 설정은 `sample.config.mappers` 패키지 아래와 그 하위 패키지를 모두 검색해서 마이바티스 매퍼 XML파일을 모두 로드할 것이다. -컨테이너 관리 트랜잭션을 사용하는 환경에서 필요한 하나의 프로퍼티는 `transactionFactoryClass` 이다. 이에 관련해서는 트랜잭션을 다루는 장에서 볼수 있다. +컨테이너 관리 트랜잭션을 사용하는 환경에서 필요한 하나의 프로퍼티는 `transactionFactoryClass` 이다. 이에 관련해서는 트랜잭션을 다루는 장에서 볼 수 있다. 만약 multi-db 기능을 사용한다면 다음과 같이 `databaseIdProvider` 속성을 설정해야 한다. @@ -83,7 +83,7 @@ public class MyBatisConfig { ```` NOTE -1.3.0 버전 부터 `configuration` 속성이 추가되었다. 다음과 같이 MyBatis XML 설정 파일없이 `Configuration` 인스턴스를 직접 지정할 수 있습니다. +1.3.0 버전부터 `configuration` 속성이 추가되었다. 다음과 같이 MyBatis XML 설정 파일 없이 `Configuration` 인스턴스를 직접 지정할 수 있습니다. ```xml diff --git a/src/site/ko/markdown/getting-started.md b/src/site/ko/markdown/getting-started.md index 9547c6f39a..553dda2ba5 100644 --- a/src/site/ko/markdown/getting-started.md +++ b/src/site/ko/markdown/getting-started.md @@ -1,11 +1,11 @@ # 시작하기 -이 장은 마이바티스 스프링 연동모듈을 설치하고 셋팅하는 방법에 대해 간단히 보여준다. 그리고 트랜잭션을 사용하는 간단한 애플리케이션을 만드는 방법까지 다룰 것이다. +이 장은 마이바티스 스프링 연동 모듈을 설치하고 셋팅하는 방법에 대해 간단히 보여준다. 그리고 트랜잭션을 사용하는 간단한 애플리케이션을 만드는 방법까지 다룰 것이다. ## 설치 -마이바티스 스프링 연동모듈을 사용하기 위해서, 클래스패스에 `mybatis-spring-${project.version}.jar`를 포함시켜야 한다. +마이바티스 스프링 연동 모듈을 사용하기 위해서, 클래스패스에 `mybatis-spring-${project.version}.jar`를 포함시켜야 한다. 메이븐을 사용하고 있다면 pom.xml에 다음처럼 의존성을 추가하면 된다. @@ -19,10 +19,10 @@ ## 빠른 설정 -마이바티스를 스프링과 함께 사용하려면 스프링의 애플리케이션 컨텍스트에 적어도 두개를 정의해줄 필요가 있다. -두가지는 `SqlSessionFactory`와 한개 이상의 매퍼 인터페이스이다. +마이바티스를 스프링과 함께 사용하려면 스프링의 애플리케이션 컨텍스트에 적어도 두 개를 정의해 줄 필요가 있다. +두 가지는 `SqlSessionFactory`와 한 개 이상의 매퍼 인터페이스이다. -마이바티스 스프링 연동모듈에서, `SqlSessionFactoryBean`은 `SqlSessionFactory`를 만들기 위해 사용된다. 팩토리 빈을 설정하기 위해, 스프링 설정파일에 다음 설정을 추가하자. +마이바티스 스프링 연동 모듈에서, `SqlSessionFactoryBean`은 `SqlSessionFactory`를 만들기 위해 사용된다. 팩토리 빈을 설정하기 위해, 스프링 설정 파일에 다음 설정을 추가하자. ```xml @@ -62,10 +62,10 @@ UserMapper인터페이스는 다음처럼 `MapperFactoryBean`을 사용해서 ``` -매퍼는 **반드시** 구현체 클래스가 아닌 인터페이스로 정의되어야 한다. 예를들어, 애노테이션이 SQL을 명시하기 위해 사용되지만 마이바티스 매퍼 XML파일 또한 사용될 수 있다. +매퍼는 **반드시** 구현체 클래스가 아닌 인터페이스로 정의되어야 한다. 예를 들어, 애노테이션이 SQL을 명시하기 위해 사용되지만 마이바티스 매퍼 XML파일 또한 사용될 수 있다. -한번만 설정하면, 다른 스프링 빈에 주입하는 같은 방법으로 비즈니스/서비스 객체에 매퍼를 직접 주입할 수 있다. `MapperFactoryBean`은 `SqlSession`을 생성하고 닫는 작업을 잘 다룬다. -실행중인 스프링 트랜잭션이 있다면, 트랜잭션이 완료되는 시점에 커밋이나 롤백이 될 것이다. 마지막으로 예외가 발생하면 스프링의 `DataAccessException`예외가 발생한다. +한 번만 설정하면, 다른 스프링 빈에 주입하는 같은 방법으로 비즈니스/서비스 객체에 매퍼를 직접 주입할 수 있다. `MapperFactoryBean`은 `SqlSession`을 생성하고 닫는 작업을 잘 다룬다. +실행 중인 스프링 트랜잭션이 있다면, 트랜잭션이 완료되는 시점에 커밋이나 롤백이 될 것이다. 마지막으로 예외가 발생하면 스프링의 `DataAccessException`예외가 발생한다. 자바로 설정하면 다음과 같다. diff --git a/src/site/ko/markdown/index.md b/src/site/ko/markdown/index.md index 83e5567550..a43f6578aa 100644 --- a/src/site/ko/markdown/index.md +++ b/src/site/ko/markdown/index.md @@ -3,18 +3,18 @@ ## MyBatis-Spring 은 무엇일까? -마이바티스 스프링 연동모듈은 마이바티스와 스프링을 편하고 간단하게 연동한다. 이 모듈은 마이바티스로 하여금 스프링 트랜잭션에 쉽게 연동되도록 처리한다. 게다가 마이바티스 매퍼와 `SqlSession`을 다루고 다른 빈에 주입시켜준다. -마이바티스 예외를 스프링의 `DataAccessException`로 변환하기도 하고 마이바티스, 스프링 또는 마이바티스 스프링 연동모듈에 의존성을 없애기도 한다. +마이바티스 스프링 연동 모듈은 마이바티스와 스프링을 편하고 간단하게 연동한다. 이 모듈은 마이바티스로 하여금 스프링 트랜잭션에 쉽게 연동되도록 처리한다. 게다가 마이바티스 매퍼와 `SqlSession`을 다루고 다른 빈에 주입시켜준다. +마이바티스 예외를 스프링의 `DataAccessException`로 변환하기도 하고 마이바티스, 스프링 또는 마이바티스 스프링 연동 모듈에 의존성을 없애기도 한다. ## 동기 부여 -스프링 2.x은 아이바티스 2.x만을 지원한다. 스프링 3.x에서 마이바티스 3.x를 지원하기 위한 시도가 진행중이다. (스프링의 이슈관리 시스템인 [이슈](https://jira.springsource.org/browse/SPR-5991) 를 보라.) -불행하게도 스프링 3의 개발이 마이바티스 3.0의 정식릴리즈전에 개발이 완료되었다. 그래서 스프링팀은 릴리즈가 안된 마이바티스 코드를 함께 릴리즈하는 것을 원하지 않았고 실제적인 스프링 지원을 기다릴수밖에 없었다. +스프링 2.x은 아이바티스 2.x만을 지원한다. 스프링 3.x에서 마이바티스 3.x를 지원하기 위한 시도가 진행 중이다. (스프링의 이슈관리 시스템인 [이슈](https://jira.springsource.org/browse/SPR-5991) 를 보라.) +불행하게도 스프링 3의 개발이 마이바티스 3.0의 정식 릴리즈 전에 개발이 완료되었다. 그래서 스프링팀은 릴리즈가 안 된 마이바티스 코드를 함께 릴리즈하는 것을 원하지 않았고 실제적인 스프링 지원을 기다릴 수밖에 없었다. 스프링의 마이바티스 지원에 대한 관심으로 인해, 마이바티스 커뮤니티는 재결합하는 형태로 결정을 내고 대신 마이바티스의 하위 프로젝트 형태로 스프링 연동 프로젝트를 추가한다. ## 필요 조건 -마이바티스 스프링 연동을 시작하기 전에, 마이바티스와 스프링의 용어를 맞추는 일이 굉장히 중요했다. 이 문서는 배경지식이나 기본적인 셋업방법 그리고 마이바티스와 스프링의 설정에 대한 튜토리얼등은 제공하지 않는다. +마이바티스 스프링 연동을 시작하기 전에, 마이바티스와 스프링의 용어를 맞추는 일이 굉장히 중요했다. 이 문서는 배경지식이나 기본적인 셋업방법 그리고 마이바티스와 스프링의 설정에 대한 튜토리얼 등은 제공하지 않는다. MyBatis-Spring requires following versions: @@ -28,9 +28,9 @@ MyBatis-Spring requires following versions: ## 감사 인사 이 프로젝트가 실제로 만들어지게 도와준 모든 특별한 분들에게 정말 감사한다. -알파벳 순서로 보면, 코딩및 테스트 그리고 문서화를 담당했던 Eduardo Macarron, Hunter Presnall, Putthiphong Boonphong; +알파벳 순서로 보면, 코딩 및 테스트 그리고 문서화를 담당했던 Eduardo Macarron, Hunter Presnall, Putthiphong Boonphong; 그외 다양한 프로젝트 기여자인 Andrius Juozapaitis, Giovanni Cuccu, Mike Lanyon, Raj Nagappan, Tomas Pinos; -그리고 마이바티스에 하위 프로젝트로 가져올수 있도록 많은 것을 찾아준 Simone Tripodi 에게 감사한다. ;) +그리고 마이바티스에 하위 프로젝트로 가져올 수 있도록 많은 것을 찾아준 Simone Tripodi 에게 감사한다. ;) 이들이 없었다면 이 프로젝트는 존재하지 않았을 것이다. ## 이 문서가 더 나아지도록 도와주세요… @@ -39,16 +39,16 @@ MyBatis-Spring requires following versions: 이 문서의 원본은 markdown 포맷이며 [프로젝트의 Git](https://github.com/mybatis/spring/tree/master/src/site)에서 찾을 수 있다. repository 를 fork 하고, 업데이트하고 pull request 를 보내주십시오. -당신처럼 이 문서를 읽는 사람들에게 이 문서의 최고의 저자가 될수 있다! +당신처럼 이 문서를 읽는 사람들에게 이 문서의 최고의 저자가 될 수 있다! ## 번역 -사용자들은 다음의 번역문서별로 마이바티스 스프링 연동모듈에 대해 알수 있다. +사용자들은 다음의 번역 문서별로 마이바티스 스프링 연동 모듈에 대해 알 수 있다. English Español - 简体中文 + 简体中文 日本語 한국어 diff --git a/src/site/ko/markdown/mappers.md b/src/site/ko/markdown/mappers.md index c56ba731ff..d537db1e65 100644 --- a/src/site/ko/markdown/mappers.md +++ b/src/site/ko/markdown/mappers.md @@ -1,7 +1,7 @@ # 매퍼 주입 -`SqlSessionDaoSupport` 나 `SqlSessionTemplate` 를 직접적으로 사용하는 데이터 접근 객체(DAO)를 생성하기 보다, 마이바티스 스프링 연동모듈은 다른 빈에 직접 주입할 수 있는 쓰레드에 안전한 매퍼를 생성할 수 있다. +`SqlSessionDaoSupport` 나 `SqlSessionTemplate` 를 직접적으로 사용하는 데이터 접근 객체(DAO)를 생성하기보다, 마이바티스 스프링 연동 모듈은 다른 빈에 직접 주입할 수 있는 쓰레드에 안전한 매퍼를 생성할 수 있다. ```xml @@ -9,7 +9,7 @@ ``` -한번 주입하고나면 매퍼는 애플리케이션 로직에서 사용할수 있는 준비가 된다. +한번 주입하고 나면 매퍼는 애플리케이션 로직에서 사용할 수 있는 준비가 된다. ```java public class FooServiceImpl implements FooService { @@ -26,7 +26,7 @@ public class FooServiceImpl implements FooService { } ``` -이 코드를 보면 `SqlSession`이나 마이바티스 객체가 보이지 않는다. 게다가 세션을 생성하거나 열고 닫을필요도 없어보인다. 마이바티스 스프링 연동모듈이 알아서 처리할 것이다. +이 코드를 보면 `SqlSession`이나 마이바티스 객체가 보이지 않는다. 게다가 세션을 생성하거나 열고 닫을 필요도 없어 보인다. 마이바티스 스프링 연동 모듈이 알아서 처리할 것이다. ## 매퍼 등록하기 @@ -35,7 +35,7 @@ public class FooServiceImpl implements FooService { ### XML설정 사용 -매퍼는 다음처럼 XML설정파일에 `MapperFactoryBean`을 두는 것으로 스프링에 등록된다. +매퍼는 다음처럼 XML설정 파일에 `MapperFactoryBean`을 두는 것으로 스프링에 등록된다. ```xml @@ -45,10 +45,10 @@ public class FooServiceImpl implements FooService { ``` UserMapper가 매퍼 인터페이스와 같은 경로의 클래스패스에 마이바티스 XML매퍼 파일을 가지고 있다면 `MapperFactoryBean`이 자동으로 파싱할것이다. -매퍼 XML파일을 다른 클래스패스에 두는게 아니라면 마이바티스 설정파일에 매퍼를 지정할 필요가 없다. 좀더 세부적인 정보는 `SqlSessionFactoryBean`의 [`configLocation`](factorybean.html) 프로퍼티를 살펴보자. +매퍼 XML파일을 다른 클래스패스에 두는게 아니라면 마이바티스 설정 파일에 매퍼를 지정할 필요가 없다. 좀 더 세부적인 정보는 `SqlSessionFactoryBean`의 [`configLocation`](factorybean.html) 프로퍼티를 살펴보자. `MapperFactoryBean`은 `SqlSessionFactory` 나 `SqlSessionTemplate`가 필요하다. `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅하면 된다. -둘다 셋팅하면 `SqlSessionFactory`가 무시된다. 세션 팩토리 셋은 `SqlSessionTemplate`이 필요하고 `MapperFactoryBean`는 팩토리를 사용할것이다. +둘 다 셋팅하면 `SqlSessionFactory`가 무시된다. 세션 팩토리 셋은 `SqlSessionTemplate`이 필요하고 `MapperFactoryBean`는 팩토리를 사용할 것이다. ### 자바설정 사용 @@ -67,15 +67,15 @@ public class MyBatisConfig { ## 매퍼 스캔 -하나씩 매퍼를 모두 등록할 필요가 없다. 대신 클래스패스를 지정해서 마이바티스 스프링 연동모듈의 자동스캔기능을 사용할 수 있다. +하나씩 매퍼를 모두 등록할 필요가 없다. 대신 클래스패스를 지정해서 마이바티스 스프링 연동 모듈의 자동 스캔 기능을 사용할 수 있다. -자동스캔을 사용하는데는 3가지 방법이 있다. +자동 스캔을 사용하는 데는 3가지 방법이 있다. * `` 엘리먼트 사용 * `@MapperScan` 애노테이션 사용 * 스프링 XML파일을 사용해서 `MapperScannerConfigurer`를 등록 -`` 와 `@MapperScan` 모두 마이바티스 스프링 연동모듈 1.2.0에서 추가된 기능이다. `@MapperScan` 은 스프링 버전이 3.1이상이어야 한다. +`` 와 `@MapperScan` 모두 마이바티스 스프링 연동 모듈 1.2.0에서 추가된 기능이다. `@MapperScan` 은 스프링 버전이 3.1이상이어야 한다. Since 2.0.2, mapper scanning feature support a option (`lazy-initialization`) that control lazy initialization enabled/disabled of mapper bean. The motivation for adding this option is supporting a lazy initialization control feature supported by Spring Boot 2.2. @@ -107,7 +107,7 @@ The `default-scope` apply to the mapper bean(`MapperFactoryBean`) when scope of `` XML엘리먼트는 스프링에서 제공하는 `` 엘리먼트와 매우 유사한 방법으로 매퍼를 검색할 것이다. -샘플 XML설정을 아래에서 볼수 있다. +샘플 XML설정을 아래에서 볼 수 있다. ```xml ``` -`base-package` 속성은 매퍼 인터페이스 파일이 있는 가장 상위 패키지를 지정하면 된다. 세미콜론이나 콤마를 구분자로 사용해서 한개 이상의 패키지를 셋팅할 수 있다. +`base-package` 속성은 매퍼 인터페이스 파일이 있는 가장 상위 패키지를 지정하면 된다. 세미콜론이나 콤마를 구분자로 사용해서 한 개 이상의 패키지를 셋팅할 수 있다. 매퍼는 지정된 패키지에서 재귀적으로 하위 패키지를 모두 검색할 것이다. ``이 자동으로 주입할 수 있는 `MapperFactoryBean`를 생성하기 때문에 `SqlSessionFactory` 나 `SqlSessionTemplate` 를 명시할 필요가 없다. -하지만 한개 이상의 `DataSource`를 사용한다면 자동주입이 생각한데로 동작하지 않을수도 있다. 이 경우 사용할 빈 이름을 지정하기 위해 `factory-ref` 나 `template-ref` 속성을 사용할수 있다. +하지만 한 개 이상의 `DataSource`를 사용한다면 자동 주입이 생각한 대로 동작하지 않을 수도 있다. 이 경우 사용할 빈 이름을 지정하기 위해 `factory-ref` 나 `template-ref` 속성을 사용할수 있다. -``은 마커(marker) 인터페이스나 애노테이션을 명시해서 생성되는 매퍼를 필터링할수도 있다. `annotation` 프로퍼티는 검색할 애노테이션을 지정한다. -`marker-interface` 프로퍼티는 검색할 상위 인터페이스를 지정한다. 이 두개의 프로퍼티를 모두 지정하면, 매퍼는 두 조건을 모두 만족하는 인터페이스만을 추가한다. -디폴트로 이 두가지 프로퍼티는 모두 null이다. 그래서 base-package프로퍼티에 설정된 패키지 아래 모든 인터페이스가 매퍼로 로드될 것이다. +``은 마커(marker) 인터페이스나 애노테이션을 명시해서 생성되는 매퍼를 필터링할 수 도 있다. `annotation` 프로퍼티는 검색할 애노테이션을 지정한다. +`marker-interface` 프로퍼티는 검색할 상위 인터페이스를 지정한다. 이 두 개의 프로퍼티를 모두 지정하면, 매퍼는 두 조건을 모두 만족하는 인터페이스만을 추가한다. +디폴트로 이 두 가지 프로퍼티는 모두 null이다. 그래서 base-package프로퍼티에 설정된 패키지 아래 모든 인터페이스가 매퍼로 로드될 것이다. -발견된 매퍼는 자동검색된 컴포넌트를 위한 스프링의 디폴트 명명규칙 전략(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) 을 사용해서 빈이름이 명명된다. -빈 이름을 정하는 애노테이션이 없다면 매퍼의 이름에서 첫글자를 소문자로 변환한 형태로 빈 이름을 사용할 것이다. `@Component` 나 JSR-330의 `@Named` 애노테이션이 있다면 애노테이션에 정의한 이름을 그대로 사용할 것이다. -`annotation` 프로퍼티를 `org.springframework.stereotype.Component`, `javax.inject.Named`(자바SE 1.6을 사용한다면) 또는 개발자가 스스로 작성한 애노테이션으로 셋팅할 수 있다. +발견된 매퍼는 자동 검색된 컴포넌트를 위한 스프링의 디폴트 명명 규칙 전략(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) 을 사용해서 빈 이름이 명명된다. +빈 이름을 정하는 애노테이션이 없다면 매퍼의 이름에서 첫 글자를 소문자로 변환한 형태로 빈 이름을 사용할 것이다. `@Component` 나 JSR-330의 `@Named` 애노테이션이 있다면 애노테이션에 정의한 이름을 그대로 사용할 것이다. +`annotation` 프로퍼티를 `org.springframework.stereotype.Component`, `jakarta.inject.Named`(Jakarta EE을 사용한다면) 또는 개발자가 스스로 작성한 애노테이션으로 셋팅할 수 있다. 그러면 애노테이션은 마커와 이름을 제공하는 역할로 동작할 것이다. 중요 -`` 가 매퍼를 검색해서 등록을 하지 못할수도 있다. 매퍼는 인터페이스고 스프링에 빈으로 등록하기 위해서는 각각의 인터페이스를 찾기 위해 스캐너가 `MapperFactoryBean` 를 생성하는 방법을 알아야만 한다. +`` 가 매퍼를 검색해서 등록을 하지 못할 수도 있다. 매퍼는 인터페이스고 스프링에 빈으로 등록하기 위해서는 각각의 인터페이스를 찾기 위해 스캐너가 `MapperFactoryBean` 를 생성하는 방법을 알아야만 한다. ### @MapperScan -`@Configuration` 라고 불리는 스프링의 자바설정을 사용한다면 ``보다는 `@MapperScan`를 사용하길 선호할것이다. +`@Configuration` 라고 불리는 스프링의 자바 설정을 사용한다면 ``보다는 `@MapperScan`를 사용하길 선호할 것이다. `@MapperScan` 애노테이션은 다음처럼 사용된다. @@ -172,13 +172,13 @@ Since 2.0.4, If `basePackageClasses` or `basePackages` are not defined, scanning ``` -`sqlSessionFactory` 나 `sqlSessionTemplate`를 지정할 필요가 있다면 빈참조가 아닌 **빈이름**이 필요하다. `value` 프로퍼티는 빈 이름을 지정하고 `ref` 는 빈 참조를 지정하기 때문에 `value` 프로퍼티를 사용하자. +`sqlSessionFactory` 나 `sqlSessionTemplate`를 지정할 필요가 있다면 빈 참조가 아닌 **빈 이름**이 필요하다. `value` 프로퍼티는 빈 이름을 지정하고 `ref` 는 빈 참조를 지정하기 때문에 `value` 프로퍼티를 사용하자. ```xml ``` 중요 -`sqlSessionFactoryBean` 과 `sqlSessionTemplateBean` 프로퍼티는 마이바티스 스프링 연동모듈 1.0.2 버전 이상에서만 사용이 가능하다. +`sqlSessionFactoryBean` 과 `sqlSessionTemplateBean` 프로퍼티는 마이바티스 스프링 연동 모듈 1.0.2 버전 이상에서만 사용이 가능하다. 하지만 `MapperScannerConfigurer`는 잦은 에러를 발생시키는 `PropertyPlaceholderConfigurer`보다 앞서 실행되기 때문에 이 프로퍼티들은 사용하지 말길 바란다(deprecated). 대신 새롭게 추가된 프로퍼티인 `sqlSessionFactoryBeanName` 과 `sqlSessionTemplateBeanName` 를 사용하도록 권한다. diff --git a/src/site/ko/markdown/sqlsession.md b/src/site/ko/markdown/sqlsession.md index 783f42df0f..d0e3458ac8 100644 --- a/src/site/ko/markdown/sqlsession.md +++ b/src/site/ko/markdown/sqlsession.md @@ -1,21 +1,21 @@ # SqlSession 사용 -마이바티스에서는 `SqlSession`를 생성하기 위해 `SqlSessionFactory`를 사용한다. 세션을 한번 생성하면 매핑구문을 실행하거나 커밋 또는 롤백을 하기 위해 세션을 사용할수 있다. +마이바티스에서는 `SqlSession`를 생성하기 위해 `SqlSessionFactory`를 사용한다. 세션을 한번 생성하면 매핑구문을 실행하거나 커밋 또는 롤백을 하기 위해 세션을 사용할 수 있다. 마지막으로 더 이상 필요하지 않은 상태가 되면 세션을 닫는다. 마이바티스 스프링 연동모듈을 사용하면 `SqlSessionFactory`를 직접 사용할 필요가 없다. 왜냐하면, 스프링 트랜잭션 설정에 따라 자동으로 커밋 혹은 롤백을 수행하고 닫혀지는, 쓰레드에 안전한 `SqlSession` 개체가 스프링 빈에 주입될 수 있기 때문이다. # SqlSessionTemplate -`SqlSessionTemplate`은 마이바티스 스프링 연동모듈의 핵심이다. `SqlSessionTemplate`은 `SqlSession`을 구현하고 코드에서 `SqlSession`를 대체하는 역할을 한다. -`SqlSessionTemplate` 은 쓰레드에 안전하고 여러개의 DAO나 매퍼에서 공유할수 있다. +`SqlSessionTemplate`은 마이바티스 스프링 연동 모듈의 핵심이다. `SqlSessionTemplate`은 `SqlSession`을 구현하고 코드에서 `SqlSession`를 대체하는 역할을 한다. +`SqlSessionTemplate` 은 쓰레드에 안전하고 여러 개의 DAO나 매퍼에서 공유할 수 있다. -`getMapper()`에 의해 리턴된 매퍼가 가진 메서드를 포함해서 SQL을 처리하는 마이바티스 메서드를 호출할때 `SqlSessionTemplate`은 `SqlSession`이 현재의 스프링 트랜잭션에서 사용될수 있도록 보장한다. -추가적으로 `SqlSessionTemplate`은 필요한 시점에 세션을 닫고, 커밋하거나 롤백하는 것을 포함한 세션의 생명주기를 관리한다. 또한 마이바티스 예외를 스프링의 `DataAccessException`로 변환하는 작업또한 처리한다. +`getMapper()`에 의해 리턴된 매퍼가 가진 메서드를 포함해서 SQL을 처리하는 마이바티스 메서드를 호출할 때 `SqlSessionTemplate`은 `SqlSession`이 현재의 스프링 트랜잭션에서 사용될 수 있도록 보장한다. +추가적으로 `SqlSessionTemplate`은 필요한 시점에 세션을 닫고, 커밋하거나 롤백하는 것을 포함한 세션의 생명주기를 관리한다. 또한 마이바티스 예외를 스프링의 `DataAccessException`로 변환하는 작업 또한 처리한다. `SqlSessionTemplate`은 마이바티스의 디폴트 구현체인 `DefaultSqlSession` 대신 **항상** 사용된다. -왜냐하면 템플릿은 스프링 트랜잭션의 일부처럼 사용될 수 있고 여러개 주입된 매퍼 클래스에 의해 사용되도록 쓰레드에 안전하다. -동일한 애플리케이션에서 두개의 클래스간의 전환은 데이터 무결성 이슈를 야기할수 있다. +왜냐하면 템플릿은 스프링 트랜잭션의 일부처럼 사용될 수 있고 여러 개 주입된 매퍼 클래스에 의해 사용되도록 쓰레드에 안전하다. +동일한 애플리케이션에서 두 개의 클래스간 전환은 데이터 무결성 이슈를 야기할 수 있다. `SqlSessionTemplate`은 생성자 인자로 `SqlSessionFactory`를 사용해서 생성될 수 있다. @@ -59,7 +59,7 @@ public class UserDaoImpl implements UserDao { ``` -`SqlSessionTemplate`은 인자로 `ExecutorType`를 가지는 생성자를 가지고 있다. 이 인자는 예를들면 스프링 설정 XML을 다음처럼 설정해서 배치형태의 `SqlSession`를 만들수도 있다. +`SqlSessionTemplate`은 인자로 `ExecutorType`를 가지는 생성자를 가지고 있다. 이 인자는 예를 들면 스프링 설정 XML을 다음처럼 설정해서 배치형태의 `SqlSession`를 만들 수도 있다. ```xml @@ -94,14 +94,14 @@ public class UserService { } ``` -이러한 설정형태는 `SqlSessionFactory`의 디폴트 형태가 아닌 다른형태로 메서드를 실행해야 할때만 사용할 필요가 있다. +이러한 설정 형태는 `SqlSessionFactory`의 디폴트 형태가 아닌 다른 형태로 메서드를 실행해야 할 때만 사용할 필요가 있다. -이러한 형태에 대해 굳이 경로를 하자면 메서드를 호출할때 `ExecutorType`이 다르면 이미 시작된 트랜잭션을 사용하지 **못할**것이다. -다른 실행자(executor) 타입을 사용할때는 `SqlSessionTemplate`의 메서드를 구분된 트랜잭션(`PROPAGATION_REQUIRES_NEW`를 사용하는)이나 트랜잭션 외부에서 호출하는지 확실히해야 한다. +이러한 형태에 대해 굳이 경로를 하자면 메서드를 호출할 때 `ExecutorType`이 다르면 이미 시작된 트랜잭션을 사용하지 **못할**것이다. +다른 실행자(executor) 타입을 사용할 때는 `SqlSessionTemplate`의 메서드를 구분된 트랜잭션(`PROPAGATION_REQUIRES_NEW`를 사용하는)이나 트랜잭션 외부에서 호출하는지 확실히 해야 한다. ## SqlSessionDaoSupport -`SqlSessionDaoSupport`는 `SqlSession`을 제공하는 추상클래스이다. `getSqlSession()`메서드를 호출해서 다음처럼 SQL을 처리하는 마이바티스 메서드를 호출하기 위해 사용할 `SqlSessionTemplate`을 얻을 수 있다. +`SqlSessionDaoSupport`는 `SqlSession`을 제공하는 추상 클래스이다. `getSqlSession()`메서드를 호출해서 다음처럼 SQL을 처리하는 마이바티스 메서드를 호출하기 위해 사용할 `SqlSessionTemplate`을 얻을 수 있다. ```java public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { @@ -111,9 +111,9 @@ public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { } ``` -대개 `MapperFactoryBean`은 추가적인 코드가 필요없기 때문에 이 클래스를 선호한다. 하지만 DAO에서 마이바티스가 필요하지 않고 구현된 클래스가 필요하지 않을때만 유용하다. +대개 `MapperFactoryBean`은 추가적인 코드가 필요 없기 때문에 이 클래스를 선호한다. 하지만 DAO에서 마이바티스가 필요하지 않고 구현된 클래스가 필요하지 않을 때만 유용하다. -`SqlSessionDaoSupport`는 `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅할 필요가 있다. 두개의 프로퍼티를 모두 셋팅하면 `sqlSessionFactory`는 무시된다. +`SqlSessionDaoSupport`는 `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅할 필요가 있다. 두 개의 프로퍼티를 모두 셋팅하면 `sqlSessionFactory`는 무시된다. `SqlSessionDaoSupport`의 하위클래스인 `UserDaoImpl`가 있다고 하면 스프링에서는 다음처럼 설정될 수 있다. diff --git a/src/site/ko/markdown/transactions.md b/src/site/ko/markdown/transactions.md index df84e8861d..f7ad754ef7 100644 --- a/src/site/ko/markdown/transactions.md +++ b/src/site/ko/markdown/transactions.md @@ -1,13 +1,13 @@ # Transactions -마이바티스 스프링 연동모듈을 사용하는 중요한 이유중 하나는 마이바티스가 스프링 트랜잭션에 자연스럽게 연동될수 있다는 것이다. +마이바티스 스프링 연동 모듈을 사용하는 중요한 이유 중 하나는 마이바티스가 스프링 트랜잭션에 자연스럽게 연동될 수 있다는 것이다. 마이바티스에 종속되는 새로운 트랜잭션 관리를 만드는 것보다는 마이바티스 스프링 연동모듈이 스프링의 `DataSourceTransactionManager`과 융합되는 것이 좋다. -스프링 트랜잭션 관리자를 한번 설정하면, 대개의 경우처럼 스프링에서 트랜잭션을 설정할 수 있다. `@Transactional` 애노테이션과 AOP스타일의 설정 모두 지원한다. -하나의 `SqlSession`객체가 생성되고 트랜잭션이 동작하는 동안 지속적으로 사용될것이다. 세션은 트랜잭션이 완료되면 적절히 커밋이 되거나 롤백될것이다. +스프링 트랜잭션 관리자를 한 번 설정하면, 대개의 경우처럼 스프링에서 트랜잭션을 설정할 수 있다. `@Transactional` 애노테이션과 AOP스타일의 설정 모두 지원한다. +하나의 `SqlSession`객체가 생성되고 트랜잭션이 동작하는 동안 지속적으로 사용될 것이다. 세션은 트랜잭션이 완료되면 적절히 커밋이 되거나 롤백될것이다. -마이바티스 스프링 연동모듈은 한번 셋업되면 트랜잭션을 투명하게 관리한다. DAO클래스에 어떠한 추가적인 코드를 넣을 필요가 없다. +마이바티스 스프링 연동 모듈은 한 번 셋업되면 트랜잭션을 투명하게 관리한다. DAO클래스에 어떠한 추가적인 코드를 넣을 필요가 없다. ## 표준 설정 @@ -30,9 +30,9 @@ public class DataSourceConfig { } ``` -명시된 `DataSource`는 스프링을 사용할때 일반적으로 사용한다면 어떠한 JDBC `DataSource`도 될수 있다. JNDI룩업을 통해 얻어진 `DataSource`뿐 아니라 커넥션 풀링 기능도 포함한다. +명시된 `DataSource`는 스프링을 사용할 때 일반적으로 사용한다면 어떠한 JDBC `DataSource`도 될 수 있다. JNDI룩업을 통해 얻어진 `DataSource`뿐 아니라 커넥션 풀링 기능도 포함한다. -트랜잭션 관리자에 명시된 `DataSource`가 `SqlSessionFactoryBean`을 생성할때 사용된 것과 **반드시** 동일한 것이어야 하는 것만 꼭 기억하자. 그렇지 않으면 트랜잭션 관리가 제대로 되지 않을것이다. +트랜잭션 관리자에 명시된 `DataSource`가 `SqlSessionFactoryBean`을 생성할 때 사용된 것과 **반드시** 동일한 것이어야 하는 것만 꼭 기억하자. 그렇지 않으면 트랜잭션 관리가 제대로 되지 않을 것이다. ## Container Managed Transactions @@ -53,7 +53,7 @@ public class DataSourceConfig { } ``` -이 설정에서, 마이바티스는 CMT와 함께 설정된 스프링 트랜잭션 리소스처럼 동작할 것이다. 스프링은 이미 설정된 트랜잭션을 사용해서 `SqlSession`을 이미 동작중인 트랜잭션에 넣을 것이다. +이 설정에서, 마이바티스는 CMT와 함께 설정된 스프링 트랜잭션 리소스처럼 동작할 것이다. 스프링은 이미 설정된 트랜잭션을 사용해서 `SqlSession`을 이미 동작 중인 트랜잭션에 넣을 것이다. 시작된 트랜잭션이 없고 트랜잭션이 필요한 경우라면 스프링은 새로운 컨테이너 관리 트랜잭션을 시작할 것이다. CMT는 사용하지만 스프링 트랜잭션 관리를 원하지 **않는다면** 어떠한 스프링 트랜잭션 관리자를 설정해서도 **안되고** 마이바티스 `ManagedTransactionFactory`를 사용하기 위해 `SqlSessionFactoryBean`를 설정**해야만** 한다. @@ -83,10 +83,10 @@ public class MyBatisConfig { ## Programmatic Transaction Management -마이바티스 `SqlSession`은 트랜잭션을 제어하는 메서드를 제공한다. 하지만 마이바티스 스프링 연동모듈은 빈을 스프링이 관리하는 `SqlSession`이나 스프링이 관리하는 매퍼에 주입한다. +마이바티스 `SqlSession`은 트랜잭션을 제어하는 메서드를 제공한다. 하지만 마이바티스 스프링 연동 모듈은 빈을 스프링이 관리하는 `SqlSession`이나 스프링이 관리하는 매퍼에 주입한다. 이 말은 스프링이 **항상** 트랜잭션을 관리한다는 뜻이다. -스프링이 관리하는 `SqlSession`에서는 `SqlSession.commit()`, `SqlSession.rollback()` 또는 `SqlSession.close()` 메서드를 호출할수가 없다. +스프링이 관리하는 `SqlSession`에서는 `SqlSession.commit()`, `SqlSession.rollback()` 또는 `SqlSession.close()` 메서드를 호출할 수가 없다. 그럼에도 불구하고 이 메서드들을 사용하면 `UnsupportedOperationException` 예외가 발생한다. 이러한 메서드는 주입된 매퍼 클래스에서는 사용할 수 없다. JDBC연결의 자동커밋 설정을 어떻게 하더라도 스프링 트랜잭션 밖의 `SqlSession` 데이터 메서드나 매퍼 메서드의 실행은 자동으로 커밋된다. diff --git a/src/site/ko/markdown/using-api.md b/src/site/ko/markdown/using-api.md index 3894e1d396..e686106e00 100644 --- a/src/site/ko/markdown/using-api.md +++ b/src/site/ko/markdown/using-api.md @@ -21,7 +21,7 @@ public class UserDaoImpl implements UserDao { } ``` -이 방법은 **신중히** 사용하자. 왜냐하면 잘못사용하면 런타임 에러나 데이터 문제등을 야기할수 있기 때문이다. API를 직접 사용할때는 다음의 규칙들에 유의해야 한다. +이 방법은 **신중히** 사용하자. 왜냐하면 잘못 사용하면 런타임 에러나 데이터 문제 등을 야기할 수 있기 때문이다. API를 직접 사용할 때는 다음의 규칙들에 유의해야 한다. * 스프링 트랜잭션에 **속하지 않고** 별도의 트랜잭션에서 동작한다. * `SqlSession`이 스프링 트랜잭션 관리자가 사용하는 `DataSource`를 사용하고 이미 트랜잭션이 동작하고 있다면 이 코드는 예외를 **발생시킬 것이다**. diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index bdfcad162f..8f9f49ae10 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -49,7 +49,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/markdown/mappers.md b/src/site/markdown/mappers.md index ad365ff92f..79a6582e10 100644 --- a/src/site/markdown/mappers.md +++ b/src/site/markdown/mappers.md @@ -135,7 +135,7 @@ By default, these two properties are null, so all interfaces in the given base p Discovered mappers will be named using Spring default naming strategy for autodetected components (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)). That is, if no annotation is found, it will use the uncapitalized non-qualified class name of the mapper. But if either a `@Component` or a JSR-330 `@Named` annotation is found it will get the name from the annotation. -Notice that you can set the `annotation` attribute to `org.springframework.stereotype.Component`, `javax.inject.Named` (if you have JSE 6) or to your own annotation (that must be itself annotated) so the annotation will work both as a marker and as a name provider. +Notice that you can set the `annotation` attribute to `org.springframework.stereotype.Component`, `jakarta.inject.Named` (if you have JakartaEE) or to your own annotation (that must be itself annotated) so the annotation will work both as a marker and as a name provider. NOTE `` won't be able to scan and register mappers. Mappers are interfaces and, in order to register them to Spring, the scanner must know how to create a `MapperFactoryBean` for each interface it finds. diff --git a/src/site/resources/zh/css/site.css b/src/site/resources/zh_CN/css/site.css similarity index 95% rename from src/site/resources/zh/css/site.css rename to src/site/resources/zh_CN/css/site.css index b3f440195c..c604f39360 100644 --- a/src/site/resources/zh/css/site.css +++ b/src/site/resources/zh_CN/css/site.css @@ -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. diff --git a/src/site/site.xml b/src/site/site.xml index 2c5f8e5ef5..7f2132dc36 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml new file mode 100644 index 0000000000..8dcbac7aa0 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml new file mode 100644 index 0000000000..37cf7c18e1 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml new file mode 100644 index 0000000000..09ec80551d --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml new file mode 100644 index 0000000000..918b0daf1a --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml new file mode 100644 index 0000000000..906765847e --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml new file mode 100644 index 0000000000..08a6d8aa07 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml new file mode 100644 index 0000000000..b479e0bed6 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml new file mode 100644 index 0000000000..145a6cd829 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml new file mode 100644 index 0000000000..bce32a0c58 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml new file mode 100644 index 0000000000..c6ab57a115 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml new file mode 100644 index 0000000000..2f82ca529d --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/default.properties b/src/test/resources/org/mybatis/spring/filter/xml/default.properties new file mode 100644 index 0000000000..6effd30b64 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/default.properties @@ -0,0 +1,19 @@ +# +# Copyright 2010-2025 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +filter.custom=org.mybatis.spring.filter.customfilter.CustomTypeFilter +filter.aspectj=*..CommonDataSourceMapper +filter.regex=org\.mybatis\.spring\.filter\.datasource\.datasource1\..* diff --git a/src/test/java/org/mybatis/spring/mybatis-config.xml b/src/test/resources/org/mybatis/spring/mybatis-config.xml similarity index 95% rename from src/test/java/org/mybatis/spring/mybatis-config.xml rename to src/test/resources/org/mybatis/spring/mybatis-config.xml index d62326a516..4abec86943 100644 --- a/src/test/java/org/mybatis/spring/mybatis-config.xml +++ b/src/test/resources/org/mybatis/spring/mybatis-config.xml @@ -1,7 +1,7 @@
+ * 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 extends MapperFactoryBean> 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 @@ -55,7 +55,7 @@ public class BatchAppConfig { } ``` -**좀더 복잡한 예제를 보자.** +**좀 더 복잡한 예제를 보자.** ```xml @@ -246,16 +246,16 @@ public class BatchAppConfig { ``` -**여러개의 테이블에 데이터를 쓰려면 한꺼번에 처리할 수 있도록 만든 writer(몇가지 규칙을 가지고)를 사용하자.** +**여러 개의 테이블에 데이터를 쓰려면 한꺼번에 처리할 수 있도록 만든 writer(몇 가지 규칙을 가지고)를 사용하자.** 이 기능은 마이바티스 3.2이상에서만 사용할 수 있다. 이전의 버전에서는 예상과 다르게 동작하는데 그 내용은 [이슈](http://code.google.com/p/mybatis/issues/detail?id=741)를 참고하면 된다. -배치가 관계를 가지는 데이터나 여러개의 데이터베이스를 다루는 것처럼 복잡한 데이터를 작성할때 필요하다면 insert구문이 한개에 테이블에만 데이터를 넣을수 있다는 사실만 피하면 가능하기도 하다. +배치가 관계를 가지는 데이터나 여러 개의 데이터베이스를 다루는 것처럼 복잡한 데이터를 작성할 때 필요하다면 insert구문이 한 개에 테이블에만 데이터를 넣을 수 있다는 사실만 피하면 가능하기도 하다. 이런 복잡한 데이터를 처리하기 위해 writer가 작성하는 아이템(Item)을 준비해야 한다. 다음의 기술을 사용하면 단순한 관계를 가진 데이터나 관계가 없는 테이블을 처리하는 아이템에서 사용할 수 있다. -이러한 방법으로 스프링 배치 아이템은 모든 레코드를 *다룰것이다*. -여기에는 1:1 *관계*를 가지는 *InteractionMetadata*, 관계가 없는 두개의 로우는 *VisitorInteraction* 와 *CustomerInteraction*이 있다. -이 각각의 객체는 다음과 같이 볼수 있다. +이러한 방법으로 스프링 배치 아이템은 모든 레코드를 *다룰 것이다*. +여기에는 1:1 *관계*를 가지는 *InteractionMetadata*, 관계가 없는 두 개의 로우는 *VisitorInteraction* 와 *CustomerInteraction*이 있다. +이 각각의 객체는 다음과 같이 볼 수 있다. ```java public class InteractionRecordToWriteInMultipleTables { @@ -271,7 +271,7 @@ public class Interaction { } ``` -그리고 스프링 설정에는 각각의 레코드를 처리하기위해 특별히 설정된 전용(delegates) writer를 사용하는 `CompositeItemWriter`가 있다. +그리고 스프링 설정에는 각각의 레코드를 처리하기 위해 특별히 설정된 전용(delegates) writer를 사용하는 `CompositeItemWriter`가 있다. ```xml @@ -304,7 +304,7 @@ public class BatchAppConfig { } ``` -각각의 전용(delegate) writer는 필요할 만큼 설정할 수 있다. 예를들면 *Interaction* 과 *InteractionMetadata*를 위한 writer가 있다. +각각의 전용(delegate) writer는 필요할 만큼 설정할 수 있다. 예를 들면 *Interaction* 과 *InteractionMetadata*를 위한 writer가 있다. ```xml ``` -reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 가리킬수 있다. +reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 가리킬 수 있다. 매퍼 파일에서 구문은 다음과 같은 방법으로 각각의 레코드를 위해 만들어져있다. @@ -345,9 +345,9 @@ reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 ``` -먼저 `insertInteractionMetadata`가 호출될것이고 update구문은 jdbc드라이버에 의해(`keyProperty` 와 `keyColumn`) 생성된 아이디들을 리턴하기 위해 설정되었다. -`InteractionMetadata` 객체가 이 쿼리에 의해 업데이트되면 다음의 쿼리는 `insertInteraction`를 통해 상위객체인 `Interaction`를 작성하기 위해 사용될수 있다. +먼저 `insertInteractionMetadata`가 호출될 것이고 update구문은 jdbc드라이버에 의해(`keyProperty` 와 `keyColumn`) 생성된 아이디들을 리턴하기 위해 설정되었다. +`InteractionMetadata` 객체가 이 쿼리에 의해 업데이트되면 다음의 쿼리는 `insertInteraction`를 통해 상위객체인 `Interaction`를 작성하기 위해 사용될 수 있다. -***방금 언급한 내용에 관련하여 JDBC드라이버가 똑같이 동작하지 않을수 있다. +***방금 언급한 내용에 관련하여 JDBC드라이버가 똑같이 동작하지 않을 수 있다. 이 글을 쓰는 시점에 H2 드라이버 1.3.168버전(`org.h2.jdbc.JdbcStatement#getGeneratedKeys`를 보라)만 배치모드에서 마지막 인덱스를 리턴한다. 반면에 MySQL 드라이버는 기대한 것과 동일하게 동작하고 모든 아이디를 리턴한다.*** diff --git a/src/site/ko/markdown/factorybean.md b/src/site/ko/markdown/factorybean.md index e0a79081ad..4c8cff57b4 100644 --- a/src/site/ko/markdown/factorybean.md +++ b/src/site/ko/markdown/factorybean.md @@ -2,11 +2,11 @@ # SqlSessionFactoryBean 마이바티스만 사용하면, `SqlSessionFactory`는 `SqlSessionFactoryBuilder`를 사용해서 생성한다. -마이바티스 스프링 연동모듈에서는, `SqlSessionFactoryBean`가 대신 사용된다. +마이바티스 스프링 연동 모듈에서는, `SqlSessionFactoryBean`가 대신 사용된다. ## 설정 -팩토리 빈을 생성하기 위해, 스프링 XML설정파일에 다음설정을 추가하자. +팩토리 빈을 생성하기 위해, 스프링 XML 설정 파일에 다음 설정을 추가하자. ```xml @@ -31,23 +31,23 @@ public class MyBatisConfig { ``` 일반적인 마이바티스 스프링 사용법에서는, `SqlSessionFactoryBean`이나 관련된 `SqlSessionFactory`를 직접 사용할 필요가 없다. -대신 세션 팩토리가 `MapperFactoryBean`나 `SqlSessionDaoSupport`를 확장하는 다른 DAO에 주입될것이다. +대신 세션 팩토리가 `MapperFactoryBean`나 `SqlSessionDaoSupport`를 확장하는 다른 DAO에 주입될 것이다. ## 속성 `SqlSessionFactory`는 JDBC `DataSource`의 필수 프로퍼티가 필요하다. 어떤 `DataSource`라도 상관없고 다른 스프링 데이터베이스 연결처럼 설정되어야만 한다. -하나의 공통적인 프로퍼티는 마이바티스 XML설정파일의 위치를 지정하기 위해 사용되는 `configLocation`이다. 이 프로퍼티를 설정하는 것은 디폴트 설정을 가진 마이바티스 설정을 변경해야 할 경우 뿐이다. +하나의 공통적인 프로퍼티는 마이바티스 XML 설정 파일의 위치를 지정하기 위해 사용되는 `configLocation`이다. 이 프로퍼티를 설정하는 것은 디폴트 설정을 가진 마이바티스 설정을 변경해야 할 경우 뿐이다. 대개는 ``과 `` 섹션을 변경하는 경우이다. -설정파일이 마이바티스 설정을 완전히 다룰 필요는 없다. 어떤 환경, 어떤 데이터소스 그리고 마이바티스 트랜잭션 관리자가 **무시**될수도 있다. +설정 파일이 마이바티스 설정을 완전히 다룰 필요는 없다. 어떤 환경, 어떤 데이터소스 그리고 마이바티스 트랜잭션 관리자가 **무시**될 수도 있다. `SqlSessionFactoryBean` 는 필요에 따라 이 값들을 설정하여 자체적인 MyBatis `Environment` 를 만든다. -설정파일이 필요한 다른 이유는 마이바티스 XML파일이 매퍼 클래스와 동일한 클래스패스에 있지 않은 경우이다. 이 설정을 사용하면 두가지 옵션이 있다. -첫번째는 마이바티스 설정파일에 `` 섹션을 사용해서 XML파일의 클래스패스를 지정하는 것이다. 두번째는 팩토리 빈의 `mapperLocations` 프로퍼티를 사용하는 것이다. +설정 파일이 필요한 다른 이유는 마이바티스 XML파일이 매퍼 클래스와 동일한 클래스패스에 있지 않은 경우이다. 이 설정을 사용하면 두 가지 옵션이 있다. +첫 번째는 마이바티스 설정파일에 `` 섹션을 사용해서 XML파일의 클래스패스를 지정하는 것이다. 두 번째는 팩토리 빈의 `mapperLocations` 프로퍼티를 사용하는 것이다. `mapperLocations` 프로퍼티는 매퍼에 관련된 자원의 위치를 나열한다. 이 프로퍼티는 마이바티스의 XML매퍼 파일들의 위치를 지정하기 위해 사용될 수 있다. -디렉터리 아래 모든 파일을 로드하기 위해 앤트(Ant) 스타일의 패턴을 사용할수도 있고 가장 상위 위치를 지정하는 것으로 재귀적으로 하위 경로를 찾도록 할수도 있다. 예를 들어보면 다음과 같다. +디렉터리 아래 모든 파일을 로드하기 위해 앤트(Ant) 스타일의 패턴을 사용할 수도 있고 가장 상위 위치를 지정하는 것으로 재귀적으로 하위 경로를 찾도록 할 수도 있다. 예를 들어보면 다음과 같다. ```xml @@ -58,7 +58,7 @@ public class MyBatisConfig { 이 설정은 `sample.config.mappers` 패키지 아래와 그 하위 패키지를 모두 검색해서 마이바티스 매퍼 XML파일을 모두 로드할 것이다. -컨테이너 관리 트랜잭션을 사용하는 환경에서 필요한 하나의 프로퍼티는 `transactionFactoryClass` 이다. 이에 관련해서는 트랜잭션을 다루는 장에서 볼수 있다. +컨테이너 관리 트랜잭션을 사용하는 환경에서 필요한 하나의 프로퍼티는 `transactionFactoryClass` 이다. 이에 관련해서는 트랜잭션을 다루는 장에서 볼 수 있다. 만약 multi-db 기능을 사용한다면 다음과 같이 `databaseIdProvider` 속성을 설정해야 한다. @@ -83,7 +83,7 @@ public class MyBatisConfig { ```` NOTE -1.3.0 버전 부터 `configuration` 속성이 추가되었다. 다음과 같이 MyBatis XML 설정 파일없이 `Configuration` 인스턴스를 직접 지정할 수 있습니다. +1.3.0 버전부터 `configuration` 속성이 추가되었다. 다음과 같이 MyBatis XML 설정 파일 없이 `Configuration` 인스턴스를 직접 지정할 수 있습니다. ```xml diff --git a/src/site/ko/markdown/getting-started.md b/src/site/ko/markdown/getting-started.md index 9547c6f39a..553dda2ba5 100644 --- a/src/site/ko/markdown/getting-started.md +++ b/src/site/ko/markdown/getting-started.md @@ -1,11 +1,11 @@ # 시작하기 -이 장은 마이바티스 스프링 연동모듈을 설치하고 셋팅하는 방법에 대해 간단히 보여준다. 그리고 트랜잭션을 사용하는 간단한 애플리케이션을 만드는 방법까지 다룰 것이다. +이 장은 마이바티스 스프링 연동 모듈을 설치하고 셋팅하는 방법에 대해 간단히 보여준다. 그리고 트랜잭션을 사용하는 간단한 애플리케이션을 만드는 방법까지 다룰 것이다. ## 설치 -마이바티스 스프링 연동모듈을 사용하기 위해서, 클래스패스에 `mybatis-spring-${project.version}.jar`를 포함시켜야 한다. +마이바티스 스프링 연동 모듈을 사용하기 위해서, 클래스패스에 `mybatis-spring-${project.version}.jar`를 포함시켜야 한다. 메이븐을 사용하고 있다면 pom.xml에 다음처럼 의존성을 추가하면 된다. @@ -19,10 +19,10 @@ ## 빠른 설정 -마이바티스를 스프링과 함께 사용하려면 스프링의 애플리케이션 컨텍스트에 적어도 두개를 정의해줄 필요가 있다. -두가지는 `SqlSessionFactory`와 한개 이상의 매퍼 인터페이스이다. +마이바티스를 스프링과 함께 사용하려면 스프링의 애플리케이션 컨텍스트에 적어도 두 개를 정의해 줄 필요가 있다. +두 가지는 `SqlSessionFactory`와 한 개 이상의 매퍼 인터페이스이다. -마이바티스 스프링 연동모듈에서, `SqlSessionFactoryBean`은 `SqlSessionFactory`를 만들기 위해 사용된다. 팩토리 빈을 설정하기 위해, 스프링 설정파일에 다음 설정을 추가하자. +마이바티스 스프링 연동 모듈에서, `SqlSessionFactoryBean`은 `SqlSessionFactory`를 만들기 위해 사용된다. 팩토리 빈을 설정하기 위해, 스프링 설정 파일에 다음 설정을 추가하자. ```xml @@ -62,10 +62,10 @@ UserMapper인터페이스는 다음처럼 `MapperFactoryBean`을 사용해서 ``` -매퍼는 **반드시** 구현체 클래스가 아닌 인터페이스로 정의되어야 한다. 예를들어, 애노테이션이 SQL을 명시하기 위해 사용되지만 마이바티스 매퍼 XML파일 또한 사용될 수 있다. +매퍼는 **반드시** 구현체 클래스가 아닌 인터페이스로 정의되어야 한다. 예를 들어, 애노테이션이 SQL을 명시하기 위해 사용되지만 마이바티스 매퍼 XML파일 또한 사용될 수 있다. -한번만 설정하면, 다른 스프링 빈에 주입하는 같은 방법으로 비즈니스/서비스 객체에 매퍼를 직접 주입할 수 있다. `MapperFactoryBean`은 `SqlSession`을 생성하고 닫는 작업을 잘 다룬다. -실행중인 스프링 트랜잭션이 있다면, 트랜잭션이 완료되는 시점에 커밋이나 롤백이 될 것이다. 마지막으로 예외가 발생하면 스프링의 `DataAccessException`예외가 발생한다. +한 번만 설정하면, 다른 스프링 빈에 주입하는 같은 방법으로 비즈니스/서비스 객체에 매퍼를 직접 주입할 수 있다. `MapperFactoryBean`은 `SqlSession`을 생성하고 닫는 작업을 잘 다룬다. +실행 중인 스프링 트랜잭션이 있다면, 트랜잭션이 완료되는 시점에 커밋이나 롤백이 될 것이다. 마지막으로 예외가 발생하면 스프링의 `DataAccessException`예외가 발생한다. 자바로 설정하면 다음과 같다. diff --git a/src/site/ko/markdown/index.md b/src/site/ko/markdown/index.md index 83e5567550..a43f6578aa 100644 --- a/src/site/ko/markdown/index.md +++ b/src/site/ko/markdown/index.md @@ -3,18 +3,18 @@ ## MyBatis-Spring 은 무엇일까? -마이바티스 스프링 연동모듈은 마이바티스와 스프링을 편하고 간단하게 연동한다. 이 모듈은 마이바티스로 하여금 스프링 트랜잭션에 쉽게 연동되도록 처리한다. 게다가 마이바티스 매퍼와 `SqlSession`을 다루고 다른 빈에 주입시켜준다. -마이바티스 예외를 스프링의 `DataAccessException`로 변환하기도 하고 마이바티스, 스프링 또는 마이바티스 스프링 연동모듈에 의존성을 없애기도 한다. +마이바티스 스프링 연동 모듈은 마이바티스와 스프링을 편하고 간단하게 연동한다. 이 모듈은 마이바티스로 하여금 스프링 트랜잭션에 쉽게 연동되도록 처리한다. 게다가 마이바티스 매퍼와 `SqlSession`을 다루고 다른 빈에 주입시켜준다. +마이바티스 예외를 스프링의 `DataAccessException`로 변환하기도 하고 마이바티스, 스프링 또는 마이바티스 스프링 연동 모듈에 의존성을 없애기도 한다. ## 동기 부여 -스프링 2.x은 아이바티스 2.x만을 지원한다. 스프링 3.x에서 마이바티스 3.x를 지원하기 위한 시도가 진행중이다. (스프링의 이슈관리 시스템인 [이슈](https://jira.springsource.org/browse/SPR-5991) 를 보라.) -불행하게도 스프링 3의 개발이 마이바티스 3.0의 정식릴리즈전에 개발이 완료되었다. 그래서 스프링팀은 릴리즈가 안된 마이바티스 코드를 함께 릴리즈하는 것을 원하지 않았고 실제적인 스프링 지원을 기다릴수밖에 없었다. +스프링 2.x은 아이바티스 2.x만을 지원한다. 스프링 3.x에서 마이바티스 3.x를 지원하기 위한 시도가 진행 중이다. (스프링의 이슈관리 시스템인 [이슈](https://jira.springsource.org/browse/SPR-5991) 를 보라.) +불행하게도 스프링 3의 개발이 마이바티스 3.0의 정식 릴리즈 전에 개발이 완료되었다. 그래서 스프링팀은 릴리즈가 안 된 마이바티스 코드를 함께 릴리즈하는 것을 원하지 않았고 실제적인 스프링 지원을 기다릴 수밖에 없었다. 스프링의 마이바티스 지원에 대한 관심으로 인해, 마이바티스 커뮤니티는 재결합하는 형태로 결정을 내고 대신 마이바티스의 하위 프로젝트 형태로 스프링 연동 프로젝트를 추가한다. ## 필요 조건 -마이바티스 스프링 연동을 시작하기 전에, 마이바티스와 스프링의 용어를 맞추는 일이 굉장히 중요했다. 이 문서는 배경지식이나 기본적인 셋업방법 그리고 마이바티스와 스프링의 설정에 대한 튜토리얼등은 제공하지 않는다. +마이바티스 스프링 연동을 시작하기 전에, 마이바티스와 스프링의 용어를 맞추는 일이 굉장히 중요했다. 이 문서는 배경지식이나 기본적인 셋업방법 그리고 마이바티스와 스프링의 설정에 대한 튜토리얼 등은 제공하지 않는다. MyBatis-Spring requires following versions: @@ -28,9 +28,9 @@ MyBatis-Spring requires following versions: ## 감사 인사 이 프로젝트가 실제로 만들어지게 도와준 모든 특별한 분들에게 정말 감사한다. -알파벳 순서로 보면, 코딩및 테스트 그리고 문서화를 담당했던 Eduardo Macarron, Hunter Presnall, Putthiphong Boonphong; +알파벳 순서로 보면, 코딩 및 테스트 그리고 문서화를 담당했던 Eduardo Macarron, Hunter Presnall, Putthiphong Boonphong; 그외 다양한 프로젝트 기여자인 Andrius Juozapaitis, Giovanni Cuccu, Mike Lanyon, Raj Nagappan, Tomas Pinos; -그리고 마이바티스에 하위 프로젝트로 가져올수 있도록 많은 것을 찾아준 Simone Tripodi 에게 감사한다. ;) +그리고 마이바티스에 하위 프로젝트로 가져올 수 있도록 많은 것을 찾아준 Simone Tripodi 에게 감사한다. ;) 이들이 없었다면 이 프로젝트는 존재하지 않았을 것이다. ## 이 문서가 더 나아지도록 도와주세요… @@ -39,16 +39,16 @@ MyBatis-Spring requires following versions: 이 문서의 원본은 markdown 포맷이며 [프로젝트의 Git](https://github.com/mybatis/spring/tree/master/src/site)에서 찾을 수 있다. repository 를 fork 하고, 업데이트하고 pull request 를 보내주십시오. -당신처럼 이 문서를 읽는 사람들에게 이 문서의 최고의 저자가 될수 있다! +당신처럼 이 문서를 읽는 사람들에게 이 문서의 최고의 저자가 될 수 있다! ## 번역 -사용자들은 다음의 번역문서별로 마이바티스 스프링 연동모듈에 대해 알수 있다. +사용자들은 다음의 번역 문서별로 마이바티스 스프링 연동 모듈에 대해 알 수 있다. English Español - 简体中文 + 简体中文 日本語 한국어 diff --git a/src/site/ko/markdown/mappers.md b/src/site/ko/markdown/mappers.md index c56ba731ff..d537db1e65 100644 --- a/src/site/ko/markdown/mappers.md +++ b/src/site/ko/markdown/mappers.md @@ -1,7 +1,7 @@ # 매퍼 주입 -`SqlSessionDaoSupport` 나 `SqlSessionTemplate` 를 직접적으로 사용하는 데이터 접근 객체(DAO)를 생성하기 보다, 마이바티스 스프링 연동모듈은 다른 빈에 직접 주입할 수 있는 쓰레드에 안전한 매퍼를 생성할 수 있다. +`SqlSessionDaoSupport` 나 `SqlSessionTemplate` 를 직접적으로 사용하는 데이터 접근 객체(DAO)를 생성하기보다, 마이바티스 스프링 연동 모듈은 다른 빈에 직접 주입할 수 있는 쓰레드에 안전한 매퍼를 생성할 수 있다. ```xml @@ -9,7 +9,7 @@ ``` -한번 주입하고나면 매퍼는 애플리케이션 로직에서 사용할수 있는 준비가 된다. +한번 주입하고 나면 매퍼는 애플리케이션 로직에서 사용할 수 있는 준비가 된다. ```java public class FooServiceImpl implements FooService { @@ -26,7 +26,7 @@ public class FooServiceImpl implements FooService { } ``` -이 코드를 보면 `SqlSession`이나 마이바티스 객체가 보이지 않는다. 게다가 세션을 생성하거나 열고 닫을필요도 없어보인다. 마이바티스 스프링 연동모듈이 알아서 처리할 것이다. +이 코드를 보면 `SqlSession`이나 마이바티스 객체가 보이지 않는다. 게다가 세션을 생성하거나 열고 닫을 필요도 없어 보인다. 마이바티스 스프링 연동 모듈이 알아서 처리할 것이다. ## 매퍼 등록하기 @@ -35,7 +35,7 @@ public class FooServiceImpl implements FooService { ### XML설정 사용 -매퍼는 다음처럼 XML설정파일에 `MapperFactoryBean`을 두는 것으로 스프링에 등록된다. +매퍼는 다음처럼 XML설정 파일에 `MapperFactoryBean`을 두는 것으로 스프링에 등록된다. ```xml @@ -45,10 +45,10 @@ public class FooServiceImpl implements FooService { ``` UserMapper가 매퍼 인터페이스와 같은 경로의 클래스패스에 마이바티스 XML매퍼 파일을 가지고 있다면 `MapperFactoryBean`이 자동으로 파싱할것이다. -매퍼 XML파일을 다른 클래스패스에 두는게 아니라면 마이바티스 설정파일에 매퍼를 지정할 필요가 없다. 좀더 세부적인 정보는 `SqlSessionFactoryBean`의 [`configLocation`](factorybean.html) 프로퍼티를 살펴보자. +매퍼 XML파일을 다른 클래스패스에 두는게 아니라면 마이바티스 설정 파일에 매퍼를 지정할 필요가 없다. 좀 더 세부적인 정보는 `SqlSessionFactoryBean`의 [`configLocation`](factorybean.html) 프로퍼티를 살펴보자. `MapperFactoryBean`은 `SqlSessionFactory` 나 `SqlSessionTemplate`가 필요하다. `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅하면 된다. -둘다 셋팅하면 `SqlSessionFactory`가 무시된다. 세션 팩토리 셋은 `SqlSessionTemplate`이 필요하고 `MapperFactoryBean`는 팩토리를 사용할것이다. +둘 다 셋팅하면 `SqlSessionFactory`가 무시된다. 세션 팩토리 셋은 `SqlSessionTemplate`이 필요하고 `MapperFactoryBean`는 팩토리를 사용할 것이다. ### 자바설정 사용 @@ -67,15 +67,15 @@ public class MyBatisConfig { ## 매퍼 스캔 -하나씩 매퍼를 모두 등록할 필요가 없다. 대신 클래스패스를 지정해서 마이바티스 스프링 연동모듈의 자동스캔기능을 사용할 수 있다. +하나씩 매퍼를 모두 등록할 필요가 없다. 대신 클래스패스를 지정해서 마이바티스 스프링 연동 모듈의 자동 스캔 기능을 사용할 수 있다. -자동스캔을 사용하는데는 3가지 방법이 있다. +자동 스캔을 사용하는 데는 3가지 방법이 있다. * `` 엘리먼트 사용 * `@MapperScan` 애노테이션 사용 * 스프링 XML파일을 사용해서 `MapperScannerConfigurer`를 등록 -`` 와 `@MapperScan` 모두 마이바티스 스프링 연동모듈 1.2.0에서 추가된 기능이다. `@MapperScan` 은 스프링 버전이 3.1이상이어야 한다. +`` 와 `@MapperScan` 모두 마이바티스 스프링 연동 모듈 1.2.0에서 추가된 기능이다. `@MapperScan` 은 스프링 버전이 3.1이상이어야 한다. Since 2.0.2, mapper scanning feature support a option (`lazy-initialization`) that control lazy initialization enabled/disabled of mapper bean. The motivation for adding this option is supporting a lazy initialization control feature supported by Spring Boot 2.2. @@ -107,7 +107,7 @@ The `default-scope` apply to the mapper bean(`MapperFactoryBean`) when scope of `` XML엘리먼트는 스프링에서 제공하는 `` 엘리먼트와 매우 유사한 방법으로 매퍼를 검색할 것이다. -샘플 XML설정을 아래에서 볼수 있다. +샘플 XML설정을 아래에서 볼 수 있다. ```xml ``` -`base-package` 속성은 매퍼 인터페이스 파일이 있는 가장 상위 패키지를 지정하면 된다. 세미콜론이나 콤마를 구분자로 사용해서 한개 이상의 패키지를 셋팅할 수 있다. +`base-package` 속성은 매퍼 인터페이스 파일이 있는 가장 상위 패키지를 지정하면 된다. 세미콜론이나 콤마를 구분자로 사용해서 한 개 이상의 패키지를 셋팅할 수 있다. 매퍼는 지정된 패키지에서 재귀적으로 하위 패키지를 모두 검색할 것이다. ``이 자동으로 주입할 수 있는 `MapperFactoryBean`를 생성하기 때문에 `SqlSessionFactory` 나 `SqlSessionTemplate` 를 명시할 필요가 없다. -하지만 한개 이상의 `DataSource`를 사용한다면 자동주입이 생각한데로 동작하지 않을수도 있다. 이 경우 사용할 빈 이름을 지정하기 위해 `factory-ref` 나 `template-ref` 속성을 사용할수 있다. +하지만 한 개 이상의 `DataSource`를 사용한다면 자동 주입이 생각한 대로 동작하지 않을 수도 있다. 이 경우 사용할 빈 이름을 지정하기 위해 `factory-ref` 나 `template-ref` 속성을 사용할수 있다. -``은 마커(marker) 인터페이스나 애노테이션을 명시해서 생성되는 매퍼를 필터링할수도 있다. `annotation` 프로퍼티는 검색할 애노테이션을 지정한다. -`marker-interface` 프로퍼티는 검색할 상위 인터페이스를 지정한다. 이 두개의 프로퍼티를 모두 지정하면, 매퍼는 두 조건을 모두 만족하는 인터페이스만을 추가한다. -디폴트로 이 두가지 프로퍼티는 모두 null이다. 그래서 base-package프로퍼티에 설정된 패키지 아래 모든 인터페이스가 매퍼로 로드될 것이다. +``은 마커(marker) 인터페이스나 애노테이션을 명시해서 생성되는 매퍼를 필터링할 수 도 있다. `annotation` 프로퍼티는 검색할 애노테이션을 지정한다. +`marker-interface` 프로퍼티는 검색할 상위 인터페이스를 지정한다. 이 두 개의 프로퍼티를 모두 지정하면, 매퍼는 두 조건을 모두 만족하는 인터페이스만을 추가한다. +디폴트로 이 두 가지 프로퍼티는 모두 null이다. 그래서 base-package프로퍼티에 설정된 패키지 아래 모든 인터페이스가 매퍼로 로드될 것이다. -발견된 매퍼는 자동검색된 컴포넌트를 위한 스프링의 디폴트 명명규칙 전략(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) 을 사용해서 빈이름이 명명된다. -빈 이름을 정하는 애노테이션이 없다면 매퍼의 이름에서 첫글자를 소문자로 변환한 형태로 빈 이름을 사용할 것이다. `@Component` 나 JSR-330의 `@Named` 애노테이션이 있다면 애노테이션에 정의한 이름을 그대로 사용할 것이다. -`annotation` 프로퍼티를 `org.springframework.stereotype.Component`, `javax.inject.Named`(자바SE 1.6을 사용한다면) 또는 개발자가 스스로 작성한 애노테이션으로 셋팅할 수 있다. +발견된 매퍼는 자동 검색된 컴포넌트를 위한 스프링의 디폴트 명명 규칙 전략(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) 을 사용해서 빈 이름이 명명된다. +빈 이름을 정하는 애노테이션이 없다면 매퍼의 이름에서 첫 글자를 소문자로 변환한 형태로 빈 이름을 사용할 것이다. `@Component` 나 JSR-330의 `@Named` 애노테이션이 있다면 애노테이션에 정의한 이름을 그대로 사용할 것이다. +`annotation` 프로퍼티를 `org.springframework.stereotype.Component`, `jakarta.inject.Named`(Jakarta EE을 사용한다면) 또는 개발자가 스스로 작성한 애노테이션으로 셋팅할 수 있다. 그러면 애노테이션은 마커와 이름을 제공하는 역할로 동작할 것이다. 중요 -`` 가 매퍼를 검색해서 등록을 하지 못할수도 있다. 매퍼는 인터페이스고 스프링에 빈으로 등록하기 위해서는 각각의 인터페이스를 찾기 위해 스캐너가 `MapperFactoryBean` 를 생성하는 방법을 알아야만 한다. +`` 가 매퍼를 검색해서 등록을 하지 못할 수도 있다. 매퍼는 인터페이스고 스프링에 빈으로 등록하기 위해서는 각각의 인터페이스를 찾기 위해 스캐너가 `MapperFactoryBean` 를 생성하는 방법을 알아야만 한다. ### @MapperScan -`@Configuration` 라고 불리는 스프링의 자바설정을 사용한다면 ``보다는 `@MapperScan`를 사용하길 선호할것이다. +`@Configuration` 라고 불리는 스프링의 자바 설정을 사용한다면 ``보다는 `@MapperScan`를 사용하길 선호할 것이다. `@MapperScan` 애노테이션은 다음처럼 사용된다. @@ -172,13 +172,13 @@ Since 2.0.4, If `basePackageClasses` or `basePackages` are not defined, scanning ``` -`sqlSessionFactory` 나 `sqlSessionTemplate`를 지정할 필요가 있다면 빈참조가 아닌 **빈이름**이 필요하다. `value` 프로퍼티는 빈 이름을 지정하고 `ref` 는 빈 참조를 지정하기 때문에 `value` 프로퍼티를 사용하자. +`sqlSessionFactory` 나 `sqlSessionTemplate`를 지정할 필요가 있다면 빈 참조가 아닌 **빈 이름**이 필요하다. `value` 프로퍼티는 빈 이름을 지정하고 `ref` 는 빈 참조를 지정하기 때문에 `value` 프로퍼티를 사용하자. ```xml ``` 중요 -`sqlSessionFactoryBean` 과 `sqlSessionTemplateBean` 프로퍼티는 마이바티스 스프링 연동모듈 1.0.2 버전 이상에서만 사용이 가능하다. +`sqlSessionFactoryBean` 과 `sqlSessionTemplateBean` 프로퍼티는 마이바티스 스프링 연동 모듈 1.0.2 버전 이상에서만 사용이 가능하다. 하지만 `MapperScannerConfigurer`는 잦은 에러를 발생시키는 `PropertyPlaceholderConfigurer`보다 앞서 실행되기 때문에 이 프로퍼티들은 사용하지 말길 바란다(deprecated). 대신 새롭게 추가된 프로퍼티인 `sqlSessionFactoryBeanName` 과 `sqlSessionTemplateBeanName` 를 사용하도록 권한다. diff --git a/src/site/ko/markdown/sqlsession.md b/src/site/ko/markdown/sqlsession.md index 783f42df0f..d0e3458ac8 100644 --- a/src/site/ko/markdown/sqlsession.md +++ b/src/site/ko/markdown/sqlsession.md @@ -1,21 +1,21 @@ # SqlSession 사용 -마이바티스에서는 `SqlSession`를 생성하기 위해 `SqlSessionFactory`를 사용한다. 세션을 한번 생성하면 매핑구문을 실행하거나 커밋 또는 롤백을 하기 위해 세션을 사용할수 있다. +마이바티스에서는 `SqlSession`를 생성하기 위해 `SqlSessionFactory`를 사용한다. 세션을 한번 생성하면 매핑구문을 실행하거나 커밋 또는 롤백을 하기 위해 세션을 사용할 수 있다. 마지막으로 더 이상 필요하지 않은 상태가 되면 세션을 닫는다. 마이바티스 스프링 연동모듈을 사용하면 `SqlSessionFactory`를 직접 사용할 필요가 없다. 왜냐하면, 스프링 트랜잭션 설정에 따라 자동으로 커밋 혹은 롤백을 수행하고 닫혀지는, 쓰레드에 안전한 `SqlSession` 개체가 스프링 빈에 주입될 수 있기 때문이다. # SqlSessionTemplate -`SqlSessionTemplate`은 마이바티스 스프링 연동모듈의 핵심이다. `SqlSessionTemplate`은 `SqlSession`을 구현하고 코드에서 `SqlSession`를 대체하는 역할을 한다. -`SqlSessionTemplate` 은 쓰레드에 안전하고 여러개의 DAO나 매퍼에서 공유할수 있다. +`SqlSessionTemplate`은 마이바티스 스프링 연동 모듈의 핵심이다. `SqlSessionTemplate`은 `SqlSession`을 구현하고 코드에서 `SqlSession`를 대체하는 역할을 한다. +`SqlSessionTemplate` 은 쓰레드에 안전하고 여러 개의 DAO나 매퍼에서 공유할 수 있다. -`getMapper()`에 의해 리턴된 매퍼가 가진 메서드를 포함해서 SQL을 처리하는 마이바티스 메서드를 호출할때 `SqlSessionTemplate`은 `SqlSession`이 현재의 스프링 트랜잭션에서 사용될수 있도록 보장한다. -추가적으로 `SqlSessionTemplate`은 필요한 시점에 세션을 닫고, 커밋하거나 롤백하는 것을 포함한 세션의 생명주기를 관리한다. 또한 마이바티스 예외를 스프링의 `DataAccessException`로 변환하는 작업또한 처리한다. +`getMapper()`에 의해 리턴된 매퍼가 가진 메서드를 포함해서 SQL을 처리하는 마이바티스 메서드를 호출할 때 `SqlSessionTemplate`은 `SqlSession`이 현재의 스프링 트랜잭션에서 사용될 수 있도록 보장한다. +추가적으로 `SqlSessionTemplate`은 필요한 시점에 세션을 닫고, 커밋하거나 롤백하는 것을 포함한 세션의 생명주기를 관리한다. 또한 마이바티스 예외를 스프링의 `DataAccessException`로 변환하는 작업 또한 처리한다. `SqlSessionTemplate`은 마이바티스의 디폴트 구현체인 `DefaultSqlSession` 대신 **항상** 사용된다. -왜냐하면 템플릿은 스프링 트랜잭션의 일부처럼 사용될 수 있고 여러개 주입된 매퍼 클래스에 의해 사용되도록 쓰레드에 안전하다. -동일한 애플리케이션에서 두개의 클래스간의 전환은 데이터 무결성 이슈를 야기할수 있다. +왜냐하면 템플릿은 스프링 트랜잭션의 일부처럼 사용될 수 있고 여러 개 주입된 매퍼 클래스에 의해 사용되도록 쓰레드에 안전하다. +동일한 애플리케이션에서 두 개의 클래스간 전환은 데이터 무결성 이슈를 야기할 수 있다. `SqlSessionTemplate`은 생성자 인자로 `SqlSessionFactory`를 사용해서 생성될 수 있다. @@ -59,7 +59,7 @@ public class UserDaoImpl implements UserDao { ``` -`SqlSessionTemplate`은 인자로 `ExecutorType`를 가지는 생성자를 가지고 있다. 이 인자는 예를들면 스프링 설정 XML을 다음처럼 설정해서 배치형태의 `SqlSession`를 만들수도 있다. +`SqlSessionTemplate`은 인자로 `ExecutorType`를 가지는 생성자를 가지고 있다. 이 인자는 예를 들면 스프링 설정 XML을 다음처럼 설정해서 배치형태의 `SqlSession`를 만들 수도 있다. ```xml @@ -94,14 +94,14 @@ public class UserService { } ``` -이러한 설정형태는 `SqlSessionFactory`의 디폴트 형태가 아닌 다른형태로 메서드를 실행해야 할때만 사용할 필요가 있다. +이러한 설정 형태는 `SqlSessionFactory`의 디폴트 형태가 아닌 다른 형태로 메서드를 실행해야 할 때만 사용할 필요가 있다. -이러한 형태에 대해 굳이 경로를 하자면 메서드를 호출할때 `ExecutorType`이 다르면 이미 시작된 트랜잭션을 사용하지 **못할**것이다. -다른 실행자(executor) 타입을 사용할때는 `SqlSessionTemplate`의 메서드를 구분된 트랜잭션(`PROPAGATION_REQUIRES_NEW`를 사용하는)이나 트랜잭션 외부에서 호출하는지 확실히해야 한다. +이러한 형태에 대해 굳이 경로를 하자면 메서드를 호출할 때 `ExecutorType`이 다르면 이미 시작된 트랜잭션을 사용하지 **못할**것이다. +다른 실행자(executor) 타입을 사용할 때는 `SqlSessionTemplate`의 메서드를 구분된 트랜잭션(`PROPAGATION_REQUIRES_NEW`를 사용하는)이나 트랜잭션 외부에서 호출하는지 확실히 해야 한다. ## SqlSessionDaoSupport -`SqlSessionDaoSupport`는 `SqlSession`을 제공하는 추상클래스이다. `getSqlSession()`메서드를 호출해서 다음처럼 SQL을 처리하는 마이바티스 메서드를 호출하기 위해 사용할 `SqlSessionTemplate`을 얻을 수 있다. +`SqlSessionDaoSupport`는 `SqlSession`을 제공하는 추상 클래스이다. `getSqlSession()`메서드를 호출해서 다음처럼 SQL을 처리하는 마이바티스 메서드를 호출하기 위해 사용할 `SqlSessionTemplate`을 얻을 수 있다. ```java public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { @@ -111,9 +111,9 @@ public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { } ``` -대개 `MapperFactoryBean`은 추가적인 코드가 필요없기 때문에 이 클래스를 선호한다. 하지만 DAO에서 마이바티스가 필요하지 않고 구현된 클래스가 필요하지 않을때만 유용하다. +대개 `MapperFactoryBean`은 추가적인 코드가 필요 없기 때문에 이 클래스를 선호한다. 하지만 DAO에서 마이바티스가 필요하지 않고 구현된 클래스가 필요하지 않을 때만 유용하다. -`SqlSessionDaoSupport`는 `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅할 필요가 있다. 두개의 프로퍼티를 모두 셋팅하면 `sqlSessionFactory`는 무시된다. +`SqlSessionDaoSupport`는 `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅할 필요가 있다. 두 개의 프로퍼티를 모두 셋팅하면 `sqlSessionFactory`는 무시된다. `SqlSessionDaoSupport`의 하위클래스인 `UserDaoImpl`가 있다고 하면 스프링에서는 다음처럼 설정될 수 있다. diff --git a/src/site/ko/markdown/transactions.md b/src/site/ko/markdown/transactions.md index df84e8861d..f7ad754ef7 100644 --- a/src/site/ko/markdown/transactions.md +++ b/src/site/ko/markdown/transactions.md @@ -1,13 +1,13 @@ # Transactions -마이바티스 스프링 연동모듈을 사용하는 중요한 이유중 하나는 마이바티스가 스프링 트랜잭션에 자연스럽게 연동될수 있다는 것이다. +마이바티스 스프링 연동 모듈을 사용하는 중요한 이유 중 하나는 마이바티스가 스프링 트랜잭션에 자연스럽게 연동될 수 있다는 것이다. 마이바티스에 종속되는 새로운 트랜잭션 관리를 만드는 것보다는 마이바티스 스프링 연동모듈이 스프링의 `DataSourceTransactionManager`과 융합되는 것이 좋다. -스프링 트랜잭션 관리자를 한번 설정하면, 대개의 경우처럼 스프링에서 트랜잭션을 설정할 수 있다. `@Transactional` 애노테이션과 AOP스타일의 설정 모두 지원한다. -하나의 `SqlSession`객체가 생성되고 트랜잭션이 동작하는 동안 지속적으로 사용될것이다. 세션은 트랜잭션이 완료되면 적절히 커밋이 되거나 롤백될것이다. +스프링 트랜잭션 관리자를 한 번 설정하면, 대개의 경우처럼 스프링에서 트랜잭션을 설정할 수 있다. `@Transactional` 애노테이션과 AOP스타일의 설정 모두 지원한다. +하나의 `SqlSession`객체가 생성되고 트랜잭션이 동작하는 동안 지속적으로 사용될 것이다. 세션은 트랜잭션이 완료되면 적절히 커밋이 되거나 롤백될것이다. -마이바티스 스프링 연동모듈은 한번 셋업되면 트랜잭션을 투명하게 관리한다. DAO클래스에 어떠한 추가적인 코드를 넣을 필요가 없다. +마이바티스 스프링 연동 모듈은 한 번 셋업되면 트랜잭션을 투명하게 관리한다. DAO클래스에 어떠한 추가적인 코드를 넣을 필요가 없다. ## 표준 설정 @@ -30,9 +30,9 @@ public class DataSourceConfig { } ``` -명시된 `DataSource`는 스프링을 사용할때 일반적으로 사용한다면 어떠한 JDBC `DataSource`도 될수 있다. JNDI룩업을 통해 얻어진 `DataSource`뿐 아니라 커넥션 풀링 기능도 포함한다. +명시된 `DataSource`는 스프링을 사용할 때 일반적으로 사용한다면 어떠한 JDBC `DataSource`도 될 수 있다. JNDI룩업을 통해 얻어진 `DataSource`뿐 아니라 커넥션 풀링 기능도 포함한다. -트랜잭션 관리자에 명시된 `DataSource`가 `SqlSessionFactoryBean`을 생성할때 사용된 것과 **반드시** 동일한 것이어야 하는 것만 꼭 기억하자. 그렇지 않으면 트랜잭션 관리가 제대로 되지 않을것이다. +트랜잭션 관리자에 명시된 `DataSource`가 `SqlSessionFactoryBean`을 생성할 때 사용된 것과 **반드시** 동일한 것이어야 하는 것만 꼭 기억하자. 그렇지 않으면 트랜잭션 관리가 제대로 되지 않을 것이다. ## Container Managed Transactions @@ -53,7 +53,7 @@ public class DataSourceConfig { } ``` -이 설정에서, 마이바티스는 CMT와 함께 설정된 스프링 트랜잭션 리소스처럼 동작할 것이다. 스프링은 이미 설정된 트랜잭션을 사용해서 `SqlSession`을 이미 동작중인 트랜잭션에 넣을 것이다. +이 설정에서, 마이바티스는 CMT와 함께 설정된 스프링 트랜잭션 리소스처럼 동작할 것이다. 스프링은 이미 설정된 트랜잭션을 사용해서 `SqlSession`을 이미 동작 중인 트랜잭션에 넣을 것이다. 시작된 트랜잭션이 없고 트랜잭션이 필요한 경우라면 스프링은 새로운 컨테이너 관리 트랜잭션을 시작할 것이다. CMT는 사용하지만 스프링 트랜잭션 관리를 원하지 **않는다면** 어떠한 스프링 트랜잭션 관리자를 설정해서도 **안되고** 마이바티스 `ManagedTransactionFactory`를 사용하기 위해 `SqlSessionFactoryBean`를 설정**해야만** 한다. @@ -83,10 +83,10 @@ public class MyBatisConfig { ## Programmatic Transaction Management -마이바티스 `SqlSession`은 트랜잭션을 제어하는 메서드를 제공한다. 하지만 마이바티스 스프링 연동모듈은 빈을 스프링이 관리하는 `SqlSession`이나 스프링이 관리하는 매퍼에 주입한다. +마이바티스 `SqlSession`은 트랜잭션을 제어하는 메서드를 제공한다. 하지만 마이바티스 스프링 연동 모듈은 빈을 스프링이 관리하는 `SqlSession`이나 스프링이 관리하는 매퍼에 주입한다. 이 말은 스프링이 **항상** 트랜잭션을 관리한다는 뜻이다. -스프링이 관리하는 `SqlSession`에서는 `SqlSession.commit()`, `SqlSession.rollback()` 또는 `SqlSession.close()` 메서드를 호출할수가 없다. +스프링이 관리하는 `SqlSession`에서는 `SqlSession.commit()`, `SqlSession.rollback()` 또는 `SqlSession.close()` 메서드를 호출할 수가 없다. 그럼에도 불구하고 이 메서드들을 사용하면 `UnsupportedOperationException` 예외가 발생한다. 이러한 메서드는 주입된 매퍼 클래스에서는 사용할 수 없다. JDBC연결의 자동커밋 설정을 어떻게 하더라도 스프링 트랜잭션 밖의 `SqlSession` 데이터 메서드나 매퍼 메서드의 실행은 자동으로 커밋된다. diff --git a/src/site/ko/markdown/using-api.md b/src/site/ko/markdown/using-api.md index 3894e1d396..e686106e00 100644 --- a/src/site/ko/markdown/using-api.md +++ b/src/site/ko/markdown/using-api.md @@ -21,7 +21,7 @@ public class UserDaoImpl implements UserDao { } ``` -이 방법은 **신중히** 사용하자. 왜냐하면 잘못사용하면 런타임 에러나 데이터 문제등을 야기할수 있기 때문이다. API를 직접 사용할때는 다음의 규칙들에 유의해야 한다. +이 방법은 **신중히** 사용하자. 왜냐하면 잘못 사용하면 런타임 에러나 데이터 문제 등을 야기할 수 있기 때문이다. API를 직접 사용할 때는 다음의 규칙들에 유의해야 한다. * 스프링 트랜잭션에 **속하지 않고** 별도의 트랜잭션에서 동작한다. * `SqlSession`이 스프링 트랜잭션 관리자가 사용하는 `DataSource`를 사용하고 이미 트랜잭션이 동작하고 있다면 이 코드는 예외를 **발생시킬 것이다**. diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index bdfcad162f..8f9f49ae10 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -49,7 +49,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/markdown/mappers.md b/src/site/markdown/mappers.md index ad365ff92f..79a6582e10 100644 --- a/src/site/markdown/mappers.md +++ b/src/site/markdown/mappers.md @@ -135,7 +135,7 @@ By default, these two properties are null, so all interfaces in the given base p Discovered mappers will be named using Spring default naming strategy for autodetected components (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)). That is, if no annotation is found, it will use the uncapitalized non-qualified class name of the mapper. But if either a `@Component` or a JSR-330 `@Named` annotation is found it will get the name from the annotation. -Notice that you can set the `annotation` attribute to `org.springframework.stereotype.Component`, `javax.inject.Named` (if you have JSE 6) or to your own annotation (that must be itself annotated) so the annotation will work both as a marker and as a name provider. +Notice that you can set the `annotation` attribute to `org.springframework.stereotype.Component`, `jakarta.inject.Named` (if you have JakartaEE) or to your own annotation (that must be itself annotated) so the annotation will work both as a marker and as a name provider. NOTE `` won't be able to scan and register mappers. Mappers are interfaces and, in order to register them to Spring, the scanner must know how to create a `MapperFactoryBean` for each interface it finds. diff --git a/src/site/resources/zh/css/site.css b/src/site/resources/zh_CN/css/site.css similarity index 95% rename from src/site/resources/zh/css/site.css rename to src/site/resources/zh_CN/css/site.css index b3f440195c..c604f39360 100644 --- a/src/site/resources/zh/css/site.css +++ b/src/site/resources/zh_CN/css/site.css @@ -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. diff --git a/src/site/site.xml b/src/site/site.xml index 2c5f8e5ef5..7f2132dc36 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml new file mode 100644 index 0000000000..8dcbac7aa0 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml new file mode 100644 index 0000000000..37cf7c18e1 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml new file mode 100644 index 0000000000..09ec80551d --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml new file mode 100644 index 0000000000..918b0daf1a --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml new file mode 100644 index 0000000000..906765847e --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml new file mode 100644 index 0000000000..08a6d8aa07 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml new file mode 100644 index 0000000000..b479e0bed6 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml new file mode 100644 index 0000000000..145a6cd829 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml new file mode 100644 index 0000000000..bce32a0c58 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml new file mode 100644 index 0000000000..c6ab57a115 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml new file mode 100644 index 0000000000..2f82ca529d --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/default.properties b/src/test/resources/org/mybatis/spring/filter/xml/default.properties new file mode 100644 index 0000000000..6effd30b64 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/default.properties @@ -0,0 +1,19 @@ +# +# Copyright 2010-2025 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +filter.custom=org.mybatis.spring.filter.customfilter.CustomTypeFilter +filter.aspectj=*..CommonDataSourceMapper +filter.regex=org\.mybatis\.spring\.filter\.datasource\.datasource1\..* diff --git a/src/test/java/org/mybatis/spring/mybatis-config.xml b/src/test/resources/org/mybatis/spring/mybatis-config.xml similarity index 95% rename from src/test/java/org/mybatis/spring/mybatis-config.xml rename to src/test/resources/org/mybatis/spring/mybatis-config.xml index d62326a516..4abec86943 100644 --- a/src/test/java/org/mybatis/spring/mybatis-config.xml +++ b/src/test/resources/org/mybatis/spring/mybatis-config.xml @@ -1,7 +1,7 @@
+ * 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 extends MapperFactoryBean> 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 @@ -55,7 +55,7 @@ public class BatchAppConfig { } ``` -**좀더 복잡한 예제를 보자.** +**좀 더 복잡한 예제를 보자.** ```xml @@ -246,16 +246,16 @@ public class BatchAppConfig { ``` -**여러개의 테이블에 데이터를 쓰려면 한꺼번에 처리할 수 있도록 만든 writer(몇가지 규칙을 가지고)를 사용하자.** +**여러 개의 테이블에 데이터를 쓰려면 한꺼번에 처리할 수 있도록 만든 writer(몇 가지 규칙을 가지고)를 사용하자.** 이 기능은 마이바티스 3.2이상에서만 사용할 수 있다. 이전의 버전에서는 예상과 다르게 동작하는데 그 내용은 [이슈](http://code.google.com/p/mybatis/issues/detail?id=741)를 참고하면 된다. -배치가 관계를 가지는 데이터나 여러개의 데이터베이스를 다루는 것처럼 복잡한 데이터를 작성할때 필요하다면 insert구문이 한개에 테이블에만 데이터를 넣을수 있다는 사실만 피하면 가능하기도 하다. +배치가 관계를 가지는 데이터나 여러 개의 데이터베이스를 다루는 것처럼 복잡한 데이터를 작성할 때 필요하다면 insert구문이 한 개에 테이블에만 데이터를 넣을 수 있다는 사실만 피하면 가능하기도 하다. 이런 복잡한 데이터를 처리하기 위해 writer가 작성하는 아이템(Item)을 준비해야 한다. 다음의 기술을 사용하면 단순한 관계를 가진 데이터나 관계가 없는 테이블을 처리하는 아이템에서 사용할 수 있다. -이러한 방법으로 스프링 배치 아이템은 모든 레코드를 *다룰것이다*. -여기에는 1:1 *관계*를 가지는 *InteractionMetadata*, 관계가 없는 두개의 로우는 *VisitorInteraction* 와 *CustomerInteraction*이 있다. -이 각각의 객체는 다음과 같이 볼수 있다. +이러한 방법으로 스프링 배치 아이템은 모든 레코드를 *다룰 것이다*. +여기에는 1:1 *관계*를 가지는 *InteractionMetadata*, 관계가 없는 두 개의 로우는 *VisitorInteraction* 와 *CustomerInteraction*이 있다. +이 각각의 객체는 다음과 같이 볼 수 있다. ```java public class InteractionRecordToWriteInMultipleTables { @@ -271,7 +271,7 @@ public class Interaction { } ``` -그리고 스프링 설정에는 각각의 레코드를 처리하기위해 특별히 설정된 전용(delegates) writer를 사용하는 `CompositeItemWriter`가 있다. +그리고 스프링 설정에는 각각의 레코드를 처리하기 위해 특별히 설정된 전용(delegates) writer를 사용하는 `CompositeItemWriter`가 있다. ```xml @@ -304,7 +304,7 @@ public class BatchAppConfig { } ``` -각각의 전용(delegate) writer는 필요할 만큼 설정할 수 있다. 예를들면 *Interaction* 과 *InteractionMetadata*를 위한 writer가 있다. +각각의 전용(delegate) writer는 필요할 만큼 설정할 수 있다. 예를 들면 *Interaction* 과 *InteractionMetadata*를 위한 writer가 있다. ```xml ``` -reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 가리킬수 있다. +reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 가리킬 수 있다. 매퍼 파일에서 구문은 다음과 같은 방법으로 각각의 레코드를 위해 만들어져있다. @@ -345,9 +345,9 @@ reader와 동일하게 `statementId`는 네임스페이스를 가진 구문을 ``` -먼저 `insertInteractionMetadata`가 호출될것이고 update구문은 jdbc드라이버에 의해(`keyProperty` 와 `keyColumn`) 생성된 아이디들을 리턴하기 위해 설정되었다. -`InteractionMetadata` 객체가 이 쿼리에 의해 업데이트되면 다음의 쿼리는 `insertInteraction`를 통해 상위객체인 `Interaction`를 작성하기 위해 사용될수 있다. +먼저 `insertInteractionMetadata`가 호출될 것이고 update구문은 jdbc드라이버에 의해(`keyProperty` 와 `keyColumn`) 생성된 아이디들을 리턴하기 위해 설정되었다. +`InteractionMetadata` 객체가 이 쿼리에 의해 업데이트되면 다음의 쿼리는 `insertInteraction`를 통해 상위객체인 `Interaction`를 작성하기 위해 사용될 수 있다. -***방금 언급한 내용에 관련하여 JDBC드라이버가 똑같이 동작하지 않을수 있다. +***방금 언급한 내용에 관련하여 JDBC드라이버가 똑같이 동작하지 않을 수 있다. 이 글을 쓰는 시점에 H2 드라이버 1.3.168버전(`org.h2.jdbc.JdbcStatement#getGeneratedKeys`를 보라)만 배치모드에서 마지막 인덱스를 리턴한다. 반면에 MySQL 드라이버는 기대한 것과 동일하게 동작하고 모든 아이디를 리턴한다.*** diff --git a/src/site/ko/markdown/factorybean.md b/src/site/ko/markdown/factorybean.md index e0a79081ad..4c8cff57b4 100644 --- a/src/site/ko/markdown/factorybean.md +++ b/src/site/ko/markdown/factorybean.md @@ -2,11 +2,11 @@ # SqlSessionFactoryBean 마이바티스만 사용하면, `SqlSessionFactory`는 `SqlSessionFactoryBuilder`를 사용해서 생성한다. -마이바티스 스프링 연동모듈에서는, `SqlSessionFactoryBean`가 대신 사용된다. +마이바티스 스프링 연동 모듈에서는, `SqlSessionFactoryBean`가 대신 사용된다. ## 설정 -팩토리 빈을 생성하기 위해, 스프링 XML설정파일에 다음설정을 추가하자. +팩토리 빈을 생성하기 위해, 스프링 XML 설정 파일에 다음 설정을 추가하자. ```xml @@ -31,23 +31,23 @@ public class MyBatisConfig { ``` 일반적인 마이바티스 스프링 사용법에서는, `SqlSessionFactoryBean`이나 관련된 `SqlSessionFactory`를 직접 사용할 필요가 없다. -대신 세션 팩토리가 `MapperFactoryBean`나 `SqlSessionDaoSupport`를 확장하는 다른 DAO에 주입될것이다. +대신 세션 팩토리가 `MapperFactoryBean`나 `SqlSessionDaoSupport`를 확장하는 다른 DAO에 주입될 것이다. ## 속성 `SqlSessionFactory`는 JDBC `DataSource`의 필수 프로퍼티가 필요하다. 어떤 `DataSource`라도 상관없고 다른 스프링 데이터베이스 연결처럼 설정되어야만 한다. -하나의 공통적인 프로퍼티는 마이바티스 XML설정파일의 위치를 지정하기 위해 사용되는 `configLocation`이다. 이 프로퍼티를 설정하는 것은 디폴트 설정을 가진 마이바티스 설정을 변경해야 할 경우 뿐이다. +하나의 공통적인 프로퍼티는 마이바티스 XML 설정 파일의 위치를 지정하기 위해 사용되는 `configLocation`이다. 이 프로퍼티를 설정하는 것은 디폴트 설정을 가진 마이바티스 설정을 변경해야 할 경우 뿐이다. 대개는 ``과 `` 섹션을 변경하는 경우이다. -설정파일이 마이바티스 설정을 완전히 다룰 필요는 없다. 어떤 환경, 어떤 데이터소스 그리고 마이바티스 트랜잭션 관리자가 **무시**될수도 있다. +설정 파일이 마이바티스 설정을 완전히 다룰 필요는 없다. 어떤 환경, 어떤 데이터소스 그리고 마이바티스 트랜잭션 관리자가 **무시**될 수도 있다. `SqlSessionFactoryBean` 는 필요에 따라 이 값들을 설정하여 자체적인 MyBatis `Environment` 를 만든다. -설정파일이 필요한 다른 이유는 마이바티스 XML파일이 매퍼 클래스와 동일한 클래스패스에 있지 않은 경우이다. 이 설정을 사용하면 두가지 옵션이 있다. -첫번째는 마이바티스 설정파일에 `` 섹션을 사용해서 XML파일의 클래스패스를 지정하는 것이다. 두번째는 팩토리 빈의 `mapperLocations` 프로퍼티를 사용하는 것이다. +설정 파일이 필요한 다른 이유는 마이바티스 XML파일이 매퍼 클래스와 동일한 클래스패스에 있지 않은 경우이다. 이 설정을 사용하면 두 가지 옵션이 있다. +첫 번째는 마이바티스 설정파일에 `` 섹션을 사용해서 XML파일의 클래스패스를 지정하는 것이다. 두 번째는 팩토리 빈의 `mapperLocations` 프로퍼티를 사용하는 것이다. `mapperLocations` 프로퍼티는 매퍼에 관련된 자원의 위치를 나열한다. 이 프로퍼티는 마이바티스의 XML매퍼 파일들의 위치를 지정하기 위해 사용될 수 있다. -디렉터리 아래 모든 파일을 로드하기 위해 앤트(Ant) 스타일의 패턴을 사용할수도 있고 가장 상위 위치를 지정하는 것으로 재귀적으로 하위 경로를 찾도록 할수도 있다. 예를 들어보면 다음과 같다. +디렉터리 아래 모든 파일을 로드하기 위해 앤트(Ant) 스타일의 패턴을 사용할 수도 있고 가장 상위 위치를 지정하는 것으로 재귀적으로 하위 경로를 찾도록 할 수도 있다. 예를 들어보면 다음과 같다. ```xml @@ -58,7 +58,7 @@ public class MyBatisConfig { 이 설정은 `sample.config.mappers` 패키지 아래와 그 하위 패키지를 모두 검색해서 마이바티스 매퍼 XML파일을 모두 로드할 것이다. -컨테이너 관리 트랜잭션을 사용하는 환경에서 필요한 하나의 프로퍼티는 `transactionFactoryClass` 이다. 이에 관련해서는 트랜잭션을 다루는 장에서 볼수 있다. +컨테이너 관리 트랜잭션을 사용하는 환경에서 필요한 하나의 프로퍼티는 `transactionFactoryClass` 이다. 이에 관련해서는 트랜잭션을 다루는 장에서 볼 수 있다. 만약 multi-db 기능을 사용한다면 다음과 같이 `databaseIdProvider` 속성을 설정해야 한다. @@ -83,7 +83,7 @@ public class MyBatisConfig { ```` NOTE -1.3.0 버전 부터 `configuration` 속성이 추가되었다. 다음과 같이 MyBatis XML 설정 파일없이 `Configuration` 인스턴스를 직접 지정할 수 있습니다. +1.3.0 버전부터 `configuration` 속성이 추가되었다. 다음과 같이 MyBatis XML 설정 파일 없이 `Configuration` 인스턴스를 직접 지정할 수 있습니다. ```xml diff --git a/src/site/ko/markdown/getting-started.md b/src/site/ko/markdown/getting-started.md index 9547c6f39a..553dda2ba5 100644 --- a/src/site/ko/markdown/getting-started.md +++ b/src/site/ko/markdown/getting-started.md @@ -1,11 +1,11 @@ # 시작하기 -이 장은 마이바티스 스프링 연동모듈을 설치하고 셋팅하는 방법에 대해 간단히 보여준다. 그리고 트랜잭션을 사용하는 간단한 애플리케이션을 만드는 방법까지 다룰 것이다. +이 장은 마이바티스 스프링 연동 모듈을 설치하고 셋팅하는 방법에 대해 간단히 보여준다. 그리고 트랜잭션을 사용하는 간단한 애플리케이션을 만드는 방법까지 다룰 것이다. ## 설치 -마이바티스 스프링 연동모듈을 사용하기 위해서, 클래스패스에 `mybatis-spring-${project.version}.jar`를 포함시켜야 한다. +마이바티스 스프링 연동 모듈을 사용하기 위해서, 클래스패스에 `mybatis-spring-${project.version}.jar`를 포함시켜야 한다. 메이븐을 사용하고 있다면 pom.xml에 다음처럼 의존성을 추가하면 된다. @@ -19,10 +19,10 @@ ## 빠른 설정 -마이바티스를 스프링과 함께 사용하려면 스프링의 애플리케이션 컨텍스트에 적어도 두개를 정의해줄 필요가 있다. -두가지는 `SqlSessionFactory`와 한개 이상의 매퍼 인터페이스이다. +마이바티스를 스프링과 함께 사용하려면 스프링의 애플리케이션 컨텍스트에 적어도 두 개를 정의해 줄 필요가 있다. +두 가지는 `SqlSessionFactory`와 한 개 이상의 매퍼 인터페이스이다. -마이바티스 스프링 연동모듈에서, `SqlSessionFactoryBean`은 `SqlSessionFactory`를 만들기 위해 사용된다. 팩토리 빈을 설정하기 위해, 스프링 설정파일에 다음 설정을 추가하자. +마이바티스 스프링 연동 모듈에서, `SqlSessionFactoryBean`은 `SqlSessionFactory`를 만들기 위해 사용된다. 팩토리 빈을 설정하기 위해, 스프링 설정 파일에 다음 설정을 추가하자. ```xml @@ -62,10 +62,10 @@ UserMapper인터페이스는 다음처럼 `MapperFactoryBean`을 사용해서 ``` -매퍼는 **반드시** 구현체 클래스가 아닌 인터페이스로 정의되어야 한다. 예를들어, 애노테이션이 SQL을 명시하기 위해 사용되지만 마이바티스 매퍼 XML파일 또한 사용될 수 있다. +매퍼는 **반드시** 구현체 클래스가 아닌 인터페이스로 정의되어야 한다. 예를 들어, 애노테이션이 SQL을 명시하기 위해 사용되지만 마이바티스 매퍼 XML파일 또한 사용될 수 있다. -한번만 설정하면, 다른 스프링 빈에 주입하는 같은 방법으로 비즈니스/서비스 객체에 매퍼를 직접 주입할 수 있다. `MapperFactoryBean`은 `SqlSession`을 생성하고 닫는 작업을 잘 다룬다. -실행중인 스프링 트랜잭션이 있다면, 트랜잭션이 완료되는 시점에 커밋이나 롤백이 될 것이다. 마지막으로 예외가 발생하면 스프링의 `DataAccessException`예외가 발생한다. +한 번만 설정하면, 다른 스프링 빈에 주입하는 같은 방법으로 비즈니스/서비스 객체에 매퍼를 직접 주입할 수 있다. `MapperFactoryBean`은 `SqlSession`을 생성하고 닫는 작업을 잘 다룬다. +실행 중인 스프링 트랜잭션이 있다면, 트랜잭션이 완료되는 시점에 커밋이나 롤백이 될 것이다. 마지막으로 예외가 발생하면 스프링의 `DataAccessException`예외가 발생한다. 자바로 설정하면 다음과 같다. diff --git a/src/site/ko/markdown/index.md b/src/site/ko/markdown/index.md index 83e5567550..a43f6578aa 100644 --- a/src/site/ko/markdown/index.md +++ b/src/site/ko/markdown/index.md @@ -3,18 +3,18 @@ ## MyBatis-Spring 은 무엇일까? -마이바티스 스프링 연동모듈은 마이바티스와 스프링을 편하고 간단하게 연동한다. 이 모듈은 마이바티스로 하여금 스프링 트랜잭션에 쉽게 연동되도록 처리한다. 게다가 마이바티스 매퍼와 `SqlSession`을 다루고 다른 빈에 주입시켜준다. -마이바티스 예외를 스프링의 `DataAccessException`로 변환하기도 하고 마이바티스, 스프링 또는 마이바티스 스프링 연동모듈에 의존성을 없애기도 한다. +마이바티스 스프링 연동 모듈은 마이바티스와 스프링을 편하고 간단하게 연동한다. 이 모듈은 마이바티스로 하여금 스프링 트랜잭션에 쉽게 연동되도록 처리한다. 게다가 마이바티스 매퍼와 `SqlSession`을 다루고 다른 빈에 주입시켜준다. +마이바티스 예외를 스프링의 `DataAccessException`로 변환하기도 하고 마이바티스, 스프링 또는 마이바티스 스프링 연동 모듈에 의존성을 없애기도 한다. ## 동기 부여 -스프링 2.x은 아이바티스 2.x만을 지원한다. 스프링 3.x에서 마이바티스 3.x를 지원하기 위한 시도가 진행중이다. (스프링의 이슈관리 시스템인 [이슈](https://jira.springsource.org/browse/SPR-5991) 를 보라.) -불행하게도 스프링 3의 개발이 마이바티스 3.0의 정식릴리즈전에 개발이 완료되었다. 그래서 스프링팀은 릴리즈가 안된 마이바티스 코드를 함께 릴리즈하는 것을 원하지 않았고 실제적인 스프링 지원을 기다릴수밖에 없었다. +스프링 2.x은 아이바티스 2.x만을 지원한다. 스프링 3.x에서 마이바티스 3.x를 지원하기 위한 시도가 진행 중이다. (스프링의 이슈관리 시스템인 [이슈](https://jira.springsource.org/browse/SPR-5991) 를 보라.) +불행하게도 스프링 3의 개발이 마이바티스 3.0의 정식 릴리즈 전에 개발이 완료되었다. 그래서 스프링팀은 릴리즈가 안 된 마이바티스 코드를 함께 릴리즈하는 것을 원하지 않았고 실제적인 스프링 지원을 기다릴 수밖에 없었다. 스프링의 마이바티스 지원에 대한 관심으로 인해, 마이바티스 커뮤니티는 재결합하는 형태로 결정을 내고 대신 마이바티스의 하위 프로젝트 형태로 스프링 연동 프로젝트를 추가한다. ## 필요 조건 -마이바티스 스프링 연동을 시작하기 전에, 마이바티스와 스프링의 용어를 맞추는 일이 굉장히 중요했다. 이 문서는 배경지식이나 기본적인 셋업방법 그리고 마이바티스와 스프링의 설정에 대한 튜토리얼등은 제공하지 않는다. +마이바티스 스프링 연동을 시작하기 전에, 마이바티스와 스프링의 용어를 맞추는 일이 굉장히 중요했다. 이 문서는 배경지식이나 기본적인 셋업방법 그리고 마이바티스와 스프링의 설정에 대한 튜토리얼 등은 제공하지 않는다. MyBatis-Spring requires following versions: @@ -28,9 +28,9 @@ MyBatis-Spring requires following versions: ## 감사 인사 이 프로젝트가 실제로 만들어지게 도와준 모든 특별한 분들에게 정말 감사한다. -알파벳 순서로 보면, 코딩및 테스트 그리고 문서화를 담당했던 Eduardo Macarron, Hunter Presnall, Putthiphong Boonphong; +알파벳 순서로 보면, 코딩 및 테스트 그리고 문서화를 담당했던 Eduardo Macarron, Hunter Presnall, Putthiphong Boonphong; 그외 다양한 프로젝트 기여자인 Andrius Juozapaitis, Giovanni Cuccu, Mike Lanyon, Raj Nagappan, Tomas Pinos; -그리고 마이바티스에 하위 프로젝트로 가져올수 있도록 많은 것을 찾아준 Simone Tripodi 에게 감사한다. ;) +그리고 마이바티스에 하위 프로젝트로 가져올 수 있도록 많은 것을 찾아준 Simone Tripodi 에게 감사한다. ;) 이들이 없었다면 이 프로젝트는 존재하지 않았을 것이다. ## 이 문서가 더 나아지도록 도와주세요… @@ -39,16 +39,16 @@ MyBatis-Spring requires following versions: 이 문서의 원본은 markdown 포맷이며 [프로젝트의 Git](https://github.com/mybatis/spring/tree/master/src/site)에서 찾을 수 있다. repository 를 fork 하고, 업데이트하고 pull request 를 보내주십시오. -당신처럼 이 문서를 읽는 사람들에게 이 문서의 최고의 저자가 될수 있다! +당신처럼 이 문서를 읽는 사람들에게 이 문서의 최고의 저자가 될 수 있다! ## 번역 -사용자들은 다음의 번역문서별로 마이바티스 스프링 연동모듈에 대해 알수 있다. +사용자들은 다음의 번역 문서별로 마이바티스 스프링 연동 모듈에 대해 알 수 있다. English Español - 简体中文 + 简体中文 日本語 한국어 diff --git a/src/site/ko/markdown/mappers.md b/src/site/ko/markdown/mappers.md index c56ba731ff..d537db1e65 100644 --- a/src/site/ko/markdown/mappers.md +++ b/src/site/ko/markdown/mappers.md @@ -1,7 +1,7 @@ # 매퍼 주입 -`SqlSessionDaoSupport` 나 `SqlSessionTemplate` 를 직접적으로 사용하는 데이터 접근 객체(DAO)를 생성하기 보다, 마이바티스 스프링 연동모듈은 다른 빈에 직접 주입할 수 있는 쓰레드에 안전한 매퍼를 생성할 수 있다. +`SqlSessionDaoSupport` 나 `SqlSessionTemplate` 를 직접적으로 사용하는 데이터 접근 객체(DAO)를 생성하기보다, 마이바티스 스프링 연동 모듈은 다른 빈에 직접 주입할 수 있는 쓰레드에 안전한 매퍼를 생성할 수 있다. ```xml @@ -9,7 +9,7 @@ ``` -한번 주입하고나면 매퍼는 애플리케이션 로직에서 사용할수 있는 준비가 된다. +한번 주입하고 나면 매퍼는 애플리케이션 로직에서 사용할 수 있는 준비가 된다. ```java public class FooServiceImpl implements FooService { @@ -26,7 +26,7 @@ public class FooServiceImpl implements FooService { } ``` -이 코드를 보면 `SqlSession`이나 마이바티스 객체가 보이지 않는다. 게다가 세션을 생성하거나 열고 닫을필요도 없어보인다. 마이바티스 스프링 연동모듈이 알아서 처리할 것이다. +이 코드를 보면 `SqlSession`이나 마이바티스 객체가 보이지 않는다. 게다가 세션을 생성하거나 열고 닫을 필요도 없어 보인다. 마이바티스 스프링 연동 모듈이 알아서 처리할 것이다. ## 매퍼 등록하기 @@ -35,7 +35,7 @@ public class FooServiceImpl implements FooService { ### XML설정 사용 -매퍼는 다음처럼 XML설정파일에 `MapperFactoryBean`을 두는 것으로 스프링에 등록된다. +매퍼는 다음처럼 XML설정 파일에 `MapperFactoryBean`을 두는 것으로 스프링에 등록된다. ```xml @@ -45,10 +45,10 @@ public class FooServiceImpl implements FooService { ``` UserMapper가 매퍼 인터페이스와 같은 경로의 클래스패스에 마이바티스 XML매퍼 파일을 가지고 있다면 `MapperFactoryBean`이 자동으로 파싱할것이다. -매퍼 XML파일을 다른 클래스패스에 두는게 아니라면 마이바티스 설정파일에 매퍼를 지정할 필요가 없다. 좀더 세부적인 정보는 `SqlSessionFactoryBean`의 [`configLocation`](factorybean.html) 프로퍼티를 살펴보자. +매퍼 XML파일을 다른 클래스패스에 두는게 아니라면 마이바티스 설정 파일에 매퍼를 지정할 필요가 없다. 좀 더 세부적인 정보는 `SqlSessionFactoryBean`의 [`configLocation`](factorybean.html) 프로퍼티를 살펴보자. `MapperFactoryBean`은 `SqlSessionFactory` 나 `SqlSessionTemplate`가 필요하다. `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅하면 된다. -둘다 셋팅하면 `SqlSessionFactory`가 무시된다. 세션 팩토리 셋은 `SqlSessionTemplate`이 필요하고 `MapperFactoryBean`는 팩토리를 사용할것이다. +둘 다 셋팅하면 `SqlSessionFactory`가 무시된다. 세션 팩토리 셋은 `SqlSessionTemplate`이 필요하고 `MapperFactoryBean`는 팩토리를 사용할 것이다. ### 자바설정 사용 @@ -67,15 +67,15 @@ public class MyBatisConfig { ## 매퍼 스캔 -하나씩 매퍼를 모두 등록할 필요가 없다. 대신 클래스패스를 지정해서 마이바티스 스프링 연동모듈의 자동스캔기능을 사용할 수 있다. +하나씩 매퍼를 모두 등록할 필요가 없다. 대신 클래스패스를 지정해서 마이바티스 스프링 연동 모듈의 자동 스캔 기능을 사용할 수 있다. -자동스캔을 사용하는데는 3가지 방법이 있다. +자동 스캔을 사용하는 데는 3가지 방법이 있다. * `` 엘리먼트 사용 * `@MapperScan` 애노테이션 사용 * 스프링 XML파일을 사용해서 `MapperScannerConfigurer`를 등록 -`` 와 `@MapperScan` 모두 마이바티스 스프링 연동모듈 1.2.0에서 추가된 기능이다. `@MapperScan` 은 스프링 버전이 3.1이상이어야 한다. +`` 와 `@MapperScan` 모두 마이바티스 스프링 연동 모듈 1.2.0에서 추가된 기능이다. `@MapperScan` 은 스프링 버전이 3.1이상이어야 한다. Since 2.0.2, mapper scanning feature support a option (`lazy-initialization`) that control lazy initialization enabled/disabled of mapper bean. The motivation for adding this option is supporting a lazy initialization control feature supported by Spring Boot 2.2. @@ -107,7 +107,7 @@ The `default-scope` apply to the mapper bean(`MapperFactoryBean`) when scope of `` XML엘리먼트는 스프링에서 제공하는 `` 엘리먼트와 매우 유사한 방법으로 매퍼를 검색할 것이다. -샘플 XML설정을 아래에서 볼수 있다. +샘플 XML설정을 아래에서 볼 수 있다. ```xml ``` -`base-package` 속성은 매퍼 인터페이스 파일이 있는 가장 상위 패키지를 지정하면 된다. 세미콜론이나 콤마를 구분자로 사용해서 한개 이상의 패키지를 셋팅할 수 있다. +`base-package` 속성은 매퍼 인터페이스 파일이 있는 가장 상위 패키지를 지정하면 된다. 세미콜론이나 콤마를 구분자로 사용해서 한 개 이상의 패키지를 셋팅할 수 있다. 매퍼는 지정된 패키지에서 재귀적으로 하위 패키지를 모두 검색할 것이다. ``이 자동으로 주입할 수 있는 `MapperFactoryBean`를 생성하기 때문에 `SqlSessionFactory` 나 `SqlSessionTemplate` 를 명시할 필요가 없다. -하지만 한개 이상의 `DataSource`를 사용한다면 자동주입이 생각한데로 동작하지 않을수도 있다. 이 경우 사용할 빈 이름을 지정하기 위해 `factory-ref` 나 `template-ref` 속성을 사용할수 있다. +하지만 한 개 이상의 `DataSource`를 사용한다면 자동 주입이 생각한 대로 동작하지 않을 수도 있다. 이 경우 사용할 빈 이름을 지정하기 위해 `factory-ref` 나 `template-ref` 속성을 사용할수 있다. -``은 마커(marker) 인터페이스나 애노테이션을 명시해서 생성되는 매퍼를 필터링할수도 있다. `annotation` 프로퍼티는 검색할 애노테이션을 지정한다. -`marker-interface` 프로퍼티는 검색할 상위 인터페이스를 지정한다. 이 두개의 프로퍼티를 모두 지정하면, 매퍼는 두 조건을 모두 만족하는 인터페이스만을 추가한다. -디폴트로 이 두가지 프로퍼티는 모두 null이다. 그래서 base-package프로퍼티에 설정된 패키지 아래 모든 인터페이스가 매퍼로 로드될 것이다. +``은 마커(marker) 인터페이스나 애노테이션을 명시해서 생성되는 매퍼를 필터링할 수 도 있다. `annotation` 프로퍼티는 검색할 애노테이션을 지정한다. +`marker-interface` 프로퍼티는 검색할 상위 인터페이스를 지정한다. 이 두 개의 프로퍼티를 모두 지정하면, 매퍼는 두 조건을 모두 만족하는 인터페이스만을 추가한다. +디폴트로 이 두 가지 프로퍼티는 모두 null이다. 그래서 base-package프로퍼티에 설정된 패키지 아래 모든 인터페이스가 매퍼로 로드될 것이다. -발견된 매퍼는 자동검색된 컴포넌트를 위한 스프링의 디폴트 명명규칙 전략(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) 을 사용해서 빈이름이 명명된다. -빈 이름을 정하는 애노테이션이 없다면 매퍼의 이름에서 첫글자를 소문자로 변환한 형태로 빈 이름을 사용할 것이다. `@Component` 나 JSR-330의 `@Named` 애노테이션이 있다면 애노테이션에 정의한 이름을 그대로 사용할 것이다. -`annotation` 프로퍼티를 `org.springframework.stereotype.Component`, `javax.inject.Named`(자바SE 1.6을 사용한다면) 또는 개발자가 스스로 작성한 애노테이션으로 셋팅할 수 있다. +발견된 매퍼는 자동 검색된 컴포넌트를 위한 스프링의 디폴트 명명 규칙 전략(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) 을 사용해서 빈 이름이 명명된다. +빈 이름을 정하는 애노테이션이 없다면 매퍼의 이름에서 첫 글자를 소문자로 변환한 형태로 빈 이름을 사용할 것이다. `@Component` 나 JSR-330의 `@Named` 애노테이션이 있다면 애노테이션에 정의한 이름을 그대로 사용할 것이다. +`annotation` 프로퍼티를 `org.springframework.stereotype.Component`, `jakarta.inject.Named`(Jakarta EE을 사용한다면) 또는 개발자가 스스로 작성한 애노테이션으로 셋팅할 수 있다. 그러면 애노테이션은 마커와 이름을 제공하는 역할로 동작할 것이다. 중요 -`` 가 매퍼를 검색해서 등록을 하지 못할수도 있다. 매퍼는 인터페이스고 스프링에 빈으로 등록하기 위해서는 각각의 인터페이스를 찾기 위해 스캐너가 `MapperFactoryBean` 를 생성하는 방법을 알아야만 한다. +`` 가 매퍼를 검색해서 등록을 하지 못할 수도 있다. 매퍼는 인터페이스고 스프링에 빈으로 등록하기 위해서는 각각의 인터페이스를 찾기 위해 스캐너가 `MapperFactoryBean` 를 생성하는 방법을 알아야만 한다. ### @MapperScan -`@Configuration` 라고 불리는 스프링의 자바설정을 사용한다면 ``보다는 `@MapperScan`를 사용하길 선호할것이다. +`@Configuration` 라고 불리는 스프링의 자바 설정을 사용한다면 ``보다는 `@MapperScan`를 사용하길 선호할 것이다. `@MapperScan` 애노테이션은 다음처럼 사용된다. @@ -172,13 +172,13 @@ Since 2.0.4, If `basePackageClasses` or `basePackages` are not defined, scanning ``` -`sqlSessionFactory` 나 `sqlSessionTemplate`를 지정할 필요가 있다면 빈참조가 아닌 **빈이름**이 필요하다. `value` 프로퍼티는 빈 이름을 지정하고 `ref` 는 빈 참조를 지정하기 때문에 `value` 프로퍼티를 사용하자. +`sqlSessionFactory` 나 `sqlSessionTemplate`를 지정할 필요가 있다면 빈 참조가 아닌 **빈 이름**이 필요하다. `value` 프로퍼티는 빈 이름을 지정하고 `ref` 는 빈 참조를 지정하기 때문에 `value` 프로퍼티를 사용하자. ```xml ``` 중요 -`sqlSessionFactoryBean` 과 `sqlSessionTemplateBean` 프로퍼티는 마이바티스 스프링 연동모듈 1.0.2 버전 이상에서만 사용이 가능하다. +`sqlSessionFactoryBean` 과 `sqlSessionTemplateBean` 프로퍼티는 마이바티스 스프링 연동 모듈 1.0.2 버전 이상에서만 사용이 가능하다. 하지만 `MapperScannerConfigurer`는 잦은 에러를 발생시키는 `PropertyPlaceholderConfigurer`보다 앞서 실행되기 때문에 이 프로퍼티들은 사용하지 말길 바란다(deprecated). 대신 새롭게 추가된 프로퍼티인 `sqlSessionFactoryBeanName` 과 `sqlSessionTemplateBeanName` 를 사용하도록 권한다. diff --git a/src/site/ko/markdown/sqlsession.md b/src/site/ko/markdown/sqlsession.md index 783f42df0f..d0e3458ac8 100644 --- a/src/site/ko/markdown/sqlsession.md +++ b/src/site/ko/markdown/sqlsession.md @@ -1,21 +1,21 @@ # SqlSession 사용 -마이바티스에서는 `SqlSession`를 생성하기 위해 `SqlSessionFactory`를 사용한다. 세션을 한번 생성하면 매핑구문을 실행하거나 커밋 또는 롤백을 하기 위해 세션을 사용할수 있다. +마이바티스에서는 `SqlSession`를 생성하기 위해 `SqlSessionFactory`를 사용한다. 세션을 한번 생성하면 매핑구문을 실행하거나 커밋 또는 롤백을 하기 위해 세션을 사용할 수 있다. 마지막으로 더 이상 필요하지 않은 상태가 되면 세션을 닫는다. 마이바티스 스프링 연동모듈을 사용하면 `SqlSessionFactory`를 직접 사용할 필요가 없다. 왜냐하면, 스프링 트랜잭션 설정에 따라 자동으로 커밋 혹은 롤백을 수행하고 닫혀지는, 쓰레드에 안전한 `SqlSession` 개체가 스프링 빈에 주입될 수 있기 때문이다. # SqlSessionTemplate -`SqlSessionTemplate`은 마이바티스 스프링 연동모듈의 핵심이다. `SqlSessionTemplate`은 `SqlSession`을 구현하고 코드에서 `SqlSession`를 대체하는 역할을 한다. -`SqlSessionTemplate` 은 쓰레드에 안전하고 여러개의 DAO나 매퍼에서 공유할수 있다. +`SqlSessionTemplate`은 마이바티스 스프링 연동 모듈의 핵심이다. `SqlSessionTemplate`은 `SqlSession`을 구현하고 코드에서 `SqlSession`를 대체하는 역할을 한다. +`SqlSessionTemplate` 은 쓰레드에 안전하고 여러 개의 DAO나 매퍼에서 공유할 수 있다. -`getMapper()`에 의해 리턴된 매퍼가 가진 메서드를 포함해서 SQL을 처리하는 마이바티스 메서드를 호출할때 `SqlSessionTemplate`은 `SqlSession`이 현재의 스프링 트랜잭션에서 사용될수 있도록 보장한다. -추가적으로 `SqlSessionTemplate`은 필요한 시점에 세션을 닫고, 커밋하거나 롤백하는 것을 포함한 세션의 생명주기를 관리한다. 또한 마이바티스 예외를 스프링의 `DataAccessException`로 변환하는 작업또한 처리한다. +`getMapper()`에 의해 리턴된 매퍼가 가진 메서드를 포함해서 SQL을 처리하는 마이바티스 메서드를 호출할 때 `SqlSessionTemplate`은 `SqlSession`이 현재의 스프링 트랜잭션에서 사용될 수 있도록 보장한다. +추가적으로 `SqlSessionTemplate`은 필요한 시점에 세션을 닫고, 커밋하거나 롤백하는 것을 포함한 세션의 생명주기를 관리한다. 또한 마이바티스 예외를 스프링의 `DataAccessException`로 변환하는 작업 또한 처리한다. `SqlSessionTemplate`은 마이바티스의 디폴트 구현체인 `DefaultSqlSession` 대신 **항상** 사용된다. -왜냐하면 템플릿은 스프링 트랜잭션의 일부처럼 사용될 수 있고 여러개 주입된 매퍼 클래스에 의해 사용되도록 쓰레드에 안전하다. -동일한 애플리케이션에서 두개의 클래스간의 전환은 데이터 무결성 이슈를 야기할수 있다. +왜냐하면 템플릿은 스프링 트랜잭션의 일부처럼 사용될 수 있고 여러 개 주입된 매퍼 클래스에 의해 사용되도록 쓰레드에 안전하다. +동일한 애플리케이션에서 두 개의 클래스간 전환은 데이터 무결성 이슈를 야기할 수 있다. `SqlSessionTemplate`은 생성자 인자로 `SqlSessionFactory`를 사용해서 생성될 수 있다. @@ -59,7 +59,7 @@ public class UserDaoImpl implements UserDao { ``` -`SqlSessionTemplate`은 인자로 `ExecutorType`를 가지는 생성자를 가지고 있다. 이 인자는 예를들면 스프링 설정 XML을 다음처럼 설정해서 배치형태의 `SqlSession`를 만들수도 있다. +`SqlSessionTemplate`은 인자로 `ExecutorType`를 가지는 생성자를 가지고 있다. 이 인자는 예를 들면 스프링 설정 XML을 다음처럼 설정해서 배치형태의 `SqlSession`를 만들 수도 있다. ```xml @@ -94,14 +94,14 @@ public class UserService { } ``` -이러한 설정형태는 `SqlSessionFactory`의 디폴트 형태가 아닌 다른형태로 메서드를 실행해야 할때만 사용할 필요가 있다. +이러한 설정 형태는 `SqlSessionFactory`의 디폴트 형태가 아닌 다른 형태로 메서드를 실행해야 할 때만 사용할 필요가 있다. -이러한 형태에 대해 굳이 경로를 하자면 메서드를 호출할때 `ExecutorType`이 다르면 이미 시작된 트랜잭션을 사용하지 **못할**것이다. -다른 실행자(executor) 타입을 사용할때는 `SqlSessionTemplate`의 메서드를 구분된 트랜잭션(`PROPAGATION_REQUIRES_NEW`를 사용하는)이나 트랜잭션 외부에서 호출하는지 확실히해야 한다. +이러한 형태에 대해 굳이 경로를 하자면 메서드를 호출할 때 `ExecutorType`이 다르면 이미 시작된 트랜잭션을 사용하지 **못할**것이다. +다른 실행자(executor) 타입을 사용할 때는 `SqlSessionTemplate`의 메서드를 구분된 트랜잭션(`PROPAGATION_REQUIRES_NEW`를 사용하는)이나 트랜잭션 외부에서 호출하는지 확실히 해야 한다. ## SqlSessionDaoSupport -`SqlSessionDaoSupport`는 `SqlSession`을 제공하는 추상클래스이다. `getSqlSession()`메서드를 호출해서 다음처럼 SQL을 처리하는 마이바티스 메서드를 호출하기 위해 사용할 `SqlSessionTemplate`을 얻을 수 있다. +`SqlSessionDaoSupport`는 `SqlSession`을 제공하는 추상 클래스이다. `getSqlSession()`메서드를 호출해서 다음처럼 SQL을 처리하는 마이바티스 메서드를 호출하기 위해 사용할 `SqlSessionTemplate`을 얻을 수 있다. ```java public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { @@ -111,9 +111,9 @@ public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao { } ``` -대개 `MapperFactoryBean`은 추가적인 코드가 필요없기 때문에 이 클래스를 선호한다. 하지만 DAO에서 마이바티스가 필요하지 않고 구현된 클래스가 필요하지 않을때만 유용하다. +대개 `MapperFactoryBean`은 추가적인 코드가 필요 없기 때문에 이 클래스를 선호한다. 하지만 DAO에서 마이바티스가 필요하지 않고 구현된 클래스가 필요하지 않을 때만 유용하다. -`SqlSessionDaoSupport`는 `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅할 필요가 있다. 두개의 프로퍼티를 모두 셋팅하면 `sqlSessionFactory`는 무시된다. +`SqlSessionDaoSupport`는 `sqlSessionFactory` 와 `sqlSessionTemplate` 프로퍼티를 셋팅할 필요가 있다. 두 개의 프로퍼티를 모두 셋팅하면 `sqlSessionFactory`는 무시된다. `SqlSessionDaoSupport`의 하위클래스인 `UserDaoImpl`가 있다고 하면 스프링에서는 다음처럼 설정될 수 있다. diff --git a/src/site/ko/markdown/transactions.md b/src/site/ko/markdown/transactions.md index df84e8861d..f7ad754ef7 100644 --- a/src/site/ko/markdown/transactions.md +++ b/src/site/ko/markdown/transactions.md @@ -1,13 +1,13 @@ # Transactions -마이바티스 스프링 연동모듈을 사용하는 중요한 이유중 하나는 마이바티스가 스프링 트랜잭션에 자연스럽게 연동될수 있다는 것이다. +마이바티스 스프링 연동 모듈을 사용하는 중요한 이유 중 하나는 마이바티스가 스프링 트랜잭션에 자연스럽게 연동될 수 있다는 것이다. 마이바티스에 종속되는 새로운 트랜잭션 관리를 만드는 것보다는 마이바티스 스프링 연동모듈이 스프링의 `DataSourceTransactionManager`과 융합되는 것이 좋다. -스프링 트랜잭션 관리자를 한번 설정하면, 대개의 경우처럼 스프링에서 트랜잭션을 설정할 수 있다. `@Transactional` 애노테이션과 AOP스타일의 설정 모두 지원한다. -하나의 `SqlSession`객체가 생성되고 트랜잭션이 동작하는 동안 지속적으로 사용될것이다. 세션은 트랜잭션이 완료되면 적절히 커밋이 되거나 롤백될것이다. +스프링 트랜잭션 관리자를 한 번 설정하면, 대개의 경우처럼 스프링에서 트랜잭션을 설정할 수 있다. `@Transactional` 애노테이션과 AOP스타일의 설정 모두 지원한다. +하나의 `SqlSession`객체가 생성되고 트랜잭션이 동작하는 동안 지속적으로 사용될 것이다. 세션은 트랜잭션이 완료되면 적절히 커밋이 되거나 롤백될것이다. -마이바티스 스프링 연동모듈은 한번 셋업되면 트랜잭션을 투명하게 관리한다. DAO클래스에 어떠한 추가적인 코드를 넣을 필요가 없다. +마이바티스 스프링 연동 모듈은 한 번 셋업되면 트랜잭션을 투명하게 관리한다. DAO클래스에 어떠한 추가적인 코드를 넣을 필요가 없다. ## 표준 설정 @@ -30,9 +30,9 @@ public class DataSourceConfig { } ``` -명시된 `DataSource`는 스프링을 사용할때 일반적으로 사용한다면 어떠한 JDBC `DataSource`도 될수 있다. JNDI룩업을 통해 얻어진 `DataSource`뿐 아니라 커넥션 풀링 기능도 포함한다. +명시된 `DataSource`는 스프링을 사용할 때 일반적으로 사용한다면 어떠한 JDBC `DataSource`도 될 수 있다. JNDI룩업을 통해 얻어진 `DataSource`뿐 아니라 커넥션 풀링 기능도 포함한다. -트랜잭션 관리자에 명시된 `DataSource`가 `SqlSessionFactoryBean`을 생성할때 사용된 것과 **반드시** 동일한 것이어야 하는 것만 꼭 기억하자. 그렇지 않으면 트랜잭션 관리가 제대로 되지 않을것이다. +트랜잭션 관리자에 명시된 `DataSource`가 `SqlSessionFactoryBean`을 생성할 때 사용된 것과 **반드시** 동일한 것이어야 하는 것만 꼭 기억하자. 그렇지 않으면 트랜잭션 관리가 제대로 되지 않을 것이다. ## Container Managed Transactions @@ -53,7 +53,7 @@ public class DataSourceConfig { } ``` -이 설정에서, 마이바티스는 CMT와 함께 설정된 스프링 트랜잭션 리소스처럼 동작할 것이다. 스프링은 이미 설정된 트랜잭션을 사용해서 `SqlSession`을 이미 동작중인 트랜잭션에 넣을 것이다. +이 설정에서, 마이바티스는 CMT와 함께 설정된 스프링 트랜잭션 리소스처럼 동작할 것이다. 스프링은 이미 설정된 트랜잭션을 사용해서 `SqlSession`을 이미 동작 중인 트랜잭션에 넣을 것이다. 시작된 트랜잭션이 없고 트랜잭션이 필요한 경우라면 스프링은 새로운 컨테이너 관리 트랜잭션을 시작할 것이다. CMT는 사용하지만 스프링 트랜잭션 관리를 원하지 **않는다면** 어떠한 스프링 트랜잭션 관리자를 설정해서도 **안되고** 마이바티스 `ManagedTransactionFactory`를 사용하기 위해 `SqlSessionFactoryBean`를 설정**해야만** 한다. @@ -83,10 +83,10 @@ public class MyBatisConfig { ## Programmatic Transaction Management -마이바티스 `SqlSession`은 트랜잭션을 제어하는 메서드를 제공한다. 하지만 마이바티스 스프링 연동모듈은 빈을 스프링이 관리하는 `SqlSession`이나 스프링이 관리하는 매퍼에 주입한다. +마이바티스 `SqlSession`은 트랜잭션을 제어하는 메서드를 제공한다. 하지만 마이바티스 스프링 연동 모듈은 빈을 스프링이 관리하는 `SqlSession`이나 스프링이 관리하는 매퍼에 주입한다. 이 말은 스프링이 **항상** 트랜잭션을 관리한다는 뜻이다. -스프링이 관리하는 `SqlSession`에서는 `SqlSession.commit()`, `SqlSession.rollback()` 또는 `SqlSession.close()` 메서드를 호출할수가 없다. +스프링이 관리하는 `SqlSession`에서는 `SqlSession.commit()`, `SqlSession.rollback()` 또는 `SqlSession.close()` 메서드를 호출할 수가 없다. 그럼에도 불구하고 이 메서드들을 사용하면 `UnsupportedOperationException` 예외가 발생한다. 이러한 메서드는 주입된 매퍼 클래스에서는 사용할 수 없다. JDBC연결의 자동커밋 설정을 어떻게 하더라도 스프링 트랜잭션 밖의 `SqlSession` 데이터 메서드나 매퍼 메서드의 실행은 자동으로 커밋된다. diff --git a/src/site/ko/markdown/using-api.md b/src/site/ko/markdown/using-api.md index 3894e1d396..e686106e00 100644 --- a/src/site/ko/markdown/using-api.md +++ b/src/site/ko/markdown/using-api.md @@ -21,7 +21,7 @@ public class UserDaoImpl implements UserDao { } ``` -이 방법은 **신중히** 사용하자. 왜냐하면 잘못사용하면 런타임 에러나 데이터 문제등을 야기할수 있기 때문이다. API를 직접 사용할때는 다음의 규칙들에 유의해야 한다. +이 방법은 **신중히** 사용하자. 왜냐하면 잘못 사용하면 런타임 에러나 데이터 문제 등을 야기할 수 있기 때문이다. API를 직접 사용할 때는 다음의 규칙들에 유의해야 한다. * 스프링 트랜잭션에 **속하지 않고** 별도의 트랜잭션에서 동작한다. * `SqlSession`이 스프링 트랜잭션 관리자가 사용하는 `DataSource`를 사용하고 이미 트랜잭션이 동작하고 있다면 이 코드는 예외를 **발생시킬 것이다**. diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md index bdfcad162f..8f9f49ae10 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -49,7 +49,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/markdown/mappers.md b/src/site/markdown/mappers.md index ad365ff92f..79a6582e10 100644 --- a/src/site/markdown/mappers.md +++ b/src/site/markdown/mappers.md @@ -135,7 +135,7 @@ By default, these two properties are null, so all interfaces in the given base p Discovered mappers will be named using Spring default naming strategy for autodetected components (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)). That is, if no annotation is found, it will use the uncapitalized non-qualified class name of the mapper. But if either a `@Component` or a JSR-330 `@Named` annotation is found it will get the name from the annotation. -Notice that you can set the `annotation` attribute to `org.springframework.stereotype.Component`, `javax.inject.Named` (if you have JSE 6) or to your own annotation (that must be itself annotated) so the annotation will work both as a marker and as a name provider. +Notice that you can set the `annotation` attribute to `org.springframework.stereotype.Component`, `jakarta.inject.Named` (if you have JakartaEE) or to your own annotation (that must be itself annotated) so the annotation will work both as a marker and as a name provider. NOTE `` won't be able to scan and register mappers. Mappers are interfaces and, in order to register them to Spring, the scanner must know how to create a `MapperFactoryBean` for each interface it finds. diff --git a/src/site/resources/zh/css/site.css b/src/site/resources/zh_CN/css/site.css similarity index 95% rename from src/site/resources/zh/css/site.css rename to src/site/resources/zh_CN/css/site.css index b3f440195c..c604f39360 100644 --- a/src/site/resources/zh/css/site.css +++ b/src/site/resources/zh_CN/css/site.css @@ -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. diff --git a/src/site/site.xml b/src/site/site.xml index 2c5f8e5ef5..7f2132dc36 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml new file mode 100644 index 0000000000..8dcbac7aa0 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextAspectJFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml new file mode 100644 index 0000000000..37cf7c18e1 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextAssignFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml new file mode 100644 index 0000000000..09ec80551d --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextCombinedFilter.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml new file mode 100644 index 0000000000..918b0daf1a --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextCustFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml new file mode 100644 index 0000000000..906765847e --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml new file mode 100644 index 0000000000..08a6d8aa07 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter1.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml new file mode 100644 index 0000000000..b479e0bed6 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextInvalidFilter2.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml new file mode 100644 index 0000000000..145a6cd829 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml new file mode 100644 index 0000000000..bce32a0c58 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextPlaceHolder1.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml new file mode 100644 index 0000000000..c6ab57a115 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextProcessPlaceHolderOff.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml b/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml new file mode 100644 index 0000000000..2f82ca529d --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/appContextRegexFilter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/org/mybatis/spring/filter/xml/default.properties b/src/test/resources/org/mybatis/spring/filter/xml/default.properties new file mode 100644 index 0000000000..6effd30b64 --- /dev/null +++ b/src/test/resources/org/mybatis/spring/filter/xml/default.properties @@ -0,0 +1,19 @@ +# +# Copyright 2010-2025 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +filter.custom=org.mybatis.spring.filter.customfilter.CustomTypeFilter +filter.aspectj=*..CommonDataSourceMapper +filter.regex=org\.mybatis\.spring\.filter\.datasource\.datasource1\..* diff --git a/src/test/java/org/mybatis/spring/mybatis-config.xml b/src/test/resources/org/mybatis/spring/mybatis-config.xml similarity index 95% rename from src/test/java/org/mybatis/spring/mybatis-config.xml rename to src/test/resources/org/mybatis/spring/mybatis-config.xml index d62326a516..4abec86943 100644 --- a/src/test/java/org/mybatis/spring/mybatis-config.xml +++ b/src/test/resources/org/mybatis/spring/mybatis-config.xml @@ -1,7 +1,7 @@
MyBatisBatchItemWriter