为什么接口和 xml 映射器文件必须在同一个包中并具有相同的名称?

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

今天准备了一个使用SpringBoot的例子,在Spring-MyBatis旁边使用MyBatis进行数据访问通信。这里是相关的项目配置(使用maven):

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:

package edu.home.ltmj.dao;

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

CategoryMapper.xml:

<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 的一个实例注入请求控制器(用于测试目的),如下所示:

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 格式的结果,但我得到了这个异常:

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 中的名称空间。在我这样做之后,一切正常。此外,在 class 和 xml 具有相同的名称后,我将 dao class 和 xml 映射器移动到不同的包中(仍然使用相同的名称:CategoryMapper .), 更新了 xml 文件中的命名空间,得到了同样的异常,消息更新为显示 dao 接口包的名称。但是话又说回来,我将这两个文件移到了同一个包中,一切又恢复了。

所以,我的问题是:为什么MyBatis需要接口和xml映射器文件同名并且在同一个包中?是这个 MyBatis 设计或 Spring MyBatis?

中的问题

你还有MyBatis Config文件吗?

如果我没记错的话,XML 文件的名称与界面的位置相同,当您想要一个无需额外配置即可运行的设置时。

如果您在其他地方有 XML 映射器,您可以使用 MyBatis configuration.[=21= 中的 <mappers> 元素手动指定 XML 文件的类路径]

来自Injecting Mappers documentation:

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.

所以试试这个:

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

    <?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 包含的内容。

  2. 设置配置文件的位置(Java配置如下或applicationContext文件中的bean):

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // ....
        sessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        // ....
        return sessionFactory;
    }
    

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

1) 设置mybatis-config.xml 同上面的步骤2

@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
    SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
    // ....
    sessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
    // ....
    return sessionFactory;
}

2) 设置 CategoryDao

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

3) mybatis内设置-config.xml

<?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>