问题描述

我们使用mybatismybatisplus作为持久框架的时候,通过dao层接口调用xml配置好的sql时,常常会遇到org.apache.ibatis.binding.BindingException Invalid bound statement问题

异常简单来说:就是无效sql绑定,即通过dao层接口的方法名称没有找到对应sql语句

百度查询文章发现大家对该问题解决总结都非常片面,不够全面。
所以分享自己对该问题解决思路。

项目环境

spring boot + mybatis-plus


解决方案

1、检测mapperlocations配置是否正确

mapperlocations配置项是用来告诉Mapper对应的XML文件位置
如果该文件位置配置错误,那么其他内容配置再怎么正确,依然会报错
这也是该异常最重要又最容易忽略一个原因。

另外,由于Mapper对应的XML文件属于静态文件资源,所以一定要存放resoureces目录

这里说下我项目中遇到的问题:
已经在配置文件添加如下配置指定Mapper所对应的XML文件的位置

mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

并且也检查配置路径正确的,没有问题。
但在执行dao自定义方法时,还是一直出现异常

原因:
官网查看mybatisplus配置文档后,终于发现mybatis-plus框架下,需要通过mybatis-plus.mapper-locations来指定XML文件的位置
在这里插入图片描述
修改为如下配置后,问题修复

#默认classpath*:/mapper/**/*.xml,推荐明确配置,出现问题更好排查
mybatis-plus.mapper-locations=classpath*:/mapper/*.xml

小结⚠️:
注意在mybatis框架和mybatis-plus框架下配置项的区别

maven依赖

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

对应的xml路径的配置属性mybatis.mapper-locations

mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

maven依赖

<dependency>
       <groupId>com.baomidou</groupId>
       <artifactId>mybatis-plus-boot-starter</artifactId>
       <version>3.5.2</version>
</dependency>

对应的xml路径的配置属性mybatis-plus.mapper-locations

mybatis-plus.mapper-locations=classpath:mybatis/mapper/*.xml

注意⚠️:
很多人可能会奇怪自己项目中明明没有明确配置mapper-locations的属性,但是执行mapper中自定义方法确并没有报错。这是由于无论是mybatis框架还是mybatis-plus框架下的start自动配置包都对mapper-locations属性默认的路径配置classpath*:/mapper/**/*.xml,这也符合spring boot中约定大于配置的规范

大家在使用IDEA开发中,也可以注意配置项下面的黄线,会提示不能解析对应的配置项。

一般引入了框架对应的start依赖后,配置框架相关的配置项是不会出现黄线下划线提醒的。只有用户定义的一些配置项,会出现黄线下划线提醒。
在这里插入图片描述
所以,框架的配置项如果出现黄线下划线提示,大家一定要高度重视

2、检测是否主动配置过SqlSessionFactory对象

很多时候们在项目中配置多数据源的时候,一般都会在配置类中通过@Bean创建SqlSessionFactory 对象并注入到Spring容器中
很多朋友由于写法规范,往往就会让mybatis或mybait-plus中通过配置项配置的属性无效化,例如:

@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
    MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
    sqlSessionFactory.setDataSource(dataSource());
    return sqlSessionFactory.getObject();
}

这里推荐参考MybatisPlusAutoConfiguration类中写法声明SqlSessionFactory,防止配置属性丢失
MybatisPlusAutoConfiguration中声明SqlSessionFactory的实现

    @Bean
    @ConditionalOnMissingBean  //spring器中存在SqlSessionFactory的bean对象才会运行方法
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        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()));
        }

        this.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);
        }

        //是否配置了MapperLocations路径
        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        Objects.requireNonNull(factory);
        this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory);
        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
        if (!ObjectUtils.isEmpty(this.languageDrivers)) {
            factory.setScriptingLanguageDrivers(this.languageDrivers);
        }

        Optional var10000 = Optional.ofNullable(defaultLanguageDriver);
        Objects.requireNonNull(factory);
        var10000.ifPresent(factory::setDefaultScriptingLanguageDriver);
        this.applySqlSessionFactoryBeanCustomizers(factory);
        GlobalConfig globalConfig = this.properties.getGlobalConfig();
        Objects.requireNonNull(globalConfig);
        this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
        this.getBeansThen(IKeyGenerator.class, (i) -> {
            globalConfig.getDbConfig().setKeyGenerators(i);
        });
        Objects.requireNonNull(globalConfig);
        this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
        Objects.requireNonNull(globalConfig);
        this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
        factory.setGlobalConfig(globalConfig);
        return factory.getObject();
    }

