MyBatisMyBatis 源码分析——类型处理器

官网点说到:无论是 MyBatis
在先行处理语句(PreparedStatement)中装置一个参数时,还是打结果集中取出一个价值时,
都见面因此色处理器将取的价为适量的办法换成 Java
类型。那么为什么会发型处理器为?这或多或少并不难理解,SQL语句其实可以了解呢同派系面向数据库的编程语言。所以相对而言都产生投机之数据类型。这也就算意味着在数据类型不一至的题材。同时不同之数据库里数据列还有一定的差义。类型处理器则就用来拍卖数据类型不一至之题材。

笔者看了几单例外的ORM框架都是正在路处理器的定义。可见类型处理器在ORM框架上落实有多么重要。在官网点已排有了20多栽的中间类型处理器。笔者建议极好选择性的关押。好比如说笔者当前之列子里面所以到之一个让UnknownTypeHandler的路处理器。

List<Product> products = dao.SelectProducts(30, "a");

<select id="SelectProducts" resultMap="result" >
        select * from Products where #{0} > ProductID and ProductName like #{1}
</select>

事例里连从未指出是什么法的JAVA类型。所以当是UnknownTypeHandler类型了。即然这样子我们不烦设置一下客的类型在来探望。我们如果把地方的布修改一下即便可了。如下红色标志。

<select id="SelectProducts" resultMap="result" >
        select * from Products where #{0,javaType=int,jdbcType=NUMERIC} > ProductID and ProductName like #{1,javaType=String,jdbcType=VARCHAR}
</select>

关于#{}的语法问题,相信笔者不用多张嘴大家还晓得。从者简单的装里,我们可以看源码里面会找到IntegerTypeHandler和StringTypeHandler类型处理器来设置参数。类型处理器都是贯彻于TypeHandler接口,源码都存放于org.apache.ibatis.type的命名空间下。TypeHandler接口的源码也非常简短。我们可以看出setParameter方法。顾名思义他虽用来安装参数。其他的章程还是用来拍卖回返的结果。如下。

public interface TypeHandler<T> {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

顾念使了解源码,就使物色一个相当的入切类——笔者选取IntegerTypeHandler类。IntegerTypeHandler类并不曾一直的贯彻了TypeHandler接口。而是继续了BaseTypeHandler类。正使上面所出口的,BaseTypeHandler类的setParameter方法我们得询问一下。如下

 1 public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
 2     if (parameter == null) {
 3       if (jdbcType == null) {
 4         throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
 5       }
 6       try {
 7         ps.setNull(i, jdbcType.TYPE_CODE);
 8       } catch (SQLException e) {
 9         throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
10                 "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
11                 "Cause: " + e, e);
12       }
13     } else {
14       try {
15         setNonNullParameter(ps, i, parameter, jdbcType);
16       } catch (Exception e) {
17         throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
18                 "Try setting a different JdbcType for this parameter or a different configuration property. " +
19                 "Cause: " + e, e);
20       }
21     }
22   }

源码的意思非常简单——把参数分为非空值和空值来分别安装。空值的安方式是JDBC内部协调的。而休空值的道是mybatis自己包裹的。所以我们得以看到setNonNullParameter方法是一个泛方法。看样子我们得猜之出来——真正的实现是放在子类的setNonNullParameter方法里面。

 public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

来拘禁一下IntegerTypeHandler类的setNonNullParameter方法。

  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setInt(i, parameter);
  }

其实笔者认为IntegerTypeHandler类没有什么可看的。但是UnknownTypeHandler类的拍卖也得我们错过研究。我们可以先想转手——不清楚路的景况下,如何知道用的型处理器。

 public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
      throws SQLException {
    TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
    handler.setParameter(ps, i, parameter, jdbcType);
  }

不难看出他同时因参数值和JDBC数据类型来判定得到对应的品类处理器。然后在展开参数设值。所以我们的靶子变向resolveTypeHandler方法。

 1   private TypeHandler<? extends Object> resolveTypeHandler(Object parameter, JdbcType jdbcType) {
 2     TypeHandler<? extends Object> handler;
 3     if (parameter == null) {
 4       handler = OBJECT_TYPE_HANDLER;
 5     } else {
 6       handler = typeHandlerRegistry.getTypeHandler(parameter.getClass(), jdbcType);
 7       // check if handler is null (issue #270)
 8       if (handler == null || handler instanceof UnknownTypeHandler) {
 9         handler = OBJECT_TYPE_HANDLER;
10       }
11     }
12     return handler;
13   }

