关于java:为什么接口和xml映射器文件必须位于同一程序包中并且具有相同的名称?

Why must the interface and xml mapper file be in same package and have the same name?

今天,我正在准备一个使用Spring Boot并使用MyBatis进行Spring-MyBatis旁边的数据访问通信的示例。以下是相关的项目配置(使用Maven):

1
2
3
4
5
6
7
8
9
10
src/main/java
- edu.home.ltmj.controller
  + CategoryController.java
- edu.home.ltmj.dao
  + CategoryDao.java
- edu.home.ltmj.domain
  + Category.java
src/main/resources
- edu.home.ltmj.dao
  + CategoryMapper.xml

文件的相关内容:

CategoryDao.java:

1
2
3
4
5
package edu.home.ltmj.dao;

public interface CategoryDao {
    List<Category> getAllCategories();
}

CategoryMapper.xml:

1
2
3
4
5
6
7
8
9
10
11
<mapper namespace="edu.home.ltmj.dao.CategoryDao">
    <resultMap id="categoryMap"
        type="edu.home.ltmj.domain.Category">
        <id property="id" column="id" />
        <result property="name" column="name" />
    </resultMap>
    <select id="getAllCategories" resultMap="categoryMap">
        SELECT id, nombre
        FROM category
    </select>
</mapper>

然后,我在请求控制器中注入此dao的一个实例(出于测试目的),如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package edu.home.ltmj.controller;

@RestController
public class CategoryController {
    @Autowired
    private CategoryDao dao;

    @RequestMapping(value="/category/all",
        method=RequestMethod.GET,
        produces=MediaType.APPLICATION_JSON_VALUE)
    public List<Categoria> getAllCategories() {
        return dao.getAllCategories();
    }
}

我运行我的项目并使用curl localhost:8080/category/all测试执行情况,然后期望看到JSON格式的结果,但我却得到了以下异常:

1
2
3
4
5
6
7
8
9
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): edu.home.ltmj.dao.CategoryDao.getAllCategories
at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:189)
at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:43)
at org.apache.ibatis.binding.MapperProxy.cachedMapperMethod(MapperProxy.java:58)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:51)
at com.sun.proxy.$Proxy45.getAllCategories(Unknown Source)
at edu.home.ltmj.controller.CategoryRestController.getAllCategories(CategoryRestController.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
(...)

我不明白原因。有一个接口CategoryDao,它具有与<select id="getAllCategories">匹配的正确方法getAllCategories。经过一段时间的使用之后,我将dao接口的名称更改为CategoryMapper,并更新了CategoryMapper.xml中的名称空间。完成此操作后,一切正常。另外,在对类和xml使用相同的名称后,我将dao类和xml映射器移到了不同??的包中(两个都使用相同的名称:CategoryMapper。),更新了xml文件中的名称空间,并得到了相同的异常,并更新消息以显示dao接口的程序包名称。但是再说一次,我将两个文件都移到了相同的程序包中,然后一切又恢复了。

所以,我的问题是:为什么MyBatis需要接口和xml映射器文件具有相同的名称并位于相同的包中?这是MyBatis设计还是Spring MyBatis中的问题?


您还有MyBatis Config文件吗?

如果我没有记错的话,当您想要进行无需额外配置的设置时,XML文件的名称与接口的位置相同。

如果您在其他地方有XML映射器,则可以使用MyBatis配置内的<mappers>元素手动指定XML文件的类路径。

来自"注入映射器"文档:

If the UserMapper has a corresponding MyBatis XML mapper file in the same classpath location as the mapper interface, it will be parsed automatically by the MapperFactoryBean. There is no need to specify the mapper in a MyBatis configuration file unless the mapper XML files are in a different classpath location. See the SqlSessionFactoryBean's configLocation property for more information.

因此,请尝试以下操作:

  • src/main/resources中创建一个mybatis-config.xml文件,其中包含以下文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?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>
      <mappers>
        <mapper resource="com/test/path/etc/etc/WhateverNameYouWant.xml"/>
      </mappers>
    </configuration>

    WhateverNameYouWant.xml包含CategoryMapper.xml包含的内容。

  • 设置配置文件的位置(如下所示的Java配置或applicationContext文件中的bean):

    1
    2
    3
    4
    5
    6
    7
    8
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // ....
        sessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        // ....
        return sessionFactory;
    }

  • 我在没有@MapperScan的情况下使用了以下方式:

    1)就像上面的步骤2一样设置mybatis-config.xml

    1
    2
    3
    4
    5
    6
    7
    8
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // ....
        sessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        // ....
        return sessionFactory;
    }

    2)设置CategoryDao

    1
    2
    3
    4
    5
    @Bean
    public CategoryDao getCategoryDao() throws Exception{
        SqlSessionTemplate sessionTemplate = new SqlSessionTemplate(sqlSessionFactoryBean());
        return sessionTemplate.getMapper( CategoryDao.class );
    }

    3)在mybatis-config.xml

    中进行设置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?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>

        <settings>
            <setting name="logImpl" value="COMMONS_LOGGING"/>
        </settings>

        <mappers>

            <mapper class="CategoryMapper.xml"/>
        </mappers>

    </configuration>