MybatisPlus 多数据源配置导致 逻辑删除等全局配置失效,同时在修改过程中发现批量新增找不到xxxMaper.insert方法

先说第一个问题,多数据源 导致 逻辑删除 等全局配置失效。

先看配置, 很常规的 mybatis-plus 的配置

#mybatis-plus 配置信息
mybatis-plus:
  configuration:
    #开启驼峰功能
    map-underscore-to-camel-case: true
    #FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
    auto-mapping-behavior: full
    #配置的缓存的全局开关
    cache-enabled: true
    #配置日志输出
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

  global-config:
    #0:数据库ID自增
    id-type: 1
    # 逻辑删除配置
    db-config:
      # 删除前
      logic-not-delete-value: 1
      # 删除后
      logic-delete-value: 0
      #全局逻辑删除的实体字段名
      logic-delete-field: status

在看自动填充我们自己写的拦截器

@Component
public class DataMetaObjectHandler implements MetaObjectHandler {
    /**
     * 新增字段填充
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        setFieldValByName("creater", SecurityUtils.getCurrentUserId(), metaObject);
        setFieldValByName("createtime", new Date(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        setFieldValByName("modifier", SecurityUtils.getCurrentUserId(), metaObject);
        setFieldValByName("modifiertime", new Date(), metaObject);
    }
}

在看 SqlSessionFactroy的配置

/**
     * 创建sqlSessionFactory
     * @param dataSource 数据源
     * @param mapperXmlPath  mapper扫描地址
     * @return
     * @throws Exception
     */
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, String mapperXmlPath) throws Exception{
        //加载数据源
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperXmlPath));
        //设置全局配置
        return factoryBean.getObject();
    }

一般来讲,发现MybatisPlus 配置后失效某些 功能,大多 都是 因为 SqlSessionFactory 配置有问题,那么我们 配置的 逻辑删除 是在全局配置中。所以找  GlobalConfig 准没错。可以看下 MybatisPlusAutoConfiguration,这个配置类是 我们但数据源下,SpringBoot 整合MybatisPlus 自动帮我们做的一些事情。这是 MybatisPlusAutoConfiguration 怎么生成的 SqlSessionFactory

MybatisPlusAutoConfiguration 源码

  @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        applyConfiguration(factory);
        if (this.properties.getConfigurationProperties() != null) {
            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }
        if (!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }
        if (this.databaseIdProvider != null) {
            factory.setDatabaseIdProvider(this.databaseIdProvider);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (this.properties.getTypeAliasesSuperType() != null) {
            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.typeHandlers)) {
            factory.setTypeHandlers(this.typeHandlers);
        }
        Resource[] mapperLocations = this.properties.resolveMapperLocations();
        if (!ObjectUtils.isEmpty(mapperLocations)) {
            factory.setMapperLocations(mapperLocations);
        }
        // TODO 修改源码支持定义 TransactionFactory
        this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);

        // TODO 对源码做了一定的修改(因为源码适配了老旧的mybatis版本,但我们不需要适配)
        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
        if (!ObjectUtils.isEmpty(this.languageDrivers)) {
            factory.setScriptingLanguageDrivers(this.languageDrivers);
        }
        Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);

        // TODO 自定义枚举包
        if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
            factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
        }
        // TODO 此处必为非 NULL
        GlobalConfig globalConfig = this.properties.getGlobalConfig();
        // TODO 注入填充器
        this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
        // TODO 注入主键生成器
        this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));
        // TODO 注入sql注入器
        this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
        // TODO 注入ID生成器
        this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
        // TODO 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean
        factory.setGlobalConfig(globalConfig);
        return factory.getObject();
    }

发现最后两行 有设置 globalConfig ,所以 我们就需要 手动设置 globalConfig 了。查了相关资料,具体那个忘了,大体意思就是 需要我们手动设置 globalConfig 在  MybatisSqlSessionFactoryBean 中,于是有了 如下的 修改和配置。

创建一个配置类 MybatisPlusConfig

然后里面的 GlobalConfig ,根据配置文件 加载 里面的属性,然后再设置 数据处理器,也就是我们自动填充的 Handler

@Configuration
public class MybatisPlusConfig {

    @Autowired
    private DataMetaObjectHandler dataMetaObjectHandler;

