├── src ├── main │ ├── resources │ │ ├── application.properties │ │ ├── schema.sql │ │ └── com │ │ │ └── example │ │ │ └── nativedemo │ │ │ └── NativeDemoApplication$MyXmlMapper.xml │ └── java │ │ └── com │ │ └── example │ │ └── nativedemo │ │ ├── NativeDemoApplication.java │ │ └── MyBatisNativeConfiguration.java └── test │ └── java │ └── com │ └── example │ └── nativedemo │ └── NativeDemoApplicationTests.java ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .gitignore ├── pom.xml ├── README.md ├── mvnw.cmd └── mvnw /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE messages( 2 | id bigint auto_increment, 3 | message text 4 | ); -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kazuki43zoo/mybatis-native-demo/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /src/test/java/com/example/nativedemo/NativeDemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.nativedemo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class NativeDemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /src/main/resources/com/example/nativedemo/NativeDemoApplication$MyXmlMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | INSERT INTO messages (message) 9 | VALUES (#{message}) 10 | 11 | 12 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/com/example/nativedemo/NativeDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.nativedemo; 2 | 3 | import org.apache.ibatis.annotations.Insert; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.apache.ibatis.annotations.Select; 6 | import org.springframework.boot.ApplicationRunner; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.core.annotation.Order; 11 | 12 | @SpringBootApplication(proxyBeanMethods = false) 13 | public class NativeDemoApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(NativeDemoApplication.class, args); 17 | } 18 | 19 | @Bean 20 | @Order(1) 21 | ApplicationRunner run(MyMapper mapper) { 22 | return args -> { 23 | mapper.insert(new Message(null, "Hello World on run!")); 24 | Message message = mapper.select(1); 25 | System.out.println(message); 26 | }; 27 | } 28 | 29 | @Bean 30 | @Order(2) 31 | ApplicationRunner runWithXmlMapper(MyXmlMapper mapper) { 32 | return args -> { 33 | mapper.insert(new Message(null, "Hello World! on runWithXmlMapper")); 34 | Message message = mapper.select(2); 35 | System.out.println(message); 36 | }; 37 | } 38 | 39 | @Mapper 40 | interface MyMapper { 41 | 42 | @Insert(""" 43 | INSERT INTO messages (message) 44 | VALUES (#{message}) 45 | """) 46 | void insert(Message message); 47 | 48 | @Select(""" 49 | SELECT 50 | id 51 | ,message 52 | FROM 53 | messages 54 | WHERE 55 | id = #{id} 56 | """) 57 | Message select(Integer id); 58 | 59 | } 60 | 61 | @Mapper 62 | interface MyXmlMapper { 63 | void insert(Message message); 64 | 65 | Message select(Integer id); 66 | } 67 | 68 | record Message(Integer id, String message) { 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.0.6 9 | 10 | 11 | com.example 12 | mybatis-native-demo 13 | 0.0.1-SNAPSHOT 14 | mybatis-native-demo 15 | mybatis-native-demo 16 | 17 | 17 18 | 19 | 20 | 21 | org.mybatis.spring.boot 22 | mybatis-spring-boot-starter 23 | 3.0.1 24 | 25 | 26 | com.h2database 27 | h2 28 | runtime 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.graalvm.buildtools 41 | native-maven-plugin 42 | 43 | 44 | --enable-url-protocols=http 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | 55 | 56 | 57 | sonatype-oss-snapshots 58 | Sonatype OSS Snapshots Repository 59 | https://oss.sonatype.org/content/repositories/snapshots 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | Requirements of this demo application are follows: 4 | 5 | * Docker 6 | * GraalVM 22.3+ 7 | * JDK 17+ 8 | * Spring Boot 3 9 | * MyBatis Spring Boot 3.0.1+ 10 | 11 | # Samples 12 | 13 | * Writing SQLs on annotation 14 | * Writing SQLs on XML file 15 | 16 | # How to build 17 | 18 | Start docker container for building native image. 19 | 20 | ``` 21 | docker run -v $(pwd):/work -v $HOME/.m2:/root/.m2 -it -w /work \ 22 | -e JAVA_HOME=/opt/graalvm-ce-java17-22.3.2 \ 23 | -e GRAALVM_HOME=/opt/graalvm-ce-java17-22.3.2 \ 24 | -e LANG=C.utf8 \ 25 | ghcr.io/graalvm/graalvm-ce:22.3.2 bash 26 | ``` 27 | 28 | Build a native image. 29 | 30 | ``` 31 | ./mvnw -Pnative clean native:compile 32 | ``` 33 | 34 | # Hot to run 35 | 36 | Run the native image. 37 | 38 | ``` 39 | ./target/mybatis-native-demo -Djavax.xml.accessExternalDTD=all 40 | ``` 41 | 42 | Print as following logs if building was success. 43 | 44 | ``` 45 | . ____ _ __ _ _ 46 | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ 47 | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ 48 | \\/ ___)| |_)| | | | | || (_| | ) ) ) ) 49 | ' |____| .__|_| |_|_| |_\__, | / / / / 50 | =========|_|==============|___/=/_/_/_/ 51 | :: Spring Boot :: (v3.0.6) 52 | 53 | 2023-05-08T18:22:46.044Z INFO 2383 --- [ main] c.e.nativedemo.NativeDemoApplication : Starting AOT-processed NativeDemoApplication using Java 17.0.7 with PID 2383 (/work/target/mybatis-native-demo started by root in /work) 54 | 2023-05-08T18:22:46.044Z INFO 2383 --- [ main] c.e.nativedemo.NativeDemoApplication : No active profile set, falling back to 1 default profile: "default" 55 | 2023-05-08T18:22:46.060Z INFO 2383 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 56 | 2023-05-08T18:22:46.065Z INFO 2383 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:649c2bb8-f60b-4e7c-9f01-6a1ed37721d1 user=SA 57 | 2023-05-08T18:22:46.066Z INFO 2383 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 58 | 2023-05-08T18:22:46.131Z INFO 2383 --- [ main] c.e.nativedemo.NativeDemoApplication : Started NativeDemoApplication in 0.147 seconds (process running for 0.155) 59 | Message[id=1, message=Hello World on run!] 60 | Message[id=2, message=Hello World! on runWithXmlMapper] 61 | 2023-05-08T18:22:46.136Z INFO 2383 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 62 | 2023-05-08T18:22:46.136Z INFO 2383 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. 63 | ``` 64 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /usr/local/etc/mavenrc ] ; then 40 | . /usr/local/etc/mavenrc 41 | fi 42 | 43 | if [ -f /etc/mavenrc ] ; then 44 | . /etc/mavenrc 45 | fi 46 | 47 | if [ -f "$HOME/.mavenrc" ] ; then 48 | . "$HOME/.mavenrc" 49 | fi 50 | 51 | fi 52 | 53 | # OS specific support. $var _must_ be set to either true or false. 54 | cygwin=false; 55 | darwin=false; 56 | mingw=false 57 | case "`uname`" in 58 | CYGWIN*) cygwin=true ;; 59 | MINGW*) mingw=true;; 60 | Darwin*) darwin=true 61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 63 | if [ -z "$JAVA_HOME" ]; then 64 | if [ -x "/usr/libexec/java_home" ]; then 65 | export JAVA_HOME="`/usr/libexec/java_home`" 66 | else 67 | export JAVA_HOME="/Library/Java/Home" 68 | fi 69 | fi 70 | ;; 71 | esac 72 | 73 | if [ -z "$JAVA_HOME" ] ; then 74 | if [ -r /etc/gentoo-release ] ; then 75 | JAVA_HOME=`java-config --jre-home` 76 | fi 77 | fi 78 | 79 | if [ -z "$M2_HOME" ] ; then 80 | ## resolve links - $0 may be a link to maven's home 81 | PRG="$0" 82 | 83 | # need this for relative symlinks 84 | while [ -h "$PRG" ] ; do 85 | ls=`ls -ld "$PRG"` 86 | link=`expr "$ls" : '.*-> \(.*\)$'` 87 | if expr "$link" : '/.*' > /dev/null; then 88 | PRG="$link" 89 | else 90 | PRG="`dirname "$PRG"`/$link" 91 | fi 92 | done 93 | 94 | saveddir=`pwd` 95 | 96 | M2_HOME=`dirname "$PRG"`/.. 97 | 98 | # make it fully qualified 99 | M2_HOME=`cd "$M2_HOME" && pwd` 100 | 101 | cd "$saveddir" 102 | # echo Using m2 at $M2_HOME 103 | fi 104 | 105 | # For Cygwin, ensure paths are in UNIX format before anything is touched 106 | if $cygwin ; then 107 | [ -n "$M2_HOME" ] && 108 | M2_HOME=`cygpath --unix "$M2_HOME"` 109 | [ -n "$JAVA_HOME" ] && 110 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 111 | [ -n "$CLASSPATH" ] && 112 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 113 | fi 114 | 115 | # For Mingw, ensure paths are in UNIX format before anything is touched 116 | if $mingw ; then 117 | [ -n "$M2_HOME" ] && 118 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 119 | [ -n "$JAVA_HOME" ] && 120 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 121 | fi 122 | 123 | if [ -z "$JAVA_HOME" ]; then 124 | javaExecutable="`which javac`" 125 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 126 | # readlink(1) is not available as standard on Solaris 10. 127 | readLink=`which readlink` 128 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 129 | if $darwin ; then 130 | javaHome="`dirname \"$javaExecutable\"`" 131 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 132 | else 133 | javaExecutable="`readlink -f \"$javaExecutable\"`" 134 | fi 135 | javaHome="`dirname \"$javaExecutable\"`" 136 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 137 | JAVA_HOME="$javaHome" 138 | export JAVA_HOME 139 | fi 140 | fi 141 | fi 142 | 143 | if [ -z "$JAVACMD" ] ; then 144 | if [ -n "$JAVA_HOME" ] ; then 145 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 146 | # IBM's JDK on AIX uses strange locations for the executables 147 | JAVACMD="$JAVA_HOME/jre/sh/java" 148 | else 149 | JAVACMD="$JAVA_HOME/bin/java" 150 | fi 151 | else 152 | JAVACMD="`\\unset -f command; \\command -v java`" 153 | fi 154 | fi 155 | 156 | if [ ! -x "$JAVACMD" ] ; then 157 | echo "Error: JAVA_HOME is not defined correctly." >&2 158 | echo " We cannot execute $JAVACMD" >&2 159 | exit 1 160 | fi 161 | 162 | if [ -z "$JAVA_HOME" ] ; then 163 | echo "Warning: JAVA_HOME environment variable is not set." 164 | fi 165 | 166 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 167 | 168 | # traverses directory structure from process work directory to filesystem root 169 | # first directory with .mvn subdirectory is considered project base directory 170 | find_maven_basedir() { 171 | 172 | if [ -z "$1" ] 173 | then 174 | echo "Path not specified to find_maven_basedir" 175 | return 1 176 | fi 177 | 178 | basedir="$1" 179 | wdir="$1" 180 | while [ "$wdir" != '/' ] ; do 181 | if [ -d "$wdir"/.mvn ] ; then 182 | basedir=$wdir 183 | break 184 | fi 185 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 186 | if [ -d "${wdir}" ]; then 187 | wdir=`cd "$wdir/.."; pwd` 188 | fi 189 | # end of workaround 190 | done 191 | echo "${basedir}" 192 | } 193 | 194 | # concatenates all lines of a file 195 | concat_lines() { 196 | if [ -f "$1" ]; then 197 | echo "$(tr -s '\n' ' ' < "$1")" 198 | fi 199 | } 200 | 201 | BASE_DIR=`find_maven_basedir "$(pwd)"` 202 | if [ -z "$BASE_DIR" ]; then 203 | exit 1; 204 | fi 205 | 206 | ########################################################################################## 207 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 208 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 209 | ########################################################################################## 210 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Found .mvn/wrapper/maven-wrapper.jar" 213 | fi 214 | else 215 | if [ "$MVNW_VERBOSE" = true ]; then 216 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 217 | fi 218 | if [ -n "$MVNW_REPOURL" ]; then 219 | jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 220 | else 221 | jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 222 | fi 223 | while IFS="=" read key value; do 224 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 225 | esac 226 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 227 | if [ "$MVNW_VERBOSE" = true ]; then 228 | echo "Downloading from: $jarUrl" 229 | fi 230 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 231 | if $cygwin; then 232 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 233 | fi 234 | 235 | if command -v wget > /dev/null; then 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Found wget ... using wget" 238 | fi 239 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 240 | wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 241 | else 242 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 243 | fi 244 | elif command -v curl > /dev/null; then 245 | if [ "$MVNW_VERBOSE" = true ]; then 246 | echo "Found curl ... using curl" 247 | fi 248 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 249 | curl -o "$wrapperJarPath" "$jarUrl" -f 250 | else 251 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 252 | fi 253 | 254 | else 255 | if [ "$MVNW_VERBOSE" = true ]; then 256 | echo "Falling back to using Java to download" 257 | fi 258 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 259 | # For Cygwin, switch paths to Windows format before running javac 260 | if $cygwin; then 261 | javaClass=`cygpath --path --windows "$javaClass"` 262 | fi 263 | if [ -e "$javaClass" ]; then 264 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 265 | if [ "$MVNW_VERBOSE" = true ]; then 266 | echo " - Compiling MavenWrapperDownloader.java ..." 267 | fi 268 | # Compiling the Java class 269 | ("$JAVA_HOME/bin/javac" "$javaClass") 270 | fi 271 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 272 | # Running the downloader 273 | if [ "$MVNW_VERBOSE" = true ]; then 274 | echo " - Running MavenWrapperDownloader.java ..." 275 | fi 276 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 277 | fi 278 | fi 279 | fi 280 | fi 281 | ########################################################################################## 282 | # End of extension 283 | ########################################################################################## 284 | 285 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 286 | if [ "$MVNW_VERBOSE" = true ]; then 287 | echo $MAVEN_PROJECTBASEDIR 288 | fi 289 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 290 | 291 | # For Cygwin, switch paths to Windows format before running java 292 | if $cygwin; then 293 | [ -n "$M2_HOME" ] && 294 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 295 | [ -n "$JAVA_HOME" ] && 296 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 297 | [ -n "$CLASSPATH" ] && 298 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 299 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 300 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 301 | fi 302 | 303 | # Provide a "standardized" way to retrieve the CLI args that will 304 | # work with both Windows and non-Windows executions. 305 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 306 | export MAVEN_CMD_LINE_ARGS 307 | 308 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 309 | 310 | exec "$JAVACMD" \ 311 | $MAVEN_OPTS \ 312 | $MAVEN_DEBUG_OPTS \ 313 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 314 | "-Dmaven.home=${M2_HOME}" \ 315 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 316 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 317 | -------------------------------------------------------------------------------- /src/main/java/com/example/nativedemo/MyBatisNativeConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.example.nativedemo; 2 | 3 | import org.apache.commons.logging.LogFactory; 4 | import org.apache.ibatis.annotations.DeleteProvider; 5 | import org.apache.ibatis.annotations.InsertProvider; 6 | import org.apache.ibatis.annotations.SelectProvider; 7 | import org.apache.ibatis.annotations.UpdateProvider; 8 | import org.apache.ibatis.cache.decorators.FifoCache; 9 | import org.apache.ibatis.cache.decorators.LruCache; 10 | import org.apache.ibatis.cache.decorators.SoftCache; 11 | import org.apache.ibatis.cache.decorators.WeakCache; 12 | import org.apache.ibatis.cache.impl.PerpetualCache; 13 | import org.apache.ibatis.javassist.util.proxy.ProxyFactory; 14 | import org.apache.ibatis.javassist.util.proxy.RuntimeSupport; 15 | import org.apache.ibatis.logging.Log; 16 | import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl; 17 | import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl; 18 | import org.apache.ibatis.logging.log4j2.Log4j2Impl; 19 | import org.apache.ibatis.logging.nologging.NoLoggingImpl; 20 | import org.apache.ibatis.logging.slf4j.Slf4jImpl; 21 | import org.apache.ibatis.logging.stdout.StdOutImpl; 22 | import org.apache.ibatis.reflection.TypeParameterResolver; 23 | import org.apache.ibatis.scripting.defaults.RawLanguageDriver; 24 | import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver; 25 | import org.apache.ibatis.session.SqlSessionFactory; 26 | import org.mybatis.spring.SqlSessionFactoryBean; 27 | import org.mybatis.spring.mapper.MapperFactoryBean; 28 | import org.mybatis.spring.mapper.MapperScannerConfigurer; 29 | import org.springframework.aot.hint.MemberCategory; 30 | import org.springframework.aot.hint.RuntimeHints; 31 | import org.springframework.aot.hint.RuntimeHintsRegistrar; 32 | import org.springframework.beans.PropertyValue; 33 | import org.springframework.beans.factory.BeanFactory; 34 | import org.springframework.beans.factory.BeanFactoryAware; 35 | import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; 36 | import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; 37 | import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; 38 | import org.springframework.beans.factory.config.BeanDefinition; 39 | import org.springframework.beans.factory.config.ConfigurableBeanFactory; 40 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 41 | import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; 42 | import org.springframework.beans.factory.support.RegisteredBean; 43 | import org.springframework.beans.factory.support.RootBeanDefinition; 44 | import org.springframework.context.annotation.Bean; 45 | import org.springframework.context.annotation.Configuration; 46 | import org.springframework.context.annotation.ImportRuntimeHints; 47 | import org.springframework.core.ResolvableType; 48 | import org.springframework.util.ClassUtils; 49 | import org.springframework.util.ReflectionUtils; 50 | 51 | import java.lang.annotation.Annotation; 52 | import java.lang.reflect.Method; 53 | import java.lang.reflect.ParameterizedType; 54 | import java.lang.reflect.Type; 55 | import java.util.ArrayList; 56 | import java.util.HashMap; 57 | import java.util.HashSet; 58 | import java.util.Map; 59 | import java.util.Set; 60 | import java.util.TreeSet; 61 | import java.util.function.Function; 62 | import java.util.stream.Collectors; 63 | import java.util.stream.Stream; 64 | 65 | /** 66 | * This configuration will move to mybatis-spring-native. 67 | */ 68 | @Configuration(proxyBeanMethods = false) 69 | @ImportRuntimeHints(MyBatisNativeConfiguration.MyBaitsRuntimeHintsRegistrar.class) 70 | public class MyBatisNativeConfiguration { 71 | 72 | @Bean 73 | MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() { 74 | return new MyBatisBeanFactoryInitializationAotProcessor(); 75 | } 76 | 77 | @Bean 78 | static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() { 79 | return new MyBatisMapperFactoryBeanPostProcessor(); 80 | } 81 | 82 | static class MyBaitsRuntimeHintsRegistrar implements RuntimeHintsRegistrar { 83 | 84 | @Override 85 | public void registerHints(RuntimeHints hints, ClassLoader classLoader) { 86 | Stream.of(RawLanguageDriver.class, 87 | XMLLanguageDriver.class, 88 | RuntimeSupport.class, 89 | ProxyFactory.class, 90 | Slf4jImpl.class, 91 | Log.class, 92 | JakartaCommonsLoggingImpl.class, 93 | Log4j2Impl.class, 94 | Jdk14LoggingImpl.class, 95 | StdOutImpl.class, 96 | NoLoggingImpl.class, 97 | SqlSessionFactory.class, 98 | PerpetualCache.class, 99 | FifoCache.class, 100 | LruCache.class, 101 | SoftCache.class, 102 | WeakCache.class, 103 | SqlSessionFactoryBean.class, 104 | ArrayList.class, 105 | HashMap.class, 106 | TreeSet.class, 107 | HashSet.class 108 | ).forEach(x -> hints.reflection().registerType(x, MemberCategory.values())); 109 | Stream.of( 110 | "org/apache/ibatis/builder/xml/.*.dtd", 111 | "org/apache/ibatis/builder/xml/.*.xsd" 112 | ).forEach(hints.resources()::registerPattern); 113 | } 114 | } 115 | 116 | static class MyBatisBeanFactoryInitializationAotProcessor 117 | implements BeanFactoryInitializationAotProcessor, BeanRegistrationExcludeFilter { 118 | 119 | private final Set> excludeClasses = new HashSet<>(); 120 | 121 | MyBatisBeanFactoryInitializationAotProcessor() { 122 | excludeClasses.add(MapperScannerConfigurer.class); 123 | } 124 | 125 | @Override public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { 126 | return excludeClasses.contains(registeredBean.getBeanClass()); 127 | } 128 | 129 | @Override 130 | public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { 131 | String[] beanNames = beanFactory.getBeanNamesForType(MapperFactoryBean.class); 132 | if (beanNames.length == 0) { 133 | return null; 134 | } 135 | return (context, code) -> { 136 | RuntimeHints hints = context.getRuntimeHints(); 137 | for (String beanName : beanNames) { 138 | BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName.substring(1)); 139 | PropertyValue mapperInterface = beanDefinition.getPropertyValues().getPropertyValue("mapperInterface"); 140 | if (mapperInterface != null && mapperInterface.getValue() != null) { 141 | Class mapperInterfaceType = (Class) mapperInterface.getValue(); 142 | if (mapperInterfaceType != null) { 143 | registerReflectionTypeIfNecessary(mapperInterfaceType, hints); 144 | hints.proxies().registerJdkProxy(mapperInterfaceType); 145 | hints.resources() 146 | .registerPattern(mapperInterfaceType.getName().replace('.', '/').concat(".xml")); 147 | registerMapperRelationships(mapperInterfaceType, hints); 148 | } 149 | } 150 | } 151 | }; 152 | } 153 | 154 | private void registerMapperRelationships(Class mapperInterfaceType, RuntimeHints hints) { 155 | Method[] methods = ReflectionUtils.getAllDeclaredMethods(mapperInterfaceType); 156 | for (Method method : methods) { 157 | if (method.getDeclaringClass() != Object.class) { 158 | ReflectionUtils.makeAccessible(method); 159 | registerSqlProviderTypes(method, hints, SelectProvider.class, SelectProvider::value, SelectProvider::type); 160 | registerSqlProviderTypes(method, hints, InsertProvider.class, InsertProvider::value, InsertProvider::type); 161 | registerSqlProviderTypes(method, hints, UpdateProvider.class, UpdateProvider::value, UpdateProvider::type); 162 | registerSqlProviderTypes(method, hints, DeleteProvider.class, DeleteProvider::value, DeleteProvider::type); 163 | Class returnType = MyBatisMapperTypeUtils.resolveReturnClass(mapperInterfaceType, method); 164 | registerReflectionTypeIfNecessary(returnType, hints); 165 | MyBatisMapperTypeUtils.resolveParameterClasses(mapperInterfaceType, method) 166 | .forEach(x -> registerReflectionTypeIfNecessary(x, hints)); 167 | } 168 | } 169 | } 170 | 171 | @SafeVarargs 172 | private void registerSqlProviderTypes( 173 | Method method, RuntimeHints hints, Class annotationType, Function>... providerTypeResolvers) { 174 | for (T annotation : method.getAnnotationsByType(annotationType)) { 175 | for (Function> providerTypeResolver : providerTypeResolvers) { 176 | registerReflectionTypeIfNecessary(providerTypeResolver.apply(annotation), hints); 177 | } 178 | } 179 | } 180 | 181 | private void registerReflectionTypeIfNecessary(Class type, RuntimeHints hints) { 182 | if (!type.isPrimitive() && !type.getName().startsWith("java")) { 183 | hints.reflection().registerType(type, MemberCategory.values()); 184 | } 185 | } 186 | 187 | } 188 | 189 | static class MyBatisMapperTypeUtils { 190 | private MyBatisMapperTypeUtils() { 191 | // NOP 192 | } 193 | 194 | static Class resolveReturnClass(Class mapperInterface, Method method) { 195 | Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); 196 | return typeToClass(resolvedReturnType, method.getReturnType()); 197 | } 198 | 199 | static Set> resolveParameterClasses(Class mapperInterface, Method method) { 200 | return Stream.of(TypeParameterResolver.resolveParamTypes(method, mapperInterface)) 201 | .map(x -> typeToClass(x, x instanceof Class ? (Class) x : Object.class)).collect(Collectors.toSet()); 202 | } 203 | 204 | private static Class typeToClass(Type src, Class fallback) { 205 | Class result = null; 206 | if (src instanceof Class) { 207 | if (((Class) src).isArray()) { 208 | result = ((Class) src).getComponentType(); 209 | } else { 210 | result = (Class) src; 211 | } 212 | } else if (src instanceof ParameterizedType) { 213 | ParameterizedType parameterizedType = (ParameterizedType) src; 214 | int index = (parameterizedType.getRawType() instanceof Class 215 | && Map.class.isAssignableFrom((Class) parameterizedType.getRawType()) 216 | && parameterizedType.getActualTypeArguments().length > 1) ? 1 : 0; 217 | Type actualType = parameterizedType.getActualTypeArguments()[index]; 218 | result = typeToClass(actualType, fallback); 219 | } 220 | if (result == null) { 221 | result = fallback; 222 | } 223 | return result; 224 | } 225 | 226 | } 227 | 228 | static class MyBatisMapperFactoryBeanPostProcessor implements MergedBeanDefinitionPostProcessor, BeanFactoryAware { 229 | 230 | private static final org.apache.commons.logging.Log LOG = LogFactory.getLog( 231 | MyBatisMapperFactoryBeanPostProcessor.class); 232 | 233 | private static final String MAPPER_FACTORY_BEAN = "org.mybatis.spring.mapper.MapperFactoryBean"; 234 | 235 | private ConfigurableBeanFactory beanFactory; 236 | 237 | @Override 238 | public void setBeanFactory(BeanFactory beanFactory) { 239 | this.beanFactory = (ConfigurableBeanFactory) beanFactory; 240 | } 241 | 242 | @Override 243 | public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { 244 | if (ClassUtils.isPresent(MAPPER_FACTORY_BEAN, this.beanFactory.getBeanClassLoader())) { 245 | resolveMapperFactoryBeanTypeIfNecessary(beanDefinition); 246 | } 247 | } 248 | 249 | private void resolveMapperFactoryBeanTypeIfNecessary(RootBeanDefinition beanDefinition) { 250 | if (!beanDefinition.hasBeanClass() || !MapperFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())) { 251 | return; 252 | } 253 | if (beanDefinition.getResolvableType().hasUnresolvableGenerics()) { 254 | Class mapperInterface = getMapperInterface(beanDefinition); 255 | if (mapperInterface != null) { 256 | // Exposes a generic type information to context for prevent early initializing 257 | beanDefinition 258 | .setTargetType(ResolvableType.forClassWithGenerics(beanDefinition.getBeanClass(), mapperInterface)); 259 | } 260 | } 261 | } 262 | 263 | private Class getMapperInterface(RootBeanDefinition beanDefinition) { 264 | try { 265 | return (Class) beanDefinition.getPropertyValues().get("mapperInterface"); 266 | } 267 | catch (Exception e) { 268 | LOG.debug("Fail getting mapper interface type.", e); 269 | return null; 270 | } 271 | } 272 | 273 | } 274 | } 275 | --------------------------------------------------------------------------------