springboot3.2.4+Mybatis-plus在graalvm21环境下打包exe

springboot3.2.4+Mybatis-plus在graalvm21环境下打包exe

  • 前提条件为之前已经能直接打包springboot3.2.4项目了
  • 然后在此基础上接入Mybatis-plus,然后能够正常进行打包exe并且执行,参考之前的文章进行打包

核心配置如下

package com.example.demo.config;

import com.baomidou.mybatisplus.annotation.IEnum;
import com.baomidou.mybatisplus.core.MybatisParameterHandler;
import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler;
import com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.cache.decorators.FifoCache;
import org.apache.ibatis.cache.decorators.LruCache;
import org.apache.ibatis.cache.decorators.SoftCache;
import org.apache.ibatis.cache.decorators.WeakCache;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.javassist.util.proxy.ProxyFactory;
import org.apache.ibatis.javassist.util.proxy.RuntimeSupport;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.reflection.TypeParameterResolver;
import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
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.aot.hint.RuntimeHintsRegistrar;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
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.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author lyk
 * @since 2024-03-30
 */
@Configuration(proxyBeanMethods = false)
@ImportRuntimeHints(MyBatisNativeConfiguration.MyBaitsRuntimeHintsRegistrar.class)
public class MyBatisNativeConfiguration {

  @Bean
  MyBatisBeanFactoryInitializationAotProcessor myBatisBeanFactoryInitializationAotProcessor() {
    return new MyBatisBeanFactoryInitializationAotProcessor();
  }

  @Bean
  static MyBatisMapperFactoryBeanPostProcessor myBatisMapperFactoryBeanPostProcessor() {
    return new MyBatisMapperFactoryBeanPostProcessor();
  }

  static class MyBaitsRuntimeHintsRegistrar implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
      Stream.of(RawLanguageDriver.class,
          // TODO 增加了MybatisXMLLanguageDriver.class
          XMLLanguageDriver.class, MybatisXMLLanguageDriver.class,
          RuntimeSupport.class,
          ProxyFactory.class,
          Slf4jImpl.class,
          Log.class,
          JakartaCommonsLoggingImpl.class,
          Log4j2Impl.class,
          Jdk14LoggingImpl.class,
          StdOutImpl.class,
          NoLoggingImpl.class,
          SqlSessionFactory.class,
          PerpetualCache.class,
          FifoCache.class,
          LruCache.class,
          SoftCache.class,
          WeakCache.class,
          //TODO 增加了MybatisSqlSessionFactoryBean.class
          SqlSessionFactoryBean.class, MybatisSqlSessionFactoryBean.class,
          ArrayList.class,
          HashMap.class,
          TreeSet.class,
          HashSet.class
      ).forEach(x -> hints.reflection().registerType(x, MemberCategory.values()));
      Stream.of(
          "org/apache/ibatis/builder/xml/*.dtd",
          "org/apache/ibatis/builder/xml/*.xsd"
      ).forEach(hints.resources()::registerPattern);

      hints.serialization().registerType(SerializedLambda.class);
      hints.serialization().registerType(SFunction.class);
      hints.serialization().registerType(java.lang.invoke.SerializedLambda.class);
      hints.reflection().registerType(SFunction.class);
      hints.reflection().registerType(SerializedLambda.class);
      hints.reflection().registerType(java.lang.invoke.SerializedLambda.class);

      hints.proxies().registerJdkProxy(StatementHandler.class);
      hints.proxies().registerJdkProxy(Executor.class);
      hints.proxies().registerJdkProxy(ResultSetHandler.class);
      hints.proxies().registerJdkProxy(ParameterHandler.class);

//        hints.reflection().registerType(MybatisPlusInterceptor.class);
      hints.reflection().registerType(AbstractWrapper.class,MemberCategory.values());
      hints.reflection().registerType(LambdaQueryWrapper.class,MemberCategory.values());
      hints.reflection().registerType(LambdaUpdateWrapper.class,MemberCategory.values());
      hints.reflection().registerType(UpdateWrapper.class,MemberCategory.values());
      hints.reflection().registerType(QueryWrapper.class,MemberCategory.values());

      hints.reflection().registerType(BoundSql.class,MemberCategory.DECLARED_FIELDS);
      hints.reflection().registerType(RoutingStatementHandler.class,MemberCategory.DECLARED_FIELDS);
      hints.reflection().registerType(BaseStatementHandler.class,MemberCategory.DECLARED_FIELDS);
      hints.reflection().registerType(MybatisParameterHandler.class,MemberCategory.DECLARED_FIELDS);


