IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> maybatis源码分析(三)——mybatis如何获取sql语句? -> 正文阅读

[Java知识库]maybatis源码分析(三)——mybatis如何获取sql语句?

?

?

在mybatis中sql的来源一般分位两种方式:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?详细总结图

1.写在xml文件中,如:

2.通过注解写入sql,如:

在此文中我们将研究mybatis是如何获取他们,并把他们放在哪。

从上一篇博客(maybatis源码分析(二)——mybatis mapper的注册流程_zhaoliubao1的博客-CSDN博客)我们可以知道,mybatis首先会读取配置文件,并最终构建出mapper接口的代理工厂类,此时我们的mapper接口的相关信息已经被获取,那么接下来我们就要具体去寻找mapper接口对应的方法的sql。有两种情况,一种是写在xml文件中,另一种写通过注解的方式去记录将要执行的sql。

我们先研究mybatis是如何解析注解中携带的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);
      //------解析sql----//
      parser.parse();
?    //------解析sql----//
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

?其实我们可以看到,解析sql的时候我们需要传入该类的信息(文件路径)其实就是mapper注册的key值。

接下来便是真正解析sql了:

public void parse() {
  String resource = type.toString();
  //防止重复解析
  if (!configuration.isResourceLoaded(resource)) {
?   //通过xml去解析sql
    loadXmlResource();
    configuration.addLoadedResource(resource);
    assistant.setCurrentNamespace(type.getName());
    parseCache();
    parseCacheRef();
    //获取改接口的所有方法 遍历解析sql 组装ResultMap
    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 {
        //解析sql的入口
        parseStatement(method);
      } catch (IncompleteElementException e) {
        configuration.addIncompleteMethod(new MethodResolver(this, method));
      }
    }
  }
  parsePendingMethods();
}

先看注解解析入口代码:

//注解解析sql的入口
parseStatement(method);

拨开云雾我们终于看到了SqlSource 这便是sql源了

void parseStatement(Method method) {
  //获取该方法的返回值
  final Class<?> parameterTypeClass = getParameterType(method);
  final LanguageDriver languageDriver = getLanguageDriver(method);

  getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {
?   //这里便是最终得到的sql源代码了
    final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);
    final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();
    final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options)x.getAnnotation()).orElse(null);
    final String mappedStatementId = type.getName() + "." + method.getName();

    final KeyGenerator keyGenerator;
    String keyProperty = null;
    String keyColumn = null;
.....
}

首先我们需要先了解一下这个方法:

private Optional<AnnotationWrapper> getAnnotationWrapper(Method method, boolean errorIfNoMatch,
    Collection<Class<? extends Annotation>> targetTypes) {
  String databaseId = configuration.getDatabaseId();
  Map<String, AnnotationWrapper> statementAnnotations = targetTypes.stream()
      .flatMap(x -> Arrays.stream(method.getAnnotationsByType(x))).map(AnnotationWrapper::new)
      .collect(Collectors.toMap(AnnotationWrapper::getDatabaseId, x -> x, (existing, duplicate) -> {
        throw new BuilderException(String.format("Detected conflicting annotations '%s' and '%s' on '%s'.",
            existing.getAnnotation(), duplicate.getAnnotation(),
            method.getDeclaringClass().getName() + "." + method.getName()));
      }));
......
}

这段代码的意思就是:将某个方法的注解何预定的注解进行匹配,并返回。?

接下来我们将研究他是如何获取并赋值给?SqlSource对象的,也就是:

final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);

我们继续深入可以发现:

private SqlSource buildSqlSource(Annotation annotation, Class<?> parameterType, LanguageDriver languageDriver,
    Method method) {
  if (annotation instanceof Select) {
    return buildSqlSourceFromStrings(((Select) annotation).value(), parameterType, languageDriver);
  } else if (annotation instanceof Update) {
    return buildSqlSourceFromStrings(((Update) annotation).value(), parameterType, languageDriver);
  } else if (annotation instanceof Insert) {
    return buildSqlSourceFromStrings(((Insert) annotation).value(), parameterType, languageDriver);
  } else if (annotation instanceof Delete) {
    return buildSqlSourceFromStrings(((Delete) annotation).value(), parameterType, languageDriver);
  } else if (annotation instanceof SelectKey) {
    return buildSqlSourceFromStrings(((SelectKey) annotation).statement(), parameterType, languageDriver);
  }
  return new ProviderSqlSource(assistant.getConfiguration(), annotation, type, method);
}

通过先前getAnnotationWrapper我们已经得到了某个方法对应的注解,那在此处我们就可以知道这个注解的具体信息

我们看其中一段代码:

private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass,
    LanguageDriver languageDriver) {
  return languageDriver.createSqlSource(configuration, String.join(" ", strings).trim(), parameterTypeClass);
}

继续进入:

@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
  // issue #3
  if (script.startsWith("<script>")) {
    XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
    return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
  } else {
    // issue #127
    script = PropertyParser.parse(script, configuration.getVariables());
    TextSqlNode textSqlNode = new TextSqlNode(script);
    if (textSqlNode.isDynamic()) {
      return new DynamicSqlSource(configuration, textSqlNode);
    } else {
      return new RawSqlSource(configuration, script, parameterType);
    }
  }
}

通过我们前面的讲解 我们已经知道了 sql来源 ,所以如果我么的来源是注解并且没有<script>标签那么他将会走else逻辑,比如我们现在拿到了一条这样的sql:

?他究竟会怎么做呢?

我们进入他的解析器一探究竟:

public static String parse(String string, Properties variables) {
  VariableTokenHandler handler = new VariableTokenHandler(variables);
  GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
  return parser.parse(string);
}

我们可以看到 这个解析器主要是用于处理含有$符号的sql,含有#号的sql是通RawSqlSource

类来处理,所以我们将会看到:

public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
  SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
  Class<?> clazz = parameterType == null ? Object.class : parameterType;
  sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}

继续进入将会看到:

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
  ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
  GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
  String sql;
  if (configuration.isShrinkWhitespacesInSql()) {
    sql = parser.parse(removeExtraWhitespaces(originalSql));
  } else {
    sql = parser.parse(originalSql);
  }
  return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

?

通过我们的通用解析器,最终我们将会得到:

?

?自此我们将得到了每个statement的目标sql

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-04 15:22:29  更:2022-03-04 15:23:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:02:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码