解決 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,任務完成~

相關文章
相關標籤/搜索