33、SQL解析基础

BoundSql

BoundSql只是一个简单的java对象,有两个属性比较重要
- sql:从解析时可以看出这个sql不是配置文件中的sql,这个sql已经经过了处理(如:占用位符的处理、动态语句的解析if、foreach等待)
- parameterObject:客户端执行sql时传入的参数
- parameterMappings: sql中的参数映射对应#{id,jdbcType=INTEGER}标签sql对应的参数列表

字段

““java
public class BoundSql {

/**
 * 经过处理的sql,这个sql已经可以被数据库执行了
 */
private String sql;
/**
 * sql中的参数映射对应#{id,jdbcType=INTEGER}
 */
private List<ParameterMapping> parameterMappings;

/**
 * 客户端执行sql时传入的参数
 */
private Object parameterObject;
private Map<String, Object> additionalParameters;
private MetaObject metaParameters;

}
““

SqlNode

java
public interface SqlNode {
boolean apply(DynamicContext context);
}

该方法的含义为,将sql的处理结果,append到DynamicContext context对象中,DynamicContext可以理解为StringBuilder对象的功能,它的作用就是计算sql片段并append到一起,形成最终的sql。

类似的实现还有whereSQLNode SetSqlNode ForEachSqlNode

VarDeclSqlNode

处理动态sql标签的SqlNode类。

    @Override
    public boolean apply(DynamicContext context) {
        final Object value = OgnlCache.getValue(expression, context.getBindings());
        // 由于没有sql可append,仅是把bind标签的变量名和值保存至上下文参数列表内
        context.bind(name, value);
        return true;
    }

MixedSqlNode

意为混合的SqlNode,它保存了其他多种SqlNode的集合,可以看做是一个List列表

SqlNode的组合设计模式

SqlNode,采用了组合设计模式,组合设计模式可以用来表示经典的树型结构

前面说的MixedSqlNode,就代表了List集合

NodeHandler

SqlNode是由NodeHandler创建出来的。

 private interface NodeHandler {
    void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
  }

BindHandler

用来生成VarDeclSqlNode

    @Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      final String name = nodeToHandle.getStringAttribute("name");
      final String expression = nodeToHandle.getStringAttribute("value");
      final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
      targetContents.add(node);
    }

TrimHandler

@Override
    public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
      List<SqlNode> contents = parseDynamicTags(nodeToHandle);
      MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
      String prefix = nodeToHandle.getStringAttribute("prefix");
      String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
      String suffix = nodeToHandle.getStringAttribute("suffix");
      String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
      TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
      targetContents.add(trim);
    }

ChooseHandler

   @Override
      public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
        List<SqlNode> whenSqlNodes = new ArrayList<SqlNode>();
        List<SqlNode> otherwiseSqlNodes = new ArrayList<SqlNode>();
        handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
        SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
        ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
        targetContents.add(chooseSqlNode);
      }

      private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {
        List<XNode> children = chooseSqlNode.getChildren();
        for (XNode child : children) {
          String nodeName = child.getNode().getNodeName();
          NodeHandler handler = nodeHandlers(nodeName);
          if (handler instanceof IfHandler) {
            handler.handleNode(child, ifSqlNodes);
          } else if (handler instanceof OtherwiseHandler) {
            handler.handleNode(child, defaultSqlNodes);
          }
        }
      }

      private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
        SqlNode defaultSqlNode = null;
        if (defaultSqlNodes.size() == 1) {
          defaultSqlNode = defaultSqlNodes.get(0);
        } else if (defaultSqlNodes.size() > 1) {
          throw new BuilderException("Too many default (otherwise) elements in choose statement.");
        }
        return defaultSqlNode;
      }

可以清楚看出,标签是和、标签配合使用的,创建ChooseSqlNode时,就同时创建了when、otherwise的逻辑,而when会转换为if标签处理,otherwise则转换为SqlNode处理,一般是StaticTextSqlNode。

LanguageDriver

LanguageDriver是一个辅助工具类,用于创建SqlSource。

  • XMLLanguageDriver:用于创建动态、静态SqlSource。
  • RawLanguageDriver:在确保只有静态sql时,可以使用,不得含有任何动态sql的内容,否则,请使用XMLLanguageDriver。它其实是对XMLLanguageDriver创建的结果进行唯静态sql检查而已,发现有动态sql的内容,就抛异常。

DynamicContext

对传入的parameterObject进行map化,

字段

““java

public static final String PARAMETER_OBJECT_KEY = "_parameter";
public static final String DATABASE_ID_KEY = "_databaseId";

static {
    OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
}

private final ContextMap bindings;
private final StringBuilder sqlBuilder = new StringBuilder();
private int uniqueNumber = 0;

““
ContextAccessor也是DynamicContext的内部类,实现了Ognl中的PropertyAccessor接口,为Ognl提供了如何使用ContextMap参数对象的说明

构造方法

    public DynamicContext(Configuration configuration, Object parameterObject) {
        if (parameterObject != null && !(parameterObject instanceof Map)) {//如果参数是map
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            bindings = new ContextMap(metaObject);
        } else {
            bindings = new ContextMap(null);
        }
        bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
        bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
    }

在DynamicContext的构造函数中,可以看到,根据传入的参数对象是否为Map类型,有两个不同构造ContextMap的方式。而ContextMap作为一个继承了HashMap的对象,作用就是用于统一参数的访问方式:用Map接口方法来访问数据。具体来说,当传入的参数对象不是Map类型时,Mybatis会将传入的POJO对象用MetaObject对象来封装,当动态计算sql过程需要获取数据时,用Map接口的get方法包装 MetaObject对象的取值过程。

参数传递和使用过程

 OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());

将传入的参数对象统一封装为ContextMap对象(继承了HashMap对象),然后Ognl运行时环境在动态计算sql语句时,会按照ContextAccessor中描述的Map接口的方式来访问和读取ContextMap对象,获取计算过程中需要的参数。ContextMap对象内部可能封装了一个普通的POJO对象,也可以是直接传递的Map对象,当然从外部是看不出来的,因为都是使用Map的接口来读取数据。

sqlSource

在Mybatis中,每一个select|insert|update|delete标签,都会被解析为一个MappedStatement对象,SqlSource就是MappedStatement对象中一个属性,其最终执行的sql字符串就是由SqlSource提供的。

public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}

sqlSource为一个接口,只有一个方法通过传入执行parameterObject参数返回BoundSql

sqlSource的实现有6中

sqlSource的实现

  • DynamicSqlSource:处理动态sql。
  • RawSqlSource:处理静态sql,其内部装饰StaticSqlSource。
  • StaticSqlSource:处理静态sql,无论是静态sql,还是动态sql,最终的处理结果,都是静态sql。
  • ProviderSqlSource:处理注解Annotation形式的sql。

DynamicSqlSource和StaticSqlSource的最大区别在于:StaticSqlSource的String sql,可以直接获取使用,而DynamicSqlSource的String sql需要逐一根据条件解析并拼接出最终的sql,方能使用。

DynamicSqlSource

DynamicSqlSource中的SqlNode rootSqlNode属性,通常都是MixedSqlNode对象(完全是静态sql时,可能是一个StaticTextSqlNode),而MixedSqlNode对象又保存了所有的List集合,这也是通过一个rootSqlNode,就能找到所有SqlNode的深层原因

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值