    /**
     * mybatis 分页插件
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    /**
     * @Author 
     * @Description mybatis 全局配置,由于多数据源原因 所以需要 代码配置
     * @Date 2021/10/12 16:02
     * @Param []
     * @return com.baomidou.mybatisplus.core.config.GlobalConfig
     **/
    @Bean(name = "globalConfig")
    @ConfigurationProperties(prefix = "mybatis-plus.global-config")
    public GlobalConfig globalConfig(){
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setMetaObjectHandler(dataMetaObjectHandler);
        return globalConfig;
    }
}

在修改 SqlSessionFactory的配置,我这个是一个 抽象类 用其他类集成使用的,可以少些一点代码

public abstract class AbstractDataSourceConfig {

    @Autowired
    private GlobalConfig globalConfig;

    @Autowired
    private MybatisPlusInterceptor mybatisPlusInterceptor;

    /**
     * 数据源配置
     * @return
     */
    public DataSource dataSource(){
        return DataSourceBuilder.create().build();
    }

    /**
     * 创建sqlSessionFactory
     * @param dataSource    数据源
     * @param mapperXmlPath mapper扫描地址
     * @return
     * @throws Exception
     */
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource, String mapperXmlPath) throws Exception{
        //加载数据源
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);

        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperXmlPath));
        factoryBean.setPlugins(mybatisPlusInterceptor);
        //设置全局配置
        factoryBean.setGlobalConfig(globalConfig);
        return factoryBean.getObject();
    }

    /**
     * 配置事务管理器
     * @param dataSource
     * @return
     */
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 封装的是数据库操作
     * @param sqlSessionFactory
     * @return
     */
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

注意这里:设置了 全局配置的 globalConfig

public SqlSessionFactory sqlSessionFactory(DataSource dataSource, String mapperXmlPath) throws Exception{
        //加载数据源
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);

        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperXmlPath));
        factoryBean.setPlugins(mybatisPlusInterceptor);
        //设置全局配置
        factoryBean.setGlobalConfig(globalConfig);
        return factoryBean.getObject();
    }

然后再 使用的时候发现,逻辑删除 是妥妥的安排明白了

但是新的问题出现,批量新增出错了。。。。

报错:Mapped Statements collection does not contain value for com.dlxx.egs.sys.business.role.mapper.RoleMenuMapper.insert

### Error updating database.  Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.dlxx.egs.sys.business.role.mapper.RoleMenuMapper.insert
### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.dlxx.egs.sys.business.role.mapper.RoleMenuMapper.insert
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:199)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:184)
	at com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.lambda$saveBatch$0(ServiceImpl.java:130)
	at com.baomidou.mybatisplus.extension.service.impl.ServiceImpl$$Lambda$1279/1360737045.accept(Unknown Source)
	at com.baomidou.mybatisplus.extension.toolkit.SqlHelper.lambda$executeBatch$0(SqlHelper.java:215)
	at com.baomidou.mybatisplus.extension.toolkit.SqlHelper$$Lambda$1280/2062815923.accept(Unknown Source)
	at com.baomidou.mybatisplus.extension.toolkit.SqlHelper.executeBatch(SqlHelper.java:179)
	... 137 more
Caused by: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.dlxx.egs.sys.business.role.mapper.RoleMenuMapper.insert
	at com.baomidou.mybatisplus.core.MybatisConfiguration$StrictMap.get(MybatisConfiguration.java:420)
	at com.baomidou.mybatisplus.core.MybatisConfiguration.getMappedStatement(MybatisConfiguration.java:305)
	at com.baomidou.mybatisplus.core.MybatisConfiguration.getMappedStatement(MybatisConfiguration.java:298)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:196)
	... 143 more

结合上面 讲的,问题还是出在 MybatisSqlSessionFactoryBean 里面,于是灵光一闪可以看看 globalConfig 源码么,兴许里面有 我想要的答案。

public class GlobalConfig implements Serializable {

