一、错误

主要错误:Caused by: org.apache.ibatis.type.TypeException: The alias ‘PoolInfo’ is already mapped to the value ‘com.XXX.PoolInfo’.
当然还报了一些相关错误。
在这里插入图片描述

二、背景

代码中需要vo类封装内容,于是在com.XXX.vo包下建立了一个PoolInfo.java类,运行后发现项目报错。报错日志显示之前已经在别的包中创建了一个名为PoolInfo.java的类。也就是我在不同的包中创建相同的类名会报错,java编译使用的是全限定类名,所以编译能通过。

三、分析

根据报错日志可以看出出错的地方是和mybatis相关的,然后查看数据源的配置类果然发现了问题,关键代码如下

// CoreDataSourceConfig.java
@Bean(name = "coreSqlSessionFactory")
@Primary
public SqlSessionFactory coreSqlSessionFactory(@Qualifier("coreDataSource") DataSource coreDataSource) throws Exception {
    final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
    sessionFactory.setTypeAliasesPackage("com.XXX");
    sessionFactory.setVfs(SpringBootVFS.class);
    sessionFactory.setDataSource(coreDataSource);
    sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(CoreDataSourceConfig.MAPPER_LOCATION));
    return sessionFactory.getObject();
}

代码中第六行sessionFactory.setTypeAliasesPackage(“com.XXX”);看源码的注释这个字段的作用是“Packages to search for type aliases.”会根据配置的路径用于搜索类型别名的包。再看源码,主要代码如下

// org/apache/ibatis/type/TypeAliasRegistry.java 部分代码
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);
      }
    }
  }

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

public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }

从源码中可以看出,它是先根据路径取到所有的类,然后判断是否有@Alias注解,如果有就用注解中的名字,没有就用类名当做别名。
因此是代码中的路径(“com.XXX”)写的太大,包含了整个项目的类并且类中没有@Alias注解进行区分名字,导致运行项目时找到了相同的别名,抛出了异常(第二个代码框中第31行)。

四、解决

有以下三种方法:

  1. 设置类型别名的路径时可以具体到实体类,多个包下的实体类可以同逗号分隔。sessionFactory.setTypeAliasesPackage(“com.aaa.bbb.entity, com.aaa.ccc.entity”);
  2. 在具有相同类名的类上使用@Alias(“XXX”)注解,注解中的字符串设置为不同值。注意:mybatis会把类设置成注解中的别名。
  3. 把相同的类名修改为不同的类名。
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