详解如何继承Mybatis中Mapper.xml文件

作者:石臻臻的杂货铺 时间:2022-12-22 14:48:34 

引言

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

修改扩展Ext文件

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中的定义的呢?

修改命名空间

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

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

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是从哪里设置的呢?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不就行了?

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

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

来源:https://juejin.cn/post/7148371447877287973

标签:Mybatis,Mapper.xml,文件继承
0
投稿

猜你喜欢

  • Spring Security 强制退出指定用户的方法

    2022-10-04 18:13:04
  • Java用栈实现综合计算器

    2021-08-16 02:01:36
  • Java中的字符串常量池详细介绍

    2023-03-08 09:16:41
  • java搜索无向图中两点之间所有路径的算法

    2023-11-10 09:28:26
  • Eureka源码阅读之环境搭建及工程结构

    2023-07-26 01:02:36
  • C# Winform消息通知系统托盘气泡提示框ToolTip控件

    2023-01-13 23:31:02
  • Java泛型T,E,K,V,N,?与Object区别和含义

    2022-02-23 07:25:30
  • java模拟微信抢红包的实例代码

    2021-12-08 19:51:21
  • Idea中SpringBoot多模块项目的建立实现

    2023-11-08 07:52:34
  • Java实现五子棋游戏

    2022-07-08 12:50:27
  • Java设计模式之抽象工厂模式实例详解

    2023-11-29 04:04:57
  • Java多线程回调方法实例解析

    2023-11-04 01:40:01
  • 浅谈Java变量的初始化顺序详解

    2023-07-26 03:23:17
  • Java经典面试题最全汇总208道(三)

    2023-11-15 23:30:42
  • Java多线程开发工具之CompletableFuture的应用详解

    2023-08-30 00:43:00
  • Spring@Value使用获取配置信息为null的操作

    2021-08-01 23:46:27
  • Springboot如何根据实体类生成数据库表

    2023-11-20 13:54:39
  • java联调生成测试数据工具类方式

    2021-10-12 06:42:17
  • 输出的文本实现对齐的方法(超简单)

    2023-06-19 03:57:09
  • SpringBoot自动配置深入探究实现原理

    2023-08-06 09:59:58
  • asp之家 软件编程 m.aspxhome.com