首页 > 其他分享 >Mybatis如何添加映射接口和映射文件?

Mybatis如何添加映射接口和映射文件?

时间:2023-08-12 18:22:06浏览次数:37  
标签:Configuration 映射 接口 Mybatis new 解析 type method

Mybatis是一种半ORM框架,需要我们手动编写SQL语句。

在启动时,它会将SQL语句等信息读取到内存中,便于操作数据库时进行参数解析、执行SQL和结果封装。

使用过Mybatis的都知道,它有两种方式编写SQL语句:

  1. xml映射文件
  2. 映射接口方法上的注解

在启动Mybatis时,可以通过Configuration的addMappers(basePackage)方法添加映射接口和映射文件,读取上述两种方式编写的SQL语句等信息。

实际上,该方法对MapperRegistry#addMappers(basePackage, superType)进行了代理,执行逻辑如下:

  1. 读取指定包路径下的.class文件
  2. 加载类对象
  3. 过滤出superType的子类
  4. 解析所有子类:子类对应的xml文件和子类方法上的注解

MapperRegistry#addMappers(basePackage, superType)源码如下,包括上述所有执行逻辑:

public void addMappers(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  // 读取指定包路径下的.class文件,过滤出superType的子类
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
  // 解析所有子类
  for (Class<?> mapperClass : mapperSet) {
    addMapper(mapperClass);
  }
}

具体解析逻辑位于MapperRegistry#addMapper,它会将解析完的映射接口添加到knownMappers中(需要注意的是,SQL语句等信息并不保存在这里):

public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      knownMappers.put(type, new MapperProxyFactory<>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      // 解析xml和注解
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

MapperAnnotationBuilder#parse会解析该映射接口对应的xml映射文件,以及映射接口方法上的注解:

public void parse() {
  String resource = type.toString();
  if (!configuration.isResourceLoaded(resource)) {
    // 解析xml映射文件
    loadXmlResource();
    configuration.addLoadedResource(resource);

    // 解析注解
    assistant.setCurrentNamespace(type.getName());
    parseCache();
    parseCacheRef();
    for (Method method : type.getMethods()) {
      if (!canHaveStatement(method)) {
        continue;
      }
      if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
          && method.getAnnotation(ResultMap.class) == null) {
        parseResultMap(method);
      }
      try {
        parseStatement(method);
      } catch (IncompleteElementException e) {
        configuration.addIncompleteMethod(new MethodResolver(this, method));
      }
    }
  }
  parsePendingMethods();
}

解析xml映射文件时,会读取全限定类名对应的.xml文件,将SQL语句等信息分别添加到Configuration的ResultMap和MappedStatement等成员变量缓存中:

public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    // 解析XML节点
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }
  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

private void configurationElement(XNode context) {
  try {
    // 获取namespace
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.isEmpty()) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    // 解析cache-ref到Configuration的cacheRefMap
    cacheRefElement(context.evalNode("cache-ref"));
    // 解析cache到Configuration的caches
    cacheElement(context.evalNode("cache"));
    // 解析parameterMap到Configuration的parameterMaps
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    // 解析resultMap到Configuration的resultMaps
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    // 解析sql到XMLMapperBuilder的sqlFragments
    sqlElement(context.evalNodes("/mapper/sql"));
    // 解析select|insert|update|delete到Configuration的mappedStatements
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}

解析映射接口方法上的注解时,会读取每个方法上的注解信息,将SQL语句等信息分别添加到Configuration的ResultMap和MappedStatement等成员变量缓存中:

for (Method method : type.getMethods()) {
  if (!canHaveStatement(method)) {
    continue;
  }
  if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
      && method.getAnnotation(ResultMap.class) == null) {
    // 解析Arg、Result和TypeDiscriminator等注解到Configuration的resultMaps
    parseResultMap(method);
  }
  try {
    // 解析Select、Update、Insert、Delete、SelectKey和ResultMap等注解到Configuration的mappedStatements
    parseStatement(method);
  } catch (IncompleteElementException e) {
    configuration.addIncompleteMethod(new MethodResolver(this, method));
  }
}

标签:Configuration,映射,接口,Mybatis,new,解析,type,method
From: https://www.cnblogs.com/Xianhuii/p/17625217.html

相关文章

  • 编写一份接口需求文档
    一、什么是接口百科上对接口的定义:API(ApplicationProgrammingInterface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。理解一下为什么要用接口?两个独立的系统,它们的......
  • 文件映射缺页中断和匿名页面缺页中断的区别
    文件映射缺页中断和匿名页面缺页中断主要区别在于缺页时的处理方式和触发原因。对于文件映射缺页中断,当程序需要访问文件映射的某个页面但该页面尚未调入内存时,就会触发文件映射缺页中断。这时,操作系统会根据文件映射的约定从磁盘加载相应的文件内容到内存中,然后更新页表,使得程序......
  • jmeter通过BeanShell对接口参数进行MD5和HmacSHA256加密【杭州多测师_王sir】
    一、在eclipse里面编写MD5加密算法packagecom.Base64;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmException;publicclassMd5Utils{publicstaticStringmd5(StringsourceStr){Stringresult="";try......
  • 细谈商品详情API接口设计
    当我们构建电商网站或应用时,常常需要获取淘宝商品的详细信息。为了实现这个功能,我们可以利用淘宝提供的开放平台API接口来获取商品数据。在这篇文章中,我们将学习如何设计一个商品详情API接口,并提供相应的代码示例。首先,我们需要注册淘宝开放平台账号,并创建一个新的应用来获取API访......
  • 细谈商品详情API接口设计
    当我们构建电商网站或应用时,常常需要获取淘宝商品的详细信息。为了实现这个功能,我们可以利用淘宝提供的开放平台API接口来获取商品数据。在这篇文章中,我们将学习如何设计一个商品详情API接口,并提供相应的代码示例。首先,我们需要注册淘宝开放平台账号,并创建一个新的应用来获取API......
  • Mybatis简介
    1.1、Mybatis简介●MyBatis是一款优秀的持久层框架●它支持定制化SQL、存储过程以及高级映射。●MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。●MyBatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POjO(PlainOldJavaObjects,普通老式Java......
  • SpringBoot复习:(19)Condition接口和@Conditional注解
    Condition接口代码如下:publicinterfaceCondition{ booleanmatches(ConditionContextcontext,AnnotatedTypeMetadatametadata);}它是一个函数式接口,只有一个方法matches用来表示条件是否满足。matches方法中的ConditionContext类对象context可以通过getEnvironment方法获......
  • MyBatis 实例
    MyBatis简介MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来配置和映射原始类型、接口和JavaPOJO(PlainOldJavaObjects,普通老式Java......
  • AbilityInputComponent,支持EnhancedInputComponent和GAS的操作映射
    目前完成进度输入系统的基本搭建​ 支持输入按键触发对应的Ability,同时支持按键的按下和释放两种状态的区分并可以在代码或蓝图中自定义特殊逻辑能力输入系统详解​ 通过配置文件来保存Action和Ability的对应关系,对应的关键词条就是GameplayTag。通过一个GameplayTag可以找到......
  • 第一节:业务幂等性介绍 和 接口幂等性的解决方案
    一.        二.        三.         !作       者:Yaopengfei(姚鹏飞)博客地址:http://www.cnblogs.com/yaopengfei/声     明1:如有错误,欢迎讨论,请勿谩骂^_^。声     明2:原创博客请在转载......