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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30253696/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 16:46:38  来源:igfitidea点击:

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

javaspringspring-bootmybatisspring-mybatis

提问by Luiggi Mendoza

Today I was preparing an example using Spring Boot and using MyBatis for the data access communication next to Spring-MyBatis. Here is the relevant project configuration (using maven):

今天在准备一个使用Spring Boot的例子,在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

Relevant content of the files:

文件的相关内容:

CategoryDao.java:

类别Dao.java:

package edu.home.ltmj.dao;

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

CategoryMapper.xml:

类别映射器.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>

Then, I inject an instance of this dao in a request controller (for testing purposes), like this:

然后,我将这个 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();
    }
}

I run my project and test the execution by using curl localhost:8080/category/alland then expected to see the results in JSON format, but I got this exception instead:

我运行我的项目并通过使用测试执行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)
(...)

I don't understand the cause of this. There's an interface CategoryDaoand it has the proper method getAllCategoriesthat matches with <select id="getAllCategories">. After some time of playing with this, I've changed the name of the dao interface to CategoryMapperand updated the namespace in CategoryMapper.xml. After I did this, everything worked normally. Also, after having the same name for class and xml, I moved the dao class and the xml mapper into different packages (stil using the same name for both: CategoryMapper.), updated the namespace in the xml file, and got the same exception, with the message updated to show the name of the package of the dao interface. But then again, I moved both files to the same package and everything worked again.

我不明白这是什么原因。有一个接口CategoryDao,它具有getAllCategories<select id="getAllCategories">. 在玩了一段时间之后,我将 dao 接口的名称更改为CategoryMapper并更新了 CategoryMapper.xml 中的命名空间。我这样做后,一切正常。此外,在类和 xml 具有相同的名称后,我将 dao 类和 xml 映射器移到不同的包中(仍然使用相同的名称:CategoryMapper。),更新了 xml 文件中的命名空间,并得到了相同的异常, 更新消息以显示 dao 接口的包名称。但话又说回来,我将两个文件移到同一个包中,一切又恢复了。

So, my question is: why does MyBatis need that the interface and the xml mapper file to have the same name and be in the same package?Is this MyBatis design or an issue in Spring MyBatis?

那么,我的问题是:为什么 MyBatis 需要接口和 xml mapper 文件具有相同的名称并在同一个包中?这是 MyBatis 的设计还是 Spring MyBatis 的问题?

采纳答案by Bogdan

Do you also have a MyBatis Config file?

你也有 MyBatis 配置文件吗?

If I remember correctly the same name same location for the XML file as the interface is when you want to have a setup that just works with no extra configuration.

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

If you have the XML mappers somewhere else you can manually specify the classpath of the XML files using a <mappers>element inside MyBatis configuration.

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

From the 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.

如果 UserMapper 在与映射器接口相同的类路径位置有对应的 MyBatis XML 映射器文件,它会被 MapperFactoryBean 自动解析。除非映射器 XML 文件位于不同的类路径位置,否则无需在 MyBatis 配置文件中指定映射器。有关更多信息,请参阅 SqlSessionFactoryBean 的 configLocation 属性。

So try this:

所以试试这个:

  1. Create a mybatis-config.xmlfile inside src/main/resourceswith this in it:

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

    Where WhateverNameYouWant.xmlcontains what your CategoryMapper.xmlcontained.

  2. Set the location of the config file (Java configuration as below or bean in the applicationContextfile):

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // ....
        sessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        // ....
        return sessionFactory;
    }
    
  1. mybatis-config.xml在里面创建一个文件src/main/resources

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

回答by Ian Lim

I used the following way without @MapperScan as follows :

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

1) Setup mybatis-config.xml just as step 2 above

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

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

2) Setup CategoryDao

2) 设置 CategoryDao

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

3) Setup within mybatis-config.xml

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>