如果项目中采用的是mybatis-springbootstarter,而没有使用mybatis-plus-bootstarter,则需要参考MybatisAutoConfiguration类中方法sqlSessionFactory去配置。

注意:
针对数据源配置的实现,如果不是有非常特殊定制需求,强烈建议直接使用mybatis-plus中的多数据源插件,不要重复造轮子,关键造的轮子质量还乍地。

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>

3、检测dao层接口是否注入spring容器

这部分在之前的文章中已经提到,@MapperScan@Mapper一定要任选一种进行配置,如果采用@MapperScan则一定要正确配置中Mapper接口所在的目录,确保在项目启动时,Mapper接口都注入到Spring容器中

启动类Application:

@SpringBootApplication
@Slf4j
@MapperScan("com.laowan.mybatis_plus.mapper")
public class MybatisPlusApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
        log.info("mybatis_plus_demo 启动成功");
    }
}

@Mapper配置方法

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

两者任意选择一种方式配置即可,如果都不配置,那么在执行dao层方法行数操作时,会出现在spring器中找不到对应的bean异常

@Mapper和@MapperScan都不配置调用mapper方法时出现的异常
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.laowan.mybatis_plus.mapper.UserMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

4、检测xml文件中namespace是否配置正确

针对这部分的检测,强烈推荐大家在idea安装mybatisx插件,借助插件快速检查是否匹配

官方文档https://baomidou.com/pages/ba5b24

安装插件后,在mapper接口和xml文件中就会出现这样的小企鹅。
点击小企鹅就可以在mapper接口和xml文件间相互跳转
如果配置错误,将不能进行跳转或调整到错误的文件中。
在这里插入图片描述
在这里插入图片描述

5、检测dao层接口的方法名和sql的id是否匹配

可以借助mybatisx插件快速检测。同样,如果名称不匹配,不会出现对应的红蓝小企鹅。

Mapper接口方法:
在这里插入图片描述

xml中对于的sql:
在这里插入图片描述

6、检测resultType和resultMap的配置

在这里插入图片描述

补充:
看到网上有一种说明dao接口与xml的文件名不一致也会导致该问题
比如:接口名与接口文件名都是DepartmentDao, 而配置文件名为DeparmentDao.xml,费了很大的劲才看到两者名字一个t字母修改后就一切正常了。验证这种说法是错误的
在这里插入图片描述
虽然是错误的,但是接口名和映射名保持一致确是推荐做法,可以避免一些低级错误。
推荐的做法还是直接通过插件生成实体类和mapper接口方法以及xml文件,避免出现低级错误。

总结

本文主要是针对spring boot + mybatis-plus框架下的常见异常:org.apache.ibatis.binding.BindingException Invalid bound statement的一些解决手段进行了说明

1、在mybatis框架下,主要需要保证2点:

  • Mapper所对应的XML文件的位置要通过mapper-locations属性配置正确
  • Mapper接口需要通过@MapperScan@Mapper注入到Spring容器中

2、注意mybatis框架和mybatis-plus配置项的区别

  • mybatis框架下mapper-locations配置项为:mybatis.mapper-locations
  • mybatis-plus框架下mapper-locations配置项为:mybatis-plus.mapper-locations

3、利用mybatisx插件快速检测命名空间、方法名和sql的id之间的映射关系。
4、手动注入SqlSessionFactory类的bean时,需要注意是否注入了相关的配置。

原文地址:https://blog.csdn.net/w1014074794/article/details/125725011

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_19399.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注