解决 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found) 以及MyBatis批量加载xml映

  错误 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found) 的出现,意味着项目须要xml文件来映射SQL语句,若是只使用接口进行SQL映射,不在本文讨论范围内。html

  个人项目的环境是IDEA下的Maven工程,而IDEA下的MAVEN工程中有一个特色就是,在src/main/java中,只有.java文件默认会被编译,而xml文件不会被编译。(出处)java

  的确这样的作法也比较符合Maven目录框架的初衷:即在src/main/java中只存放.java的源码文件。spring

 

  在使用MyBatis Generator进行逆向生成时是能够指定mapper接口和对应的xml文件的位置的,有的人也喜欢把它们指定在java目录下的同一个包(如:src/main/java/com/abc/mapper)中,而我就喜欢指定在不一样的目录下(接口文件:src/main/java/com/abc/mapper,xml文件:scr/mian/resources/mapper)。咱们分开讨论sql

 

1、Mapper接口文件和对应的XML在同一个目录下数据库

  通常来讲,接口文件和XML文件是同名一一对应的。apache

  这种状况就是要改变IDEA中MAVEN工程默认不编译src/main/java中的xml文件的行为,须要在maven的pom.xml中配置一个节点:api

<project>
        ......
        <build>
            <resources>
        <resource>
            <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
        </build>
</project>

  这样IDEA就会对src/main/java中的xml文件进行编译了。session

  个人工程中进行相应的修改后,的确奏效了。mybatis

  可是我项目中MyBatis的核心配置文件中,配置是空的,没有指定任何xml的位置:app

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

</configuration>

  而后发现,在Spring容器中建立SqlSessionFactory的Bean时(在applicationContext-dao.xml中),就已经指定了Mapper接口的位置:

<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据库链接池 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 加载mybatis的全局核心配置文件 -->
        <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.taotao.mapper" />
    </bean>

  在上面的配置中,若是我去掉第二个bean,应用就没法正常启动。报错的内容大概是这样:由于那些Mapper接口没有被注册为Bean,因此在Service层中的相关类中的属性的自动注入无法完成,由于找不到那些Mapper的bean。事实上,在Spring的配置文件中使用这个MapperScannerConfigurer类我也是第一次碰到,查了一下,这个类就是进行Bean的定义工做——“从接口的基础包开始递归搜索,并将它们注册为MapperFactoryBean。 请注意,仅注册具备至少一种方法的接口; 具体类将被忽略”。

  而我本身习惯的作法是,在applicationContext-dao.xml中进行以下声明,把MyBatis的相关映射接口注册为Bean:

<!--将指定的包中全部接口看成mapper来配置,以后能够自动引入mapper类-->
  <mybatis:scan base-package="com.biguo.mapper"/>

  因此须要确保把Mapper接口注册为Bean,不论用哪一种方式,都是能够的,而同目录下的xml文件是被自动加载。

  第一种状况的解决方案就是这样。

 

2、Mapper接口文件和对应的XML在不一样的目录下

  以下图,我习惯的作法是在进行MyBatis逆向生成时,把Mapper接口放在src/main/java下,把对应的XML文件放到src/main/resources下,分开存放。

 

   mapper接口是必定要注册为Bean的,上面提到的两种方式均可以。

  而后就是,告诉MyBatis去哪里找到哪些映射SQL语句的XML文件,最最经常使用的就是在MyBatis 的核心配置文件中使用<mappers>标签进行指定。官方文档也说得很清楚:MyBatsi官方文档 。

  官方文档里写了有四种方式来指定xml映射文件的位置:类路径相对资源引用(resource),彻底限定的url引用(包括file:/// URL),类名或包名,下面分别举个例子:

<mappers>
    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>

    <mapper url="file:///var/mappers/AuthorMapper.xml"/>

    <mapper class="org.mybatis.builder.AuthorMapper"/>

    <package name="org.mybatis.builder"/>

</mappers>

  然而,根据这位朋友(Hern)的文章(参考出处),这四种指定方式有必定的限制:

一、当采用class、package方式时,映射文件(Mapper.xml)和接口必须命名相同,而且放在与接口同一目录下。(尽可能不要采用这种方法)

二、当采用class方式时,没有SQL映射文件,全部的SQL都是利用注解写在接口上,这样就能够避免注意1的事情发生,提升维护性,不是很重要的SQL语句能够采用注解的方式,这样能够提升开发速度,重要和复杂的接口、SQL建议仍是采用SQL映射文件的方式。(尽可能采用这种方法)

  我什么会搜到这位朋友的文章呢?由于我本身有个问题一直没解决。

  之前通常都是用resource指定xml文件的位置,不论有多少个xml文件,都在<mappers>里一个个写出来,由于我找不到能够批量指定的方法(通配符什么的试了,没成功)(注意别忘了如今的背景是mapper接口和xml映射文件在不一样的目录下)。

  Hern的文章提醒了我,能够指定一个Package来批量加载XML映射文件,可是它们必须和mapper接口在同一目录下。若是是这样,那咱们又回到了上第一种状况——必须解决掉IDEA下Maven工程默认不会对src/main/java的非.java文件的行为。这样作好像也不错,也就是能够不须要批量指定,只要配置了Maven节点,mapper接口同目录下的xml映射文件就能自动加载了。

  因此如今的问题就变成了:必须在XML文件与mapper接口在不一样目录下的状况下找到批量加载XML映射文件的方法。

  还真的找到了,StackOverflow(How to load mapper xml files with wildcard?)给了启发。

  方法就是在applicationContext-dao.xml注册SqlSessionFactory的bean时,设置一个"mapperLocation"属性(property),来告诉MyBatis那些XML映射文件的位置(可使用通配符),以下:

<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据库链接池 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 加载mybatis的全局配置文件 -->
        <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
<!--批量指定mapper的xml文件的方法,此时xml无需与接口在同一个文件夹下--> <property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"/> </bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.taotao.mapper" /> </bean>

  也就是说,在同一个地方,建立了SqlSessionFactory的Bean,批量加载了XML映射文件,建立了全部mapper接口的Bean。这样作,个人MyBatis核心配置文件就仍然能够保持0配置。

   而后我也查看了SqlSessionFactoryBean的官方文档,这个类中的确有这么一个方法:

void setMapperLocations(Resource[] mapperLocations)

 Set locations of MyBatis mapper files that are going to be merged into the SqlSessionFactory configuration at runtime.This is an alternative to specifying "<sqlmapper>" entries in an MyBatis config file. This property being based on Spring's resource abstraction also allows for specifying resource patterns here: e.g. "classpath*:sqlmap/*-mapper.xml".

设置将在运行时合并到SqlSessionFactory配置中的MyBatis映射器文件的位置。 这是在MyBatis配置文件中指定“<sqlmapper>”条目的替代方法。 此属性基于Spring的资源抽象,也容许在此指定资源模式,例如: "classpath*:sqlmap/*-mapper.xml"

   还能再说什么呢,Spring牛逼就完事了!!

  这个应该是最佳解决方案了,ok,任务完成~

相关文章
相关标签/搜索