[超级前台][问题][解决] [superdesk] tk mybatis Mapped Statements collection already contains value selectByPrimaryKey 动态sql

2019-10-30 21:50:17.342 ERROR 18364 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cn.superdesk.libs.springboot.starter.mybatis.SuperdeskMybatisConfiguration': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Mapped Statements collection already contains value for cn.superdesk.app.core.dao.mapper.AppUserFloorEntityMapper.selectByPrimaryKey
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1699)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:573)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:333)
    at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:137)
    at cn.superdesk.app.transfer.canal.instance.supercloud.ApplicationStarter.main(ApplicationStarter.java:48)
Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for cn.superdesk.app.core.dao.mapper.AppUserFloorEntityMapper.selectByPrimaryKey
    at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:872)
    at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:844)
    at org.apache.ibatis.session.Configuration.addMappedStatement(Configuration.java:668)
    at cn.superdesk.libs.mybatis.crud.builder.GetByPrimaryKeyBuilder.build(GetByPrimaryKeyBuilder.java:53)
    at cn.superdesk.libs.mybatis.crud.GeneralSqlGenerator.generate(GeneralSqlGenerator.java:45)
    at cn.superdesk.libs.mybatis.spring.SuperdeskMybatisRegistry.register(SuperdeskMybatisRegistry.java:25)
    at cn.superdesk.libs.springboot.starter.mybatis.SuperdeskMybatisConfiguration.afterPropertiesSet(SuperdeskMybatisConfiguration.java:42)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1758)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1695)
    ... 15 common frames omitted

先去看几个文章

springboot2.0.6(附) 解析META-INF/spring.factories通过系统加载类获取对应的 class 的全限定名称 - 小亮89的个人空间 - OSCHINA 解析META-INF/spring.factories通过系统加载类获取对应的 class 的全限定名称 - 小亮89的个人空间 - OSCHINA")

Spring Boot的扩展机制之Spring Factories - 程序员伊成 - CSDN博客

springboot2.0自动注入文件spring.factories如何加载详解 - 简书

关键代码

superdesk-libs-springboot-starter

/mnt1t/JAVA_HOME/superdesk-libs/superdesk-libs-springboot-starter/src/main/resources/META-INF/spring.factories

/mnt1t/JAVA_HOME/superdesk-libs/superdesk-libs-springboot-starter/src/main/resources/META-INF/spring.provides

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.superdesk.libs.springboot.starter.mybatis.SuperdeskMybatisConfiguration,\
cn.superdesk.libs.springboot.starter.cache.DelegateCacheConfiguration
package cn.superdesk.libs.springboot.starter.mybatis;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternUtils;

import cn.superdesk.libs.mybatis.MybatisConfigs;
import cn.superdesk.libs.mybatis.datasource.MutiRouteDataSource;
import cn.superdesk.libs.mybatis.parser.MybatisMapperParser;
import cn.superdesk.libs.mybatis.spring.SuperdeskMybatisRegistry;

@Configuration
@EnableConfigurationProperties(MybatisPluginProperties.class)
@ConditionalOnClass(MutiRouteDataSource.class)
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class SuperdeskMybatisConfiguration implements InitializingBean {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    
    @Autowired
    private MybatisPluginProperties properties;

    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Override
    public void afterPropertiesSet() throws Exception {
        Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader()).getResources(mapperLocations);
        String group = "default";
        MybatisMapperParser.addMapperLocations(group,resources);
        MybatisConfigs.addProperties(group, properties.getProperties());
        SuperdeskMybatisRegistry.register(group,sqlSessionFactory.getConfiguration());
    }

}
package cn.superdesk.libs.springboot.starter.mybatis;

import java.util.Properties;

import org.springframework.boot.context.properties.ConfigurationProperties;

import cn.superdesk.libs.mybatis.MybatisConfigs;

/**
 * @author superdesk
 * @description
 * @date 2016年12月31日
 */
@ConfigurationProperties(prefix = "superdesk.mybatis")
public class MybatisPluginProperties {

    private Properties properties = new Properties();

    public void setCacheEnabled(boolean cacheEnabled) {
        properties.setProperty(MybatisConfigs.CACHE_ENABLED, String.valueOf(cacheEnabled));
    }

    public void setRwRouteEnabled(boolean rwRouteEnabled) {
        properties.setProperty(MybatisConfigs.RW_ROUTE_ENABLED, String.valueOf(rwRouteEnabled));
    }

    public void setPaginationEnabled(boolean paginationEnabled) {
        properties.setProperty(MybatisConfigs.PAGINATION_ENABLED, String.valueOf(paginationEnabled));
    }

    public void setDbType(String dbType) {
        properties.setProperty(MybatisConfigs.DB_TYPE, dbType);
    }

    public void setCrudDriver(String crudDriver) {
        properties.setProperty(MybatisConfigs.CRUD_DRIVER, crudDriver);
    }

    public void setNullValueCache(boolean nullValueCache) {
        properties.setProperty(MybatisConfigs.CACHE_NULL_VALUE, String.valueOf(nullValueCache));
    }

    public void setCacheExpireSeconds(long cacheExpireSeconds) {
        properties.setProperty(MybatisConfigs.CACHE_EXPIRE_SECONDS, String.valueOf(cacheExpireSeconds));
    }

    public void setDynamicExpire(boolean dynamicExpire) {
        properties.setProperty(MybatisConfigs.CACHE_DYNAMIC_EXPIRE, String.valueOf(dynamicExpire));
    }

    public void setInterceptorHandlerClass(String interceptorHandlerClass) {
        properties.setProperty(MybatisConfigs.INTERCEPTOR_HANDLERCLASS, interceptorHandlerClass);
    }