    /**
     * 是否开启 LOGO
     */
    private boolean banner = true;
    /**
     * 机器 ID 部分
     *
     * @see #setIdentifierGenerator(IdentifierGenerator)
     * @deprecated 3.3.0
     */
    @Deprecated
    private Long workerId;
    /**
     * 数据标识 ID 部分
     *
     * @see #setIdentifierGenerator(IdentifierGenerator)
     * @deprecated 3.3.0
     */
    @Deprecated
    private Long datacenterId;
    /**
     * 是否初始化 SqlRunner
     */
    private boolean enableSqlRunner = false;
    /**
     * 数据库相关配置
     */
    private DbConfig dbConfig;
    /**
     * SQL注入器
     */
    private ISqlInjector sqlInjector = new DefaultSqlInjector();
    /**
     * Mapper父类
     */
    private Class<?> superMapperClass = Mapper.class;
    /**
     * 仅用于缓存 SqlSessionFactory(外部勿进行set,set了也没用)
     */
    private SqlSessionFactory sqlSessionFactory;
    /**
     * 缓存已注入CRUD的Mapper信息
     */
    private Set<String> mapperRegistryCache = new ConcurrentSkipListSet<>();
    /**
     * 元对象字段填充控制器
     */
    private MetaObjectHandler metaObjectHandler;
    /**
     * 主键生成器
     */
    private IdentifierGenerator identifierGenerator;

    @Data
    public static class DbConfig {
        /**
         * 主键类型
         */
        private IdType idType = IdType.ASSIGN_ID;
        /**
         * 表名前缀
         */
        private String tablePrefix;
        /**
         * schema
         *
         * @since 3.1.1
         */
        private String schema;
        /**
         * db字段 format
         * <li> 例: `%s` </li>
         * <p> 对主键无效 </p>
         *
         * @since 3.1.1
         */
        private String columnFormat;
        /**
         * entity 的字段(property)的 format,
         * 只有在 column as property 这种情况下生效
         * <li> 例: `%s` </li>
         * <p> 对主键无效 </p>
         *
         * @since 3.3.0
         */
        private String propertyFormat;
        /**
         * 表名是否使用驼峰转下划线命名,只对表名生效
         */
        private boolean tableUnderline = true;
        /**
         * 大写命名,对表名和字段名均生效
         */
        private boolean capitalMode = false;
        /**
         * 表主键生成器
         */
        private IKeyGenerator keyGenerator;
        /**
         * 逻辑删除全局属性名
         */
        private String logicDeleteField;
        /**
         * 逻辑删除全局值(默认 1、表示已删除)
         */
        private String logicDeleteValue = "1";
        /**
         * 逻辑未删除全局值(默认 0、表示未删除)
         */
        private String logicNotDeleteValue = "0";
        /**
         * 字段验证策略之 insert
         *
         * @since 3.1.2
         */
        private FieldStrategy insertStrategy = FieldStrategy.NOT_NULL;
        /**
         * 字段验证策略之 update
         *
         * @since 3.1.2
         */
        private FieldStrategy updateStrategy = FieldStrategy.NOT_NULL;
        /**
         * 字段验证策略之 select
         *
         * @since 3.1.2
         */
        private FieldStrategy selectStrategy = FieldStrategy.NOT_NULL;
    }
}

于是乎就想到之前 看 Mybatis-plus 学习视频的时候,记得可以自定义 SQl 注入,于是找到一行代码。

/**
     * SQL注入器
     */
private ISqlInjector sqlInjector = new DefaultSqlInjector();

于是猜想可能原因是 两个数据源 需要两个 GlobalConfig

于是修改代码 加了一行 @Scope("prototype"),改为多例。再次复测问题搞定。

 /**
     * @Author shiJin
     * @Description mybatis 全局配置,由于多数据源原因 所以需要 代码配置
     * @Date 2021/10/12 16:02
     * @Param []
     * @return com.baomidou.mybatisplus.core.config.GlobalConfig
     **/
    @Scope("prototype")
    @Bean(name = "globalConfig")
    @ConfigurationProperties(prefix = "mybatis-plus.global-config")
    public GlobalConfig globalConfig(){
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setMetaObjectHandler(dataMetaObjectHandler);
        return globalConfig;
    }

由于本人 还未细究过 Mybatis-plus 的源码,所以这次 改好,完全处于瞎猫碰到死耗子,以后如果研究到 源码了,在来慢慢 细捋这个 问题。

如果有哪里说的不对的,欢迎指出,相互学习。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值