源码的意思是判断传入的参数值是无是为空的,空则调用ObjectTypeHandler类型处器。如果无呢空,根据参数传入的型及时JDBC数据类型来获得对应的档次处理器。如果要找不至或还是UnknownTypeHandler类,则调用ObjectTypeHandler类型处器。UnknownTypeHandler类实际并未设置参数值的效应。而是由于其他的类型处理器来贯彻。

哼了,到了这边当让路处理器,相信大家还发出了肯定之知道。但是这里还有一个题目值得我们去研究一下——类型处理器是呀时创建的。想使询问他的来源于就必须回头去看一下DefaultParameterHandler类的setParameters方法。也就是是达标平等回的结尾处相同的代码。

 public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }

BoundSql类及一章笔者也起言到。他是用于存放sql语句信息。为什么这里要提到他也?相信于源码里面我们好看看类型处理器是由此ParameterMapping类来获得的。而ParameterMapping类又是经过 BoundSql类获得。所以即使不得不去了解一下BoundSql类是什么样收获ParameterMapping类的。

有关什么BoundSql类是怎么样获得ParameterMapping类的。事实就等同片及XMLMapperBuilder类有涉嫌。XMLMapperBuilder类就是用来加载Mapper文件的。而他的configurationElement方法。正是我们现亟待了解之。如下

 private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

说到底当之buildStatementFromContext方法是摸底哪些收获ParameterMapping类的始发。这句代码中已经颇明显的视他是用来加载select,insert等节点信息。当我们累与进去吧,最终会盼一个了parseStatementNode方法。这一个过程读者们自行查看。

1  processSelectKeyNodes(id, parameterTypeClass, langDriver);
2     
3     // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
4     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
5     String resultSets = context.getStringAttribute("resultSets");
6     String keyProperty = context.getStringAttribute("keyProperty");
7     String keyColumn = context.getStringAttribute("keyColumn");

笔者只是贴出一部分之代码。红色标志的始末是咱们所假设关心的显要。(其外parseStatementNode方法还是了解什么创造MappedStatement类的入口类。)笔者为什么要提到SqlSource接口呢?我们还了解当mybatis框架间的sql语句并非容许只有sql语句。还会现出if之类的语法。那么这种sql语句,mybatis框架叫他呢动态
SQL。而BoundSql类即是经过SqlSource接口所累的类似获得。

mybatis对sql语句定义为老三种植半。为什么是三种半啊?因为还有一个恐怕在研发中。每一样种植于源码中都发生相关的类。

1.DynamicSqlSource类用于动态SQL。

2.StaticSqlSource类用于静态SQL。

3.ProviderSqlSource类用于脚本语言。

4.VelocitySqlSource类用于XXX。还是读者们融洽失去看吧。

SqlSource接口的源码才是作者为什么而讲话SqlSource接口的尽好证明。MyBatis如下

public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}

我们打出懂了BoundSql类是哪里来了随后。我们于去探听如何取得ParameterMapping类就更换得简单多矣。这里笔者就不把整个底进程将出去说了。就径直将获得ParameterMapping类的始点提出来——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);
    String sql = parser.parse(originalSql);
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }

ParameterMappingTokenHandler类就是用来解析ParameterMapping类的。具体什么去分析不用笔者于着手吧。在盖新ParameterMapping类的下,他的里resolveTypeHandler方法会被调用。而以此时候我们的型处理器正式面世了。

 private void resolveTypeHandler() {
      if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
        Configuration configuration = parameterMapping.configuration;
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
      }
    }

路处理器相关的始末。到了此可以说凡是收了。我们清楚了外的来源于,也理解了他的去处。事实使我们认真点去打听刚刚只过程。你们会意识这历程用于深buidler设计模式。同时mybatis框架把mapper文件的音信分为mapper根处理、四节点处理、脚本处理、SQL语句处理。

1.Mapper根处理相应之XMLMapperBuilder类。

2.四节点拍卖相应的XMLStatementBuilder类。即是select节点,update节点,insert节点,delect节点。

3.脚本处理相应之XMLScriptBuilder类。

4.SQL告知句子处理相应的SqlSourceBuilder类。

本这吗是作者自己的意见而以。不肯定对。你们可自己失去询问。

 

网站地图xml地图