mybatis设置setMapperLocations之后出现BindingException: Invalid bound statement (not found)

笔者在开发ssm项目(非springboot)时,笔者使用的是java api方式设置mybatis,没有使用xml,SqlSessionFactory的Bean也是通过java注解方式注册的,也没有使用xml进行配置,如下:

	@Bean
    public SqlSessionFactory sqlSessionFactory(@Autowired DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
        sqlSessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath" + ":/mapping/*Mapper.xml"));
        sqlSessionFactoryBean.setTypeAliasesPackage("org.j2ee.server.pojo");
        return sqlSessionFactoryBean.getObject();
    }

有一个Mapper定义了如下方法:

	@Insert ("insert into Book(title, author, stock_number, publisher, stock_date, price) values(#{title}, " + "#{author}, #{stockNumber}, #{publisher}, #{stockDate, jdbcType=DATE}, #{price})")
    @Options (useGeneratedKeys = true, keyProperty = "bookId")
    public long insert(Book book);

    public Book find(@Param ("title") String title);

相应的,BookMapper.xml如下:

<mapper namespace="org.j2ee.server.dao.BookMapper">
    <select id="find" resultType="Book">
        select *
        from Book
        where title like #{title} limit 1
    </select>
</mapper>

但是在运行时出现了Binding Exception,提示find方法没有进行绑定。
读者可以首先从常见的pojo的全限定名是否设置了type alias、xml的namespace是否正确等错误开始排查。

笔者从以下几个地方开始排查:

一、mapper.xml是否被加载了

笔者是使用java api形式进行设置的,xml配置方式网上已经有很多,不再介绍。在debug模式启动时, 可以设置断点,并对以下表达式进行评估:

new PathMatchingResourcePatternResolver().getResources("classpath" + ":/mapping/*Mapper.xml")

返回值是一个Resource[]数组,笔者发现数组长度为0,说明并没有检测到mapper.xml文件。同时发现路径是target/classes/xxx,也就是classpath是target/classes。去target/classes下发现没有mapper.xml,说明build时没有复制过来,需要在pom.xml的build标签下配置:

		<resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>

build时才会将xml文件复制到target里。
但是如此配置完之后,返回的Resource数组是没错了,可以解析到mapper.xml,却还是报同样的错误。

二、sqlSessionFactory里是否有你配置的mapperLocations

阅读以下前请先区分sqlSessionFactory与SqlSessionFactoryBean之间的关系
随便建个controller,写一个成员,将sqlSessionFactory注入进去,并在test方法处建断点,并在debug模式运行:
另外类型写DefaultSqlSessionFactory,而不是注册Bean时的SqlSessionFactoryBean,这里还有点意思,可以自己试一下,这篇博文(点击)有解析

@Controller
public class TestController {
    @Resource
    DefaultSqlSessionFactory sqlSessionFactoryBean;
    @Resource
    private BookMapper bookMapper;

    @GetMapping ("/test")
    @ResponseBody
    public String test(Model model) {
        return bookMapper.find("呐喊").toString(); // 此处设断点
    }
}

停在断点处时,查看this.sqlSessionFactory.configuration.mappedStatements:
在这里插入图片描述
发现只有两个成员,而且都是insert,而find方法完全没有,说明确实find方法没有被绑定。
之后笔者想到可能是sqlSessionFactoryBean.setMapperLocations方法有问题,
在这里插入图片描述
很普通的setter,看不出什么,设个断点,然后在sqlSessionFactoryBean.getObject()看看,毕竟最后用的bean是sqlSessionFactory而不是sqlSessionFactoryBean:
在这里插入图片描述
进afterPropertiesSet看看:
在这里插入图片描述
几个断言没什么意思,进buildSqlSessionFactory()看看:
在这里插入图片描述
找到上图中的设置sqlSessionFactory的mapperLocations的代码,设置断点。
运行程序,发现断点却先停在了上面这张图的地方!!
调用堆栈:
在这里插入图片描述

也就是说getObject()方法早于setMapperLocations调用,导致sqlSessionFactoryBean的mapperLocations成员没有赋给sqlSessionFactory。最终sqlSessionFactory的mapperLocations是null!
知道了问题所在之后,把mybatis的bean注册sqlSessionFactory,改为注册sqlSessionFactoryBean,由spring来调用getObject()来获取sqlSessionFactory:(注意对比第一个代码块)

	@Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(@Autowired DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
        sqlSessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath" + ":/mapping/*Mapper.xml"));
        sqlSessionFactoryBean.setTypeAliasesPackage("org.j2ee.server.pojo");
        return sqlSessionFactoryBean;
    }

然后就可以成功解决问题了。然而笔者还是不知道为什么getObject()会先于setMapperLocations调用,有知道的朋友欢迎讨论,谢谢!

更新
还是这个项目遇到的问题,第一个代码片段,注释处语句不生效。

	@Bean
    public SqlSessionFactory sqlSessionFactory(@Autowired DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 下面这句不生效
        sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true); 
        sqlSessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath" + ":/mapping/*Mapper.xml"));
        sqlSessionFactoryBean.setTypeAliasesPackage("org.j2ee.server.pojo");
        return sqlSessionFactoryBean.getObject();
    }

断点调试前文所述的defaultSqlSessionFactory会发现,其属性mapUnderScoreToCamelCase是false,导致mybatis收集select的结果封装成pojo时失败,返回null。也是同样的错误,由于提供给IOC容器的bean是sqlSessionFactoryBean而非sqlSessionFactory,所以驼峰映射应该这样配置:

		org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        sqlSessionFactoryBean.setConfiguration(configuration);
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值