mybatis升级后导致alias表名设置设备诊断

mybatis从1.3.2 升级 2.0.7到后部分系统解析 alias出错了,具体错误如下:

问题触发位置:

  • TypeAliasRegistry.registerAlias
  • SqlSessionFactoryBean.buildSqlSessionFactory

下面从TypeAliasRegistry.registerAlias 开始逐步分析两个版本差异和抛出问题原因。

跟踪表名设置底层原理

两个版本的 TypeAliasRegistry.registerAlias 逻辑一致。具体代码如下。

# TypeAliasRegistry.java
  public void registerAlias(Class<?> type) {
    String alias = type.getSimpleName();
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias, type);
  }

从代码中可以看出,mybatis别名使用 class 的 simpleName。如果多个包中出现实体名称,那么就会发送如上错误。

但是开发反馈mybatis升级前不存在该问题,需要继续向上追踪代码。下面是根据升级前后两个版本别名设置分析。

分析 SqlSessionFactoryBean.buildSqlSessionFactory 实现机制差异

  1. mybatis-spring 1.3.2 typeAliasesPackage 解析逻辑如下

     # SqlSessionFactoryBean
     protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
     ...
        if (hasLength(this.typeAliasesPackage)) {
           String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
               ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
           for (String packageToScan : typeAliasPackageArray) {
             configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                     typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
             if (LOGGER.isDebugEnabled()) {
               LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
             }
           }
         }
     ...
     }
    
     # TypeAliasRegistry
     public void registerAliases(String packageName, Class<?> superType){
         ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
         resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
         Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
         for(Class<?> type : typeSet){
           // Ignore inner classes and interfaces (including package-info.java)
           // Skip also inner classes. See issue #6
           if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
             registerAlias(type);
           }
         }
       }

    跟踪代码发现 typeAliasesPackage 不支持路径正则表达式;*实际配置 type-aliases-package: cn.pinming.suppervision..entity 无效。***

  2. mybatis-spring 2.0.7 typeAliasesPackage 源码分析

     #SqlSessionFactoryBean
     protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
         if (hasLength(this.typeAliasesPackage)) {
           scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
               .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
               .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
         }
     }
    
     private Set<Class<?>> scanClasses(String packagePatterns, Class<?> assignableType) throws IOException {
         Set<Class<?>> classes = new HashSet<>();
         String[] packagePatternArray = tokenizeToStringArray(packagePatterns,
             ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
         for (String packagePattern : packagePatternArray) {
           Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
               + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
           for (Resource resource : resources) {
             try {
               ClassMetadata classMetadata = METADATA_READER_FACTORY.getMetadataReader(resource).getClassMetadata();
               Class<?> clazz = Resources.classForName(classMetadata.getClassName());
               if (assignableType == null || assignableType.isAssignableFrom(clazz)) {
                 classes.add(clazz);
               }
             } catch (Throwable e) {
               LOGGER.warn(() -> "Cannot load the '" + resource + "'. Cause by " + e.toString());
             }
           }
         }
         return classes;
       }

    该版本 typeAliasesPackage 已经支撑路径正则表达式。如 /a/b//c。原来解析不了的配置现在能解析了。**

问题解决方案

由于旧版本 mybatis 的 typeAliasesPackage配置不支撑正则表达式,原来的配置是无效的,所以没有报错。而新版本支撑正则表达式,能将以前无效的配置解析出来,但存在类名简称存在重复。所以导致出现了上面描述的问题。

方案一、项目中如果存在同名实体,修正同名实体。
方案二、移除typeAliasesPackage。不在mybatis中使用别名。比如输入不存在的报名:cn.pinming.suppervision.aaaaa

mybatis:
  base-packages: cn.pinming.suppervision.**.dao,cn.pinming.suppervision.push.mapping,cn.pinming.suppervision.web.test.sys
  mapper-locations: classpath*:/mapper/**/*.xml
  type-aliases-package: cn.pinming.suppervision.aaaaa

mybatis 升级后 Specified class is an interface 错误

问题原因:新版本 mapper-spring-boot-starter 提供了 mapper-spring-boot-autoconfigure 实现,和自己定义的 有冲突。排除依赖即可。

解决方案

 <dependency>
     <groupId>tk.mybatis</groupId>
     <artifactId>mapper-spring-boot-starter</artifactId>
     <exclusions>
         <exclusion>
             <groupId>tk.mybatis</groupId>
             <artifactId>mapper-spring-boot-autoconfigure</artifactId>
         </exclusion>
     </exclusions>
 </dependency>
  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值