版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa
版权协议,转载请附上原文出处链接和本声明。
本文链接: http://blog.shiyi.online/articles/2019/08/15/1565846912291.html
最近在写一个 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);
}
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);
}
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>
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>
注意:这里返回的 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'.
因为 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;
}
这个 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();
}
看到 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();
}
}
}
看 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>
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);
}
这样子之后,基本上问题就解决了,还有一个地方特别要注意一下的是.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>
这样配置没有错,但是我之前的配置写成了
<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());
}
err_console
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.test.dao.mapper.srctest.SrcTestMapperExt.selectExtTest
但是执行 ````srcTestService.insertSelective(srcTest);不会出错 原因就是 insertSelective是在SrcTestMapper.xml中存在 ,已经被注册到
com.test.dao.mapper.srctest.SrcTestMapperExt``` 命名空间了,但是 selectExtTest 由于没有被注册,所以报错了;
有兴趣可以下载阅读或者直接使用我整合的
自动生成扩展插件
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于