From 337e6b0ab472470d51a2e4527472a83c7b236710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=99=93=E4=BC=9F?= Date: Wed, 7 Aug 2024 21:20:03 +0800 Subject: [PATCH] :sparkles: Support annotation based on MyBatis for Spring Boot 3 Native https://github.com/mybatis/spring-boot-starter/issues/994 --- .github/workflows/native.yaml | 73 +++++ ...BeanFactoryInitializationAotProcessor.java | 138 ++++++++++ ...MyBatisMapperFactoryBeanPostProcessor.java | 87 ++++++ .../mybatis-reflection.json | 14 + .../native-image.properties | 17 ++ .../resources/META-INF/spring/aot.factories | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../LICENSE_HEADER | 13 + .../format.xml | 22 ++ .../pom.xml | 176 ++++++++++++ .../SampleAnnotationApplication.java | 43 +++ .../graalvm/annotation/domain/City.java | 72 +++++ .../graalvm/annotation/mapper/CityMapper.java | 33 +++ .../src/main/resources/application.properties | 18 ++ .../src/main/resources/data.sql | 17 ++ .../src/main/resources/schema.sql | 20 ++ .../annotation/CaptureSystemOutput.java | 255 ++++++++++++++++++ .../SampleMybatisApplicationMainTest.java | 38 +++ .../SampleMybatisApplicationTest.java | 40 +++ .../annotation/mapper/CityMapperTest.java | 48 ++++ .../mapper/MapperTestApplication.java | 34 +++ mybatis-spring-boot-samples/pom.xml | 3 +- pom.xml | 1 + 23 files changed, 1164 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/native.yaml create mode 100644 mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MyBatisBeanFactoryInitializationAotProcessor.java create mode 100644 mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MyBatisMapperFactoryBeanPostProcessor.java create mode 100644 mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/native-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/mybatis-reflection.json create mode 100644 mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/native-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/native-image.properties create mode 100644 mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/LICENSE_HEADER create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/format.xml create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/pom.xml create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/SampleAnnotationApplication.java create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/domain/City.java create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/mapper/CityMapper.java create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/application.properties create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/data.sql create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/schema.sql create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/extensions/annotation/CaptureSystemOutput.java create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/SampleMybatisApplicationMainTest.java create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/SampleMybatisApplicationTest.java create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/mapper/CityMapperTest.java create mode 100644 mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/mapper/MapperTestApplication.java diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml new file mode 100644 index 000000000..9a911e3a5 --- /dev/null +++ b/.github/workflows/native.yaml @@ -0,0 +1,73 @@ +# +# Copyright 2015-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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "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. +# + +name: Spring Boot Native Support Samples + +on: [ push, pull_request ] + +jobs: + test-ubuntu: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, macOS-latest ] + java: [ 17.0.9, 21.0.2, 22.0.2 ] + fail-fast: false + max-parallel: 5 + name: Test GraalVM JDK ${{ matrix.java }}, ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Ubuntu Set up JDK + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + JAVA_HOME=$RUNNER_WORKSPACE/.graalvm + echo $JAVA_HOME + mkdir -p $JAVA_HOME + echo "JAVA_HOME=$JAVA_HOME" >> $GITHUB_ENV + curl -L -o graalvm.tar.gz https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${{ matrix.java }}/graalvm-community-jdk-${{ matrix.java }}_linux-x64_bin.tar.gz + tar -zxvf graalvm.tar.gz -C $JAVA_HOME --strip-components=1 + ls -lh $JAVA_HOME + mvn -v + + - name: MacOS Set up JDK + if: ${{ matrix.os == 'macOS-latest' }} + run: | + JAVA_HOME=$RUNNER_WORKSPACE/.graalvm + echo $JAVA_HOME + mkdir -p $JAVA_HOME + curl -L -o graalvm.tar.gz https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-${{ matrix.java }}/graalvm-community-jdk-${{ matrix.java }}_macos-x64_bin.tar.gz + tar -zxvf graalvm.tar.gz -C $JAVA_HOME --strip-components=1 + JAVA_HOME=$RUNNER_WORKSPACE/.graalvm/Contents/Home + echo $JAVA_HOME + echo "JAVA_HOME=$JAVA_HOME" >> $GITHUB_ENV + ls -lh $JAVA_HOME + mvn -v + + - name: Ubuntu Prerequisites + if: ${{ matrix.os == 'ubuntu-latest' }} + run: sudo apt-get install build-essential zlib1g-dev + + #- name: MacOS Prerequisites + # if: ${{ matrix.os == 'macOS-latest' }} + # run: xcode-select --install + + - name: Test with Spring Boot Native Latest + run: ./mvnw -V compile -Pnative native:compile -am -pl mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation + + - name: Run Native Latest + run: mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/target/mybatis-spring-boot-sample-graalvm-annotation diff --git a/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MyBatisBeanFactoryInitializationAotProcessor.java b/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MyBatisBeanFactoryInitializationAotProcessor.java new file mode 100644 index 000000000..24c910c4f --- /dev/null +++ b/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MyBatisBeanFactoryInitializationAotProcessor.java @@ -0,0 +1,138 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mybatis.spring.boot.autoconfigure; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.ibatis.reflection.TypeParameterResolver; +import org.mybatis.spring.mapper.MapperFactoryBean; +import org.mybatis.spring.mapper.MapperScannerConfigurer; +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; +import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; +import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.RegisteredBean; +import org.springframework.util.ReflectionUtils; + +/** + * @since 3.0.4 + */ +class MyBatisBeanFactoryInitializationAotProcessor + implements BeanFactoryInitializationAotProcessor, BeanRegistrationExcludeFilter { + + private static final Set> EXCLUDE_CLASSES = new HashSet<>(); + + static { + EXCLUDE_CLASSES.add(MapperScannerConfigurer.class); + } + + @Override + public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { + String[] beanNames = beanFactory.getBeanNamesForType(MapperFactoryBean.class); + if (beanNames.length == 0) { + return null; + } + return (context, code) -> { + RuntimeHints hints = context.getRuntimeHints(); + for (String beanName : beanNames) { + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName.substring(1)); + PropertyValue mapperInterface = beanDefinition.getPropertyValues().getPropertyValue("mapperInterface"); + if (mapperInterface != null && mapperInterface.getValue() != null) { + Class mapperInterfaceType = (Class) mapperInterface.getValue(); + if (mapperInterfaceType != null) { + registerReflectionTypeIfNecessary(mapperInterfaceType, hints); + hints.proxies().registerJdkProxy(mapperInterfaceType); + registerMapperRelationships(mapperInterfaceType, hints); + } + } + } + }; + } + + @Override + public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { + return EXCLUDE_CLASSES.contains(registeredBean.getBeanClass()); + } + + private void registerMapperRelationships(Class mapperInterfaceType, RuntimeHints hints) { + Method[] methods = ReflectionUtils.getAllDeclaredMethods(mapperInterfaceType); + for (Method method : methods) { + if (method.getDeclaringClass() != Object.class) { + ReflectionUtils.makeAccessible(method); + Class returnType = MyBatisMapperTypeUtils.resolveReturnClass(mapperInterfaceType, method); + registerReflectionTypeIfNecessary(returnType, hints); + MyBatisMapperTypeUtils.resolveParameterClasses(mapperInterfaceType, method) + .forEach(x -> registerReflectionTypeIfNecessary(x, hints)); + } + } + } + + static class MyBatisMapperTypeUtils { + private MyBatisMapperTypeUtils() { + // NOP + } + + static Class resolveReturnClass(Class mapperInterface, Method method) { + Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); + return typeToClass(resolvedReturnType, method.getReturnType()); + } + + static Set> resolveParameterClasses(Class mapperInterface, Method method) { + return Stream.of(TypeParameterResolver.resolveParamTypes(method, mapperInterface)) + .map(x -> typeToClass(x, x instanceof Class ? (Class) x : Object.class)).collect(Collectors.toSet()); + } + + private static Class typeToClass(Type src, Class fallback) { + Class result = null; + if (src instanceof Class) { + if (((Class) src).isArray()) { + result = ((Class) src).getComponentType(); + } else { + result = (Class) src; + } + } else if (src instanceof ParameterizedType parameterizedType) { + int index = (parameterizedType.getRawType() instanceof Class + && Map.class.isAssignableFrom((Class) parameterizedType.getRawType()) + && parameterizedType.getActualTypeArguments().length > 1) ? 1 : 0; + Type actualType = parameterizedType.getActualTypeArguments()[index]; + result = typeToClass(actualType, fallback); + } + if (result == null) { + result = fallback; + } + return result; + } + + } + + private void registerReflectionTypeIfNecessary(Class type, RuntimeHints hints) { + if (!type.isPrimitive() && !type.getName().startsWith("java")) { + hints.reflection().registerType(type, MemberCategory.values()); + } + } + +} diff --git a/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MyBatisMapperFactoryBeanPostProcessor.java b/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MyBatisMapperFactoryBeanPostProcessor.java new file mode 100644 index 000000000..12d730a26 --- /dev/null +++ b/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MyBatisMapperFactoryBeanPostProcessor.java @@ -0,0 +1,87 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mybatis.spring.boot.autoconfigure; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.mybatis.spring.mapper.MapperFactoryBean; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.ResolvableType; +import org.springframework.util.ClassUtils; + +/** + * The {@code BeanDefinitionPostProcessor} for customizing a {@code MapperFactoryBean}. + * + * @author Stéphane Nicoll + * @author Kazuki Shimizu + * @author xuxiaowei + * + * @since 3.0.4 + */ +@Configuration +class MyBatisMapperFactoryBeanPostProcessor implements MergedBeanDefinitionPostProcessor, BeanFactoryAware { + + private static final Log LOG = LogFactory.getLog(MyBatisMapperFactoryBeanPostProcessor.class); + + private static final String MAPPER_FACTORY_BEAN = MapperFactoryBean.class.getName(); + + private ConfigurableBeanFactory beanFactory; + + @Override + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = (ConfigurableBeanFactory) beanFactory; + } + + @Override + public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { + if (ClassUtils.isPresent(MAPPER_FACTORY_BEAN, this.beanFactory.getBeanClassLoader())) { + resolveMapperFactoryBeanTypeIfNecessary(beanDefinition); + } + } + + private void resolveMapperFactoryBeanTypeIfNecessary(RootBeanDefinition beanDefinition) { + if (!beanDefinition.hasBeanClass() || !MapperFactoryBean.class.isAssignableFrom(beanDefinition.getBeanClass())) { + return; + } + if (beanDefinition.getResolvableType().hasUnresolvableGenerics()) { + Class mapperInterface = getMapperInterface(beanDefinition); + if (mapperInterface != null) { + // Exposes a generic type information to context for prevent early initializing + ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues(); + constructorArgumentValues.addGenericArgumentValue(mapperInterface); + beanDefinition.setConstructorArgumentValues(constructorArgumentValues); + beanDefinition + .setTargetType(ResolvableType.forClassWithGenerics(beanDefinition.getBeanClass(), mapperInterface)); + } + } + } + + private Class getMapperInterface(RootBeanDefinition beanDefinition) { + try { + return (Class) beanDefinition.getPropertyValues().get("mapperInterface"); + } catch (Exception e) { + LOG.debug("Fail getting mapper interface type.", e); + return null; + } + } + +} diff --git a/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/native-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/mybatis-reflection.json b/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/native-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/mybatis-reflection.json new file mode 100644 index 000000000..1549e08ea --- /dev/null +++ b/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/native-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/mybatis-reflection.json @@ -0,0 +1,14 @@ +[ + { "name": "org.apache.ibatis.logging.slf4j.Slf4jImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "org.apache.ibatis.logging.log4j2.Log4j2Impl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "org.apache.ibatis.logging.log4j.Log4jImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "org.apache.ibatis.logging.nologging.NoLoggingImpl", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "org.apache.ibatis.plugin.Interceptor", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "org.apache.ibatis.javassist.util.proxy.ProxyFactory", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "org.apache.ibatis.scripting.xmltags.XMLLanguageDriver", "methods": [{ "name": "", "parameterTypes": [] }] }, + { "name": "org.apache.ibatis.scripting.defaults.RawLanguageDriver", "methods": [{ "name": "", "parameterTypes": [] }] }, + { "name": "org.mybatis.spring.SqlSessionFactoryBean", "allDeclaredConstructors": true, "allDeclaredMethods": true }, + { "name": "java.util.ArrayList", "methods": [{ "name": "", "parameterTypes": [] }] } +] diff --git a/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/native-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/native-image.properties b/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/native-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/native-image.properties new file mode 100644 index 000000000..96994bb21 --- /dev/null +++ b/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/native-image/org.mybatis.spring.boot/mybatis-spring-boot-autoconfigure/native-image.properties @@ -0,0 +1,17 @@ +# +# Copyright 2015-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. +# 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. +# + +Args = -H:ReflectionConfigurationResources=${.}/mybatis-reflection.json diff --git a/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories b/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 000000000..c319033dd --- /dev/null +++ b/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ +org.mybatis.spring.boot.autoconfigure.MyBatisBeanFactoryInitializationAotProcessor diff --git a/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 2fad2b7c9..91a4e32f2 100644 --- a/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/mybatis-spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,3 @@ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration +org.mybatis.spring.boot.autoconfigure.MyBatisMapperFactoryBeanPostProcessor diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/LICENSE_HEADER b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/LICENSE_HEADER new file mode 100644 index 000000000..a81590a5b --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/LICENSE_HEADER @@ -0,0 +1,13 @@ + Copyright ${license.git.copyrightYears} 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. diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/format.xml b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/format.xml new file mode 100644 index 000000000..bad5a4711 --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/format.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/pom.xml b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/pom.xml new file mode 100644 index 000000000..b00677d9b --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/pom.xml @@ -0,0 +1,176 @@ + + + + 4.0.0 + + org.mybatis.spring.boot + mybatis-spring-boot-samples + 3.0.4-SNAPSHOT + + mybatis-spring-boot-sample-graalvm-annotation + jar + mybatis-spring-boot-sample-graalvm-annotation + + org.mybatis.spring.boot.sample.graalvmannotation + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.mybatis.spring.boot + mybatis-spring-boot-starter-test + test + + + + + + org.graalvm.buildtools + native-maven-plugin + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + + native + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + paketobuildpacks/builder-jammy-tiny:latest + + true + + + + + + process-aot + + process-aot + + + + + + org.graalvm.buildtools + native-maven-plugin + ${native-build-tools-plugin.version} + + ${project.build.outputDirectory} + 22.3 + + + + add-reachability-metadata + + add-reachability-metadata + + + + + + + + + + nativeTest + + + org.junit.platform + junit-platform-launcher + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + process-test-aot + + process-test-aot + + + + + + org.graalvm.buildtools + native-maven-plugin + ${native-build-tools-plugin.version} + + ${project.build.outputDirectory} + 22.3 + + + + native-test + + test + + + + + + + + + diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/SampleAnnotationApplication.java b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/SampleAnnotationApplication.java new file mode 100644 index 000000000..f79a9d8da --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/SampleAnnotationApplication.java @@ -0,0 +1,43 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sample.mybatis.graalvm.annotation; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import sample.mybatis.graalvm.annotation.mapper.CityMapper; + +@SpringBootApplication +public class SampleAnnotationApplication implements CommandLineRunner { + + public static void main(String[] args) { + SpringApplication.run(SampleAnnotationApplication.class, args); + } + + private final CityMapper cityMapper; + + public SampleAnnotationApplication(CityMapper cityMapper) { + this.cityMapper = cityMapper; + } + + @Override + @SuppressWarnings("squid:S106") + public void run(String... args) { + System.out.println(this.cityMapper.findByState("CA")); + } + +} diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/domain/City.java b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/domain/City.java new file mode 100644 index 000000000..67764b598 --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/domain/City.java @@ -0,0 +1,72 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sample.mybatis.graalvm.annotation.domain; + +import java.io.Serializable; + +/** + * @author Eddú Meléndez + */ +public class City implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long id; + + private String name; + + private String state; + + private String country; + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getState() { + return this.state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCountry() { + return this.country; + } + + public void setCountry(String country) { + this.country = country; + } + + @Override + public String toString() { + return getId() + "," + getName() + "," + getState() + "," + getCountry(); + } + +} diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/mapper/CityMapper.java b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/mapper/CityMapper.java new file mode 100644 index 000000000..a6a8449cf --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/java/sample/mybatis/graalvm/annotation/mapper/CityMapper.java @@ -0,0 +1,33 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sample.mybatis.graalvm.annotation.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import sample.mybatis.graalvm.annotation.domain.City; + +/** + * @author Eddú Meléndez + */ +@Mapper +public interface CityMapper { + + @Select("select id, name, state, country from city where state = #{state}") + City findByState(@Param("state") String state); + +} diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/application.properties b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/application.properties new file mode 100644 index 000000000..2845bb39c --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/application.properties @@ -0,0 +1,18 @@ +# +# Copyright 2015-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. +# 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. +# + +logging.level.root=WARN +logging.level.sample.mybatis.graalvm.annotation.mapper=TRACE diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/data.sql b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/data.sql new file mode 100644 index 000000000..12ddae3b8 --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/data.sql @@ -0,0 +1,17 @@ +-- +-- Copyright 2015-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. +-- 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. +-- + +insert into city (name, state, country) values ('San Francisco', 'CA', 'US'); diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/schema.sql b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/schema.sql new file mode 100644 index 000000000..b3e263bb8 --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/main/resources/schema.sql @@ -0,0 +1,20 @@ +-- +-- Copyright 2015-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. +-- 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. +-- + +drop table if exists city; + +create table city (id int primary key auto_increment, name varchar, state varchar, country varchar); + diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/extensions/annotation/CaptureSystemOutput.java b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/extensions/annotation/CaptureSystemOutput.java new file mode 100644 index 000000000..44c15a26b --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/extensions/annotation/CaptureSystemOutput.java @@ -0,0 +1,255 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package extensions.annotation; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.List; + +import org.hamcrest.Matcher; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.platform.commons.support.ReflectionSupport; + +/** + * {@code @CaptureSystemOutput} is a JUnit JUpiter extension for capturing output to {@code System.out} and + * {@code System.err} with expectations supported via Hamcrest matchers. + *