      hints.reflection().registerType(IEnum.class,MemberCategory.INVOKE_PUBLIC_METHODS);
      // register typeHandler
      hints.reflection().registerType(CompositeEnumTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
      hints.reflection().registerType(FastjsonTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
      hints.reflection().registerType(GsonTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
      hints.reflection().registerType(JacksonTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
      hints.reflection().registerType(MybatisEnumTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
    }
  }

  static class MyBatisBeanFactoryInitializationAotProcessor
      implements BeanFactoryInitializationAotProcessor, BeanRegistrationExcludeFilter {

    private final Set<Class<?>> excludeClasses = new HashSet<>();

    MyBatisBeanFactoryInitializationAotProcessor() {
      excludeClasses.add(MapperScannerConfigurer.class);
    }

    @Override public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) {
      return excludeClasses.contains(registeredBean.getBeanClass());
    }

    @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);
              hints.resources()
                  .registerPattern(mapperInterfaceType.getName().replace('.', '/').concat(".xml"));
              registerMapperRelationships(mapperInterfaceType, hints);
            }
          }
        }
      };
    }

    private void registerMapperRelationships(Class<?> mapperInterfaceType, RuntimeHints hints) {
      Method[] methods = ReflectionUtils.getAllDeclaredMethods(mapperInterfaceType);
      for (Method method : methods) {
        if (method.getDeclaringClass() != Object.class) {
          ReflectionUtils.makeAccessible(method);
          registerSqlProviderTypes(method, hints, SelectProvider.class, SelectProvider::value, SelectProvider::type);
          registerSqlProviderTypes(method, hints, InsertProvider.class, InsertProvider::value, InsertProvider::type);
          registerSqlProviderTypes(method, hints, UpdateProvider.class, UpdateProvider::value, UpdateProvider::type);
          registerSqlProviderTypes(method, hints, DeleteProvider.class, DeleteProvider::value, DeleteProvider::type);
          Class<?> returnType = MyBatisMapperTypeUtils.resolveReturnClass(mapperInterfaceType, method);
          registerReflectionTypeIfNecessary(returnType, hints);
          MyBatisMapperTypeUtils.resolveParameterClasses(mapperInterfaceType, method)
              .forEach(x -> registerReflectionTypeIfNecessary(x, hints));
        }
      }
    }

    @SafeVarargs
    private <T extends Annotation> void registerSqlProviderTypes(
        Method method, RuntimeHints hints, Class<T> annotationType, Function<T, Class<?>>... providerTypeResolvers) {
      for (T annotation : method.getAnnotationsByType(annotationType)) {
        for (Function<T, Class<?>> providerTypeResolver : providerTypeResolvers) {
          registerReflectionTypeIfNecessary(providerTypeResolver.apply(annotation), hints);
        }
      }
    }

    private void registerReflectionTypeIfNecessary(Class<?> type, RuntimeHints hints) {
      if (!type.isPrimitive() && !type.getName().startsWith("java")) {
        hints.reflection().registerType(type, MemberCategory.values());
      }
    }

  }

  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<Class<?>> 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 parameterizedType = (ParameterizedType) src;
        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;
    }

  }

  static class MyBatisMapperFactoryBeanPostProcessor implements MergedBeanDefinitionPostProcessor, BeanFactoryAware {

    private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(
        MyBatisMapperFactoryBeanPostProcessor.class);

    private static final String MAPPER_FACTORY_BEAN = "org.mybatis.spring.mapper.MapperFactoryBean";

    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;
      }
    }

  }
}

package com.example.demo.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author lyk
 * @since 2024-03-30
 */
@Configuration
//TODO 如果使用这个方式的话,记得一定要指定sqlSessionTemplateRef或sqlSessionFactoryRef https://github.com/mybatis/spring-boot-starter/wiki/Quick-Start-for-building-native-image#how-to-use-mapperscan
@MapperScan(basePackages = "com.example.demo.mapper", sqlSessionTemplateRef = "sqlSessionTemplate")
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor();
        //plusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        //TODO 3.5.3.1 以下插件多的话,可能会报异常,这个在最新版本修改.  https://github.com/baomidou/mybatis-plus/issues/5532
//        plusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
//        plusInterceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
//        plusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return plusInterceptor;
    }

}

demo地址如下

https://gitee.com/lgbisha/springboot3-mybatis-plus/tree/master
在使用Spring Boot 3.2.4版本集成Batis 2.1.3时,出现"Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"错误的原因是没有正确配置MyBatis的相关属性。 解决这个问题的方法是在Spring Boot的配置文件中添加正确的MyBatis配置。具体步骤如下: 1. 确保已经在pom.xml文件中添加了MyBatisMyBatis-Spring的依赖。 2. 在Spring Boot的配置文件(通常是application.properties或application.yml)中添加以下配置: - 如果使用sqlSessionFactory: ```properties mybatis.config-location=classpath:mybatis-config.xml mybatis.mapper-locations=classpath:mapper/*.xml ``` - 如果使用sqlSessionTemplate: ```properties mybatis.mapper-locations=classpath:mapper/*.xml ``` 这些配置指定了MyBatis的配置文件位置和Mapper文件的位置。 3. 创建一个MyBatis的配置文件(mybatis-config.xml),并在其中配置sqlSessionFactory或sqlSessionTemplate。示例配置如下: - 使用sqlSessionFactory: ```xml <?xml version="1.0" encoding="UTF-8"?> <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <!-- 配置实体类别名 --> <package name="com.example.entity"/> </typeAliases> <mappers> <!-- 配置Mapper接口 --> <mapper resource="mapper/ExampleMapper.xml"/> </mappers> </configuration> ``` - 使用sqlSessionTemplate: ```xml <?xml version="1.0" encoding="UTF-8"?> <configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <!-- 配置实体类别名 --> <package name="com.example.entity"/> </typeAliases> </configuration> ``` 这些配置文件中的内容根据你的具体项目需求进行修改。 4. 确保Mapper接口和对应的Mapper XML文件已经正确创建,并且位于指定的位置。 完成以上步骤后,重新运行项目,应该就能够解决"Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"错误。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lgbisha

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值