【Mybatis】如何继承Mybatis中的Mapper.xml文件

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

作者石臻臻, CSDN博客之星Top5Kafka Contributornacos Contributor华为云 MVP ,腾讯云TVP, 滴滴Kafka技术专家KnowStreaming, 《Kafka运维与实战宝典》电子书作者。 领取《Kafka运维与实战宝典》PDF请联系石臻臻


KnowStreaming  是滴滴开源的Kafka运维管控平台, 有兴趣一起参与参与开发的同学,但是怕自己能力不够的同学,可以联系我,当你导师带你参与开源!

最近在写一个 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 codeMapperBuilderAssistant

 

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由于没有被注册,所以报错了;

有兴趣可以下载阅读或者直接使用我整合的自动生成扩展插件

目录
相关文章
|
8月前
|
SQL Java 数据库连接
【YashanDB知识库】解决mybatis的mapper文件sql语句结尾加分号";"报错
【YashanDB知识库】解决mybatis的mapper文件sql语句结尾加分号";"报错
|
8月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
384 0
|
6月前
|
Android开发 开发者
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
本文详细介绍了如何通过自定义 `attrs.xml` 文件实现 Android 自定义 View 的属性配置。以一个包含 TextView 和 ImageView 的 DemoView 为例,讲解了如何使用自定义属性动态改变文字内容和控制图片显示隐藏。同时,通过设置布尔值和点击事件,实现了图片状态的切换功能。代码中展示了如何在构造函数中解析自定义属性,并通过方法 `setSetting0n` 和 `setbackeguang` 实现功能逻辑的优化与封装。此示例帮助开发者更好地理解自定义 View 的开发流程与 attrs.xml 的实际应用。
128 2
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
|
6月前
|
SQL XML Java
菜鸟之路Day35一一Mybatis之XML映射与动态SQL
本文介绍了MyBatis框架中XML映射与动态SQL的使用方法,作者通过实例详细解析了XML映射文件的配置规范,包括namespace、id和resultType的设置。文章还对比了注解与XML映射的优缺点,强调复杂SQL更适合XML方式。在动态SQL部分,重点讲解了`&lt;if&gt;`、`&lt;where&gt;`、`&lt;set&gt;`、`&lt;foreach&gt;`等标签的应用场景,如条件查询、动态更新和批量删除,并通过代码示例展示了其灵活性与实用性。最后,通过`&lt;sql&gt;`和`&lt;include&gt;`实现代码复用,优化维护效率。
483 5
|
XML 前端开发 Java
讲解SSM的xml文件
本文详细介绍了SSM框架中的xml配置文件,包括springMVC.xml和applicationContext.xml,涉及组件扫描、数据源配置、事务管理、MyBatis集成以及Spring MVC的视图解析器配置。
245 1
|
8月前
|
XML Java 数据库连接
二、搭建MyBatis采用xml方式,验证CRUD(增删改查操作)
二、搭建MyBatis采用xml方式,验证CRUD(增删改查操作)
232 21
|
8月前
|
SQL Java 数据库连接
【YashanDB 知识库】解决 mybatis 的 mapper 文件 sql 语句结尾加分号";"报错
【YashanDB 知识库】解决 mybatis 的 mapper 文件 sql 语句结尾加分号";"报错
|
12月前
|
Java Maven
maven项目的pom.xml文件常用标签使用介绍
第四届人文,智慧教育与服务管理国际学术会议(HWESM 2025) 2025 4th International Conference on Humanities, Wisdom Education and Service Management
910 8
|
12月前
|
XML Android开发 数据格式
Eclipse 创建 XML 文件
Eclipse 创建 XML 文件
173 2
|
12月前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
438 5