    public Properties getProperties() {
        return properties;
    }

}

看到这, 关键点在这, 得加入这个

# mybatis
superdesk.mybatis.crudDriver=mapper3
#superdesk.mybatis.dbType=Mysql
superdesk.mybatis.cacheEnabled=false
superdesk.mybatis.dynamicExpire=false
superdesk.mybatis.cacheExpireSeconds=3600
#superdesk.mybatis.rwRouteEnabled=false
superdesk.mybatis.paginationEnabled=true

问题来了

其实 这个mybatis mapper xml 很有可能加载了两次

第一次 加载

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.1</version>
        </dependency>

        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>1.2.4</version>
        </dependency>

第二次加载

类 spi Service Provider Interface

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.superdesk.libs.springboot.starter.mybatis.SuperdeskMybatisConfiguration,\
cn.superdesk.libs.springboot.starter.cache.DelegateCacheConfiguration

->

SuperdeskMybatisConfiguration

@Configuration
@EnableConfigurationProperties(MybatisPluginProperties.class)
@ConditionalOnClass(MutiRouteDataSource.class)
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class SuperdeskMybatisConfiguration implements InitializingBean {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    
    @Autowired
    private MybatisPluginProperties properties;

    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Override
    public void afterPropertiesSet() throws Exception {
        Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader()).getResources(mapperLocations);
        String group = "default";
        MybatisMapperParser.addMapperLocations(group,resources);
        MybatisConfigs.addProperties(group, properties.getProperties());
        SuperdeskMybatisRegistry.register(group,sqlSessionFactory.getConfiguration());
    }

}

作用地方

@EnableConfigurationProperties(MybatisPluginProperties.class)
@ConditionalOnClass(MutiRouteDataSource.class)
@AutoConfigureAfter(MybatisAutoConfiguration.class)
@Override
    public void afterPropertiesSet() throws Exception {
        Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader()).getResources(mapperLocations);
        String group = "default";
        // MARK 001 加到一个新的 group 上 再解决转换
        MybatisMapperParser.addMapperLocations(group,resources);
        MybatisConfigs.addProperties(group, properties.getProperties());
        // MARK 002 再注册了一次
        SuperdeskMybatisRegistry.register(group,sqlSessionFactory.getConfiguration());
    }
public class SuperdeskMybatisRegistry {

    public static void register(String group, Configuration configuration) throws Exception {

        if ("default".equals(MybatisConfigs.getCrudDriver(group))) {

            new GeneralSqlGenerator(group, configuration).generate();

        } else if ("mapper3".equals(MybatisConfigs.getCrudDriver(group))) {

            Class<?> helperClazz = Class.forName("tk.mybatis.mapper.mapperhelper.MapperHelper");
            Object   helper      = helperClazz.newInstance();
            Class<?> configClazz = Class.forName("tk.mybatis.mapper.entity.Config");
            Object   config      = configClazz.newInstance();
            Method   method      = configClazz.getDeclaredMethod("setNotEmpty", boolean.class);
            method.invoke(config, false);
            method = helperClazz.getDeclaredMethod("setConfig", configClazz);
            method.invoke(helper, config);

            method = helperClazz.getDeclaredMethod("registerMapper", Class.class);
            List<EntityInfo> entityInfos = MybatisMapperParser.getEntityInfos(group);
            for (EntityInfo entityInfo : entityInfos) {
                method.invoke(helper, entityInfo.getMapperClass());
            }

            method = helperClazz.getDeclaredMethod("processConfiguration", Configuration.class);
            method.invoke(helper, configuration);
        } else {

            new GeneralSqlGenerator(group, configuration).generate();

        }

        //注册拦截器
        String[] hanlderNames = MybatisConfigs.getHanlderNames(group);

        if (hanlderNames.length > 0) {
            SuperdeskMybatisInterceptor interceptor = new SuperdeskMybatisInterceptor(group, hanlderNames);
            configuration.addInterceptor(interceptor);
            interceptor.afterRegister();
        }

    }
}

关键是这个mapper3 不走 default 不走 else , 如果没配置就会走 else 去生成, 就会报错了

/**
 * @author superdesk
 * @description
 * @date 2016年2月2日
 */
public class GeneralSqlGenerator {

    private static final Logger                  log           = LoggerFactory.getLogger(GeneralSqlGenerator.class);
    public static        DefaultCrudMethodDefine methodDefines = new DefaultCrudMethodDefine();

    private LanguageDriver languageDriver;
    private Configuration  configuration;
    private String         group;

    public GeneralSqlGenerator(String group, Configuration configuration) {
        this.group = group;
        this.configuration = configuration;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
    }

    public void generate() {
        if (languageDriver == null) languageDriver = configuration.getDefaultScriptingLanguageInstance();
        List<EntityInfo> entityInfos = MybatisMapperParser.getEntityInfos(group);
        for (EntityInfo entity : entityInfos) {
            //TODO 排除生成
            GetByPrimaryKeyBuilder.build(configuration, languageDriver, entity);
            InsertBuilder.build(configuration, languageDriver, entity);
            UpdateBuilder.build(configuration, languageDriver, entity);
            DeleteBuilder.build(configuration, languageDriver, entity);
            BatchInsertBuilder.build(configuration, languageDriver, entity);
            SelectAllBuilder.build(configuration, languageDriver, entity);
            log.info(" >> generate autoCrud for:[{}] finish", entity.getEntityClass().getName());
        }
    }
}
GetByPrimaryKeyBuilder.build(configuration, languageDriver, entity);

这个再生成动态 sql 的就会报错了

请先后发表评论
  • 最新评论
  • 总共0条评论