zoukankan      html  css  js  c++  java
  • Mybatis源码解析-BoundSql

    mybatis作为持久层,其操作数据库离不开sql语句。而BoundSql则是其保存Sql语句的对象

    前提

    1. 针对mybatis的配置文件的节点解析,比如where/if/trim的节点解析可见文章Spring mybatis源码篇章-NodeHandler实现类具体解析保存Dynamic sql节点信息

    2. 针对mybatis配置文件的解析帮助类SqlSource[一般为DynamicSqlSource]的使用可见文章Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource

    3. 对BoundSql对象的调用获取可见文章Mybatis源码分析-BaseExecutor

    本文将在上述的知识前提下展开对Sql语句的解析

    BoundSql的引用

    主要是通过MappedStatement#getBoundSql()方法调用获取的。我们可以简单跟踪下其中的源码,如下

      public BoundSql getBoundSql(Object parameterObject) {
        // 通过SqlSource获取BoundSql对象
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        // 校验当前的sql语句有无绑定parameterMapping属性
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings == null || parameterMappings.isEmpty()) {
          boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
        }
    
        // check for nested result maps in parameter mappings (issue #30)
        for (ParameterMapping pm : boundSql.getParameterMappings()) {
          String rmId = pm.getResultMapId();
          if (rmId != null) {
            ResultMap rm = configuration.getResultMap(rmId);
            if (rm != null) {
              hasNestedResultMaps |= rm.hasNestedResultMaps();
            }
          }
        }
    
        return boundSql;
      }
    

    RawSqlSource-常用的mybatis解析sql帮助类

    我们观察下其getBoundSql()方法,源码如下

      public BoundSql getBoundSql(Object parameterObject) {
        //此处的sqlSource为RawSqlSource的内部属性
        return sqlSource.getBoundSql(parameterObject);
      }
    

    我们看下sqlSource是如何生成的,由此观察其构造函数

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

    #{}的使用这里稍微提下,一般的写法都为{name,jdbcType=String,mode=out,javaType=java.lang.String...},其中jdbcType也可以不指定,系统会自动识别。上述的代码其实主要就是针对#{}字符内容的处理

    注意:${}这样的字符是通过DynamicSqlSource来完成解析的,具体的解析读者可自行分析

    我们可以继续看下SqlSourceBuilder类是如何解析获取sql语句的

    SqlSourceBuilder#parse()

    直接上源码

    public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
        // 对#{}这样的字符串内容的解析处理类
        ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
        GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
        // 获取真实的可执行性的sql语句
        String sql = parser.parse(originalSql);
        // 包装成StaticSqlSource返回
        return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
      }
    

    简单的看下ParameterMappingTokenHandler是如何解析的,其是TokenHandler接口的实现类,我们就关注实现方法handleToken

    @Override
        public String handleToken(String content) {
          // 此处的作用就是对`#{}`节点中的key值保存映射,比如javaType/jdbcType/mode等信息,限于篇幅过长,读者可自行分析          
          parameterMappings.add(buildParameterMapping(content));
          // 将`#{}`替换为?,即一般包装成`select * form test where name=? and age=?`预表达式语句
          return "?";
        }
    

    上述主要通过ParameterMappingTokenHandler类来完成对#{}字符串的解析,其中的映射信息则保存至BoundSql的parameterMappings属性中

    总结

    1. BoundSql语句的解析主要是通过对#{}字符的解析,将其替换成?。最后均包装成预表达式供PrepareStatement调用执行

    2. #{}中的key属性以及相应的参数映射,比如javaType、jdbcType等信息均保存至BoundSql的parameterMappings属性中供最后的预表达式对象PrepareStatement赋值使用

  • 相关阅读:
    20200816
    20200815
    20200813
    20200811
    20200810
    20200806
    20200804
    20200803
    20200802
    20200801
  • 原文地址:https://www.cnblogs.com/question-sky/p/7535482.html
Copyright © 2011-2022 走看看