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

Today I was preparing an example using Spring Boot and using MyBatis to link data access next to Spring-MyBatis. Here is the relevant project configuration (using 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 file contents:

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> 

Then I insert an instance of this dao into the request controller (for testing purposes), for example:

 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 execution with curl localhost:8080/category/all , and then expect to see the results in JSON format, but instead got this exception:

 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 do not understand the reason for this. There is a CategoryDao interface and it has the correct getAllCategories method, which corresponds to <select id="getAllCategories"> . After some time playing with this, I changed the dao interface name to CategoryMapper and updated the namespace in CategoryMapper.xml. After I did this, everything worked fine . In addition, after the same name for the class and xml, I moved the dao and xml-mapper class to different packages (for example, using the same name for: CategoryMapper.), Updated the namespace in the XML file and received the same exception with an updated message to show the dao interface package name. But then again, I moved both files to the same package, and everything worked again.

So my question is: why does MyBatis need the interface and the xml mapper file to have the same name and be in the same package? Is this a MyBatis project or a problem in Spring MyBatis?

+5
source share
2 answers

Do you also have a MyBatis Config file?

If I remember correctly the same name for the XML file as an interface, when you want to have an installation that just works without any additional configuration.

If you have XML-mappers elsewhere, you can manually specify the class path of the XML files using the <mappers> element inside the MyBatis configuration .

From Documentation for Injectable Mappers:

If UserMapper has the corresponding Mapper XML file MyBatis in the same place as the mapper interface, it is automatically parsed by MapperFactoryBean. There is no need to specify mapper in the MyBatis configuration file if the mapper XML files are not in a different path location. See the SqlSessionFactoryBean configLocation property description for more information.

So try the following:

  • Create mybatis-config.xml file inside src/main/resources with 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.xml contains what is contained in your CategoryMapper.xml .

  • Set the location of the configuration file (Java configuration, as indicated below, or bean in the applicationContext file):

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

I used the following method without @MapperScan as follows:

1) Install mybatis-config.xml as step 2 above

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

2) Setting CategoryDao

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

3) Setting in 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> 
+1
source

All Articles