Example Usage

+ * + *
+ * {@literal @}Test
+ * {@literal @}CaptureSystemOutput
+ * void systemOut(OutputCapture outputCapture) {
+ *     outputCapture.expect(containsString("System.out!"));
+ *
+ *     System.out.println("Printed to System.out!");
+ * }
+ *
+ * {@literal @}Test
+ * {@literal @}CaptureSystemOutput
+ * void systemErr(OutputCapture outputCapture) {
+ *     outputCapture.expect(containsString("System.err!"));
+ *
+ *     System.err.println("Printed to System.err!");
+ * }
+ * 
+ *

+ * Based on code from Spring Boot's OutputCapture + * rule for JUnit 4 by Phillip Webb and Andy Wilkinson. + *

+ * Borrowing source from Sam Brannen as listed online at spring and stackoverflow from here CaptureSystemOutput + *

+ * Additional changes to Sam Brannen logic supplied by kazuki43zoo from here enhancement + * capture + * + * @author Sam Brannen + * @author Phillip Webb + * @author Andy Wilkinson + */ +@Target({ TYPE, METHOD }) +@Retention(RUNTIME) +@ExtendWith(CaptureSystemOutput.Extension.class) +public @interface CaptureSystemOutput { + + class Extension implements BeforeAllCallback, AfterAllCallback, AfterEachCallback, ParameterResolver { + + @Override + public void beforeAll(ExtensionContext context) { + getOutputCapture(context).captureOutput(); + } + + public void afterAll(ExtensionContext context) { + getOutputCapture(context).releaseOutput(); + } + + @Override + public void afterEach(ExtensionContext context) { + OutputCapture outputCapture = getOutputCapture(context); + try { + if (!outputCapture.matchers.isEmpty()) { + String output = outputCapture.toString(); + assertThat(output, allOf(outputCapture.matchers)); + } + } finally { + outputCapture.reset(); + } + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + boolean isTestMethodLevel = extensionContext.getTestMethod().isPresent(); + boolean isOutputCapture = parameterContext.getParameter().getType() == OutputCapture.class; + return isTestMethodLevel && isOutputCapture; + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return getOutputCapture(extensionContext); + } + + private OutputCapture getOutputCapture(ExtensionContext context) { + return getOrComputeIfAbsent(getStore(context), OutputCapture.class); + } + + private V getOrComputeIfAbsent(Store store, Class type) { + return store.getOrComputeIfAbsent(type, ReflectionSupport::newInstance, type); + } + + private Store getStore(ExtensionContext context) { + return context.getStore(Namespace.create(getClass())); + } + + } + + /** + * {@code OutputCapture} captures output to {@code System.out} and {@code System.err}. + *

+ * To obtain an instance of {@code OutputCapture}, declare a parameter of type {@code OutputCapture} in a JUnit + * Jupiter {@code @Test}, {@code @BeforeEach}, or {@code @AfterEach} method. + *

+ * {@linkplain #expect Expectations} are supported via Hamcrest matchers. + *

+ * To obtain all output to {@code System.out} and {@code System.err}, simply invoke {@link #toString()}. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @author Sam Brannen + */ + static class OutputCapture { + + private final List> matchers = new ArrayList<>(); + + private CaptureOutputStream captureOut; + + private CaptureOutputStream captureErr; + + private ByteArrayOutputStream copy; + + void captureOutput() { + this.copy = new ByteArrayOutputStream(); + this.captureOut = new CaptureOutputStream(System.out, this.copy); + this.captureErr = new CaptureOutputStream(System.err, this.copy); + System.setOut(new PrintStream(this.captureOut)); + System.setErr(new PrintStream(this.captureErr)); + } + + void releaseOutput() { + System.setOut(this.captureOut.getOriginal()); + System.setErr(this.captureErr.getOriginal()); + this.copy = null; + } + + private void flush() { + try { + this.captureOut.flush(); + this.captureErr.flush(); + } catch (IOException ex) { + // ignore + } + } + + /** + * Verify that the captured output is matched by the supplied {@code matcher}. + *

+ * Verification is performed after the test method has executed. + * + * @param matcher + * the matcher + */ + public void expect(Matcher matcher) { + this.matchers.add(matcher); + } + + /** + * Return all captured output to {@code System.out} and {@code System.err} as a single string. + */ + @Override + public String toString() { + flush(); + return this.copy.toString(); + } + + void reset() { + this.matchers.clear(); + this.copy.reset(); + } + + private static class CaptureOutputStream extends OutputStream { + + private final PrintStream original; + + private final OutputStream copy; + + CaptureOutputStream(PrintStream original, OutputStream copy) { + this.original = original; + this.copy = copy; + } + + PrintStream getOriginal() { + return this.original; + } + + @Override + public void write(int b) throws IOException { + this.copy.write(b); + this.original.write(b); + this.original.flush(); + } + + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + this.copy.write(b, off, len); + this.original.write(b, off, len); + } + + @Override + public void flush() throws IOException { + this.copy.flush(); + this.original.flush(); + } + + } + + } + +} diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/SampleMybatisApplicationMainTest.java b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/SampleMybatisApplicationMainTest.java new file mode 100644 index 000000000..5ccbf28c5 --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/SampleMybatisApplicationMainTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sample.mybatis.graalvm.annotation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import extensions.annotation.CaptureSystemOutput; +import extensions.annotation.CaptureSystemOutput.OutputCapture; + +/** + * @author Kazuki Shimizu + */ +@CaptureSystemOutput +class SampleMybatisApplicationMainTest { + + @Test + void test(OutputCapture outputCapture) { + SampleAnnotationApplication.main(new String[] {}); + String output = outputCapture.toString(); + assertThat(output).contains("1,San Francisco,CA,US"); + } + +} diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/SampleMybatisApplicationTest.java b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/SampleMybatisApplicationTest.java new file mode 100644 index 000000000..8b10b8db8 --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/SampleMybatisApplicationTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sample.mybatis.graalvm.annotation; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import extensions.annotation.CaptureSystemOutput; +import extensions.annotation.CaptureSystemOutput.OutputCapture; + +/** + * @author Eddú Meléndez + * @author Kazuki Shimizu + */ +@CaptureSystemOutput +@SpringBootTest +class SampleMybatisApplicationTest { + + @Test + void test(OutputCapture outputCapture) { + String output = outputCapture.toString(); + assertThat(output).contains("1,San Francisco,CA,US"); + } + +} diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/mapper/CityMapperTest.java b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/mapper/CityMapperTest.java new file mode 100644 index 000000000..209ec6fc2 --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/mapper/CityMapperTest.java @@ -0,0 +1,48 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sample.mybatis.graalvm.annotation.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; +import org.springframework.beans.factory.annotation.Autowired; + +import sample.mybatis.graalvm.annotation.domain.City; + +/** + * Tests for {@link CityMapper}. + * + * @author wonwoo + * + * @since 1.2.1 + */ +@MybatisTest +class CityMapperTest { + + @Autowired + private CityMapper cityMapper; + + @Test + void findByStateTest() { + City city = cityMapper.findByState("CA"); + assertThat(city.getId()).isEqualTo(1); + assertThat(city.getName()).isEqualTo("San Francisco"); + assertThat(city.getState()).isEqualTo("CA"); + assertThat(city.getCountry()).isEqualTo("US"); + } + +} diff --git a/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/mapper/MapperTestApplication.java b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/mapper/MapperTestApplication.java new file mode 100644 index 000000000..13f8768c6 --- /dev/null +++ b/mybatis-spring-boot-samples/mybatis-spring-boot-sample-graalvm-annotation/src/test/java/sample/mybatis/graalvm/annotation/mapper/MapperTestApplication.java @@ -0,0 +1,34 @@ +/* + * Copyright 2015-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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package sample.mybatis.graalvm.annotation.mapper; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * The Spring Boot Application for testing {@link org.mybatis.spring.boot.test.autoconfigure.MybatisTest @MybatisTest}. + *

+ * This class has role for prevent to run the {@link sample.mybatis.graalvm.annotation.SampleAnnotationApplication}. For + * more detail information, please refer + * Here. + * + * @author Kazuki Shimizu + * + * @since 1.2.1 + */ +@SpringBootApplication +public class MapperTestApplication { + +} diff --git a/mybatis-spring-boot-samples/pom.xml b/mybatis-spring-boot-samples/pom.xml index 14a9ab91f..f19c2be68 100644 --- a/mybatis-spring-boot-samples/pom.xml +++ b/mybatis-spring-boot-samples/pom.xml @@ -1,7 +1,7 @@