「Mybatis」如何继承Mybatis中的Mapper.xml文件

最近在写一个 Mybatis 代码自动生成插件,用的是Mybatis来扩展,其中有一个需求就是 生成javaMapper文件和 xmlMapper文件的时候 希望另外生成一个扩展类和扩展xml文件。原文件不修改,只存放一些基本的信息,开发过程中只修改扩展的Ext文件

形式如下:

SrcTestMapper.java


package com.test.dao.mapper.srctest;
import com.test.dao.model.srctest.SrcTest;
import com.test.dao.model.srctest.SrcTestExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface SrcTestMapper {
 long countByExample(SrcTestExample example);
 int deleteByExample(SrcTestExample example);
 int deleteByPrimaryKey(Integer id);
 int insert(SrcTest record);
 int insertSelective(SrcTest record);
 List<SrcTest> selectByExample(SrcTestExample example);
 SrcTest selectByPrimaryKey(Integer id);
 int updateByExampleSelective(@Param("record") SrcTest record, @Param("example") SrcTestExample example);
 int updateByExample(@Param("record") SrcTest record, @Param("example") SrcTestExample example);
 int updateByPrimaryKeySelective(SrcTest record);
 int updateByPrimaryKey(SrcTest record);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

SrcTestMapperExt.java


package com.test.dao.mapper.srctest;
import com.test.dao.model.srctest.SrcTest;
import org.apache.ibatis.annotations.Param;
import javax.annotation.Resource;
import java.util.List;
/**
* SrcTestMapperExt接口
* Created by shirenchuang on 2018/6/30.
*/
@Resource
public interface SrcTestMapperExt extends SrcTestMapper {
 List<SrcTest> selectExtTest(@Param("age") int age);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

SrcTestMapper.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.dao.mapper.srctest.SrcTestMapperExt">
 <resultMap id="BaseResultMap" type="com.test.dao.model.srctest.SrcTest">
 <id column="id" jdbcType="INTEGER" property="id" />
 <result column="name" jdbcType="VARCHAR" property="name" />
 <result column="age" jdbcType="INTEGER" property="age" />
 <result column="ctime" jdbcType="BIGINT" property="ctime" />
 </resultMap>
<!-- 省略....-->
</mapper>
1
2
3
4
5
6
7
8
9
10
11

SrcTestMapperExt.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.dao.mapper.srctest.SrcTestMapperExt">
 <select id="selectExtTest" resultMap="BaseResultMap">
 select * from src_test where age>#{age}
 </select>
</mapper>
1
2
3
4
5
6
7
8

注意:这里返回的resultMap=”BaseResultMap” 这个Map并没有再这个xml中定义,这样能使用吗?

上面是我生成的代码;并且能够正常使用;

那么SrcTestMapperExt.xml是如何继承SrcTestMapper.xml中的定义的呢?

1. 修改命名空间,使他们的命名空间相同,namespace=”com.test.dao.mapper.srctest.SrcTestMapperExt”


2. 光这样还不够,因为这个时候你去运行的时候会报错


Caused by: org.apache.ibatis.builder.BuilderException: Wrong namespace. Expected 'com.test.dao.mapper.srctest.SrcTestMapper' but found 'com.test.dao.mapper.srctest.SrcTestMapperExt'.
1

因为Mybatis中是必须要 xml的文件包名和文件名必须跟 Mapper.java对应起来的

比如com.test.dao.mapper.srctest.SrcTestMapper.java这个相对应的是

com.test.dao.mapper.srctest.SrcTestMapper.xml

必须是这样子,没有例外,否则就会报错

show the code

MapperBuilderAssistant

public void setCurrentNamespace(String currentNamespace) {
 if (currentNamespace == null) {
 throw new BuilderException("The mapper element requires a namespace attribute to be specified.");
 }
 if (this.currentNamespace != null && !this.currentNamespace.equals(currentNamespace)) {
 throw new BuilderException("Wrong namespace. Expected '"
 + this.currentNamespace + "' but found '" + currentNamespace + "'.");
 }
 this.currentNamespace = currentNamespace;
 }
1
2
3
4
5
6
7
8
9
10
11
12

这个this.currentNamespace 和参数传进来的currentNamespace比较是否相等;

参数传进来的currentNamespace就是我们xml中的

<mapper namespace="com.test.dao.mapper.srctest.SrcTestMapperExt"> 值;

然后this.currentNamespace是从哪里设置的呢?this.currentNamespace = currentNamespace;

跟下代码:MapperAnnotationBuilder

public void parse() {
 String resource = type.toString();
 if (!configuration.isResourceLoaded(resource)) {
 loadXmlResource();
 configuration.addLoadedResource(resource);
 assistant.setCurrentNamespace(type.getName());
 parseCache();
 parseCacheRef();
 Method[] methods = type.getMethods();
 for (Method method : methods) {
 try {
 // issue #237
 if (!method.isBridge()) {
 parseStatement(method);
 }
 } catch (IncompleteElementException e) {
 configuration.addIncompleteMethod(new MethodResolver(this, method));
 }
 }
 }
 parsePendingMethods();
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

看到 assistant.setCurrentNamespace(type.getName());

它获取的是 type.getName() ;这个type的最终来源是

MapperFactoryBean

@Override
 protected void checkDaoConfig() {
 super.checkDaoConfig();
 notNull(this.mapperInterface, "Property 'mapperInterface' is required");
 Configuration configuration = getSqlSession().getConfiguration();
 if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
 try {
 configuration.addMapper(this.mapperInterface);
 } catch (Exception e) {
 logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
 throw new IllegalArgumentException(e);
 } finally {
 ErrorContext.instance().reset();
 }
 }
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

看configuration.addMapper(this.mapperInterface);这行应该就明白了

加载mapperInterface的时候会跟相应的xml映射,并且会去检验namespace是否跟mapperInterface相等!

那么既然命名空间不能修改,那第一条不白说了?还怎么实现Mapper.xml的继承啊?

别慌,既然是这样子,那我们可以让 MapperInterface 中的SrcTestMapper.java别被加载进来就行了啊!!

只加载 MapperExt.java不就行了?

3. 修改applicationContext.xml,让Mapper.java不被扫描


Mapper.java接口扫描配置

<!-- Mapper接口所在包名,Spring会自动查找其下的Mapper -->
 <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
 <property name="basePackage" value="com.test.dao.mapper"/>
<!--
 该属性实际上就是起到一个过滤的作用,如果设置了该属性,那么MyBatis的接口只有包含该注解,才会被扫描进去。
-->
 <property name="annotationClass" value="javax.annotation.Resource"/>
 <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
 </bean>
1
2
3
4
5
6
7
8
9

basePackage 把Mapper.java扫描进去没有关系,重点是

<property name="annotationClass" value="javax.annotation.Resource"/>

这样 MapperScanner会把没有配置注解的过滤掉;

回头看我们的MapperExt.java配置文件是有加上注解的

/**
* SrcTestMapperExt接口
* Created by shirenchuang on 2018/6/30.
*/
@Resource
public interface SrcTestMapperExt extends SrcTestMapper {
 List<SrcTest> selectExtTest(@Param("age") int age);
}
1
2
3
4
5
6
7
8

这样子之后,基本上问题就解决了,还有一个地方特别要注意一下的是.xml文件的配置

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource"/>
 <property name="configLocation" value="classpath:mybatis-config.xml"/>
 <!-- 必须将mapper,和mapperExt也一起扫描-->
 <property name="mapperLocations" value="classpath:com/test/dao/mapper/**/*.xml"/>
 </bean>
1
2
3
4
5
6

这样配置没有错,但是我之前的配置写成了

<property name="mapperLocations" value="classpath:com/test/dao/mapper/**/*Mapper.xml"/>

这样子 MapperExt.xml 没有被扫描进去,在我执行单元测试的时候

@Test
 public void selectExt(){
 List<SrcTest> tests = srcTestService.selectExtTest(9);
 System.out.println(tests.toString());
 }
1
2
3
4
5

err_console

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.test.dao.mapper.srctest.SrcTestMapperExt.selectExtTest
1
2

但是执行 ` srcTestService.insertSelective(srcTest);不会出错

原因就是 insertSelective是在SrcTestMapper.xml中存在 ,已经被注册到

com.test.dao.mapper.srctest.SrcTestMapperExt命名空间了,但是selectExtTest由于没有被注册,所以报错了;

有兴趣可以下载阅读或者直接使用我整合的

「Mybatis」如何继承Mybatis中的Mapper.xml文件

相关推荐