33、SQL解析基础

来源:互联网 发布:淘宝网主要特色 编辑:程序博客网 时间:2024/06/07 20:48

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的深层原因

原创粉丝点击