上节分析了Mapper对象的创建。

在ORM的定义中可以理解为Object->SQLMapper抽象层(这一层并不负责具体的SQL执行。这一层可以理解为SQL代理层)

本节分析以下内容:

①SqlSession在具体执行SQL时,如果通过namespace+sqlid定位到具体的MappedStatement(sql的对象化表现形式)

②参数(Object) 如何填充到具体的SQL

③SQL是如何执行的

  • 获取StateMentMapper.前面讲到初始化时,会缓存MappedStatement,MappedStatement被保存在StrictMap中.

    StrictMap是Mybatis实现HashMap子类。Key重复放入的时候会报错。

  

  • 参数(Object) 如何填充到具体的SQL(Param-SQL的Orm转换)

  1、通过Executor执行SQL

  @Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

 2、Executor是如何获取的?

  SqlSessionFactory工厂中代码如下:

   从Configuration中获得Excecutor,默认的执行器类型为configuration.getDefaultExecutorType()在configuration类中定义为

  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

  ExecutorType.SIMPLE会创建何种执行器?

  来看Configuraiton的获得执行器的方法

  默认的执行器为:SimpleExecutor,而cacheEnabled默认值为true.所以实际是CachingExecutor,使用了装饰器模式。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
  •   Excecutor是如何将参数成功映射到具体SQL的参数?

   先看下MappedStatement中的类成员构成。sqlSource是具体获取待执行SQL的对象。

      

  •   sqlSource接口定义:
    BoundSql getBoundSql(Object parameterObject)的方法,改方调用实际的SqlSource的实现类,来获取真正执行的SQL

    

  先说说几个处理类的区别:

  DynamicSqlSource:sql中包含<where><if><choose>等条件是,会被定义为.通过具体的Node处理对象,拼接SQL。

  看一段代码,我们关注rootSqlNode变量,以及getBoundSql()方法的执行。

  

public class DynamicSqlSource implements SqlSource {

  private final Configuration configuration;
private final SqlNode rootSqlNode; public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
} @Override
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;
}

  解析过程描述:

  ①rootSqlNode为从XML解析的具体SQL节点,每一行作为一个node。对于<if><choose>等。每一个节点是一个node

  ②<if></if>的判断是在具体的node中执行的。SQLNode有以下几种类型。

  

  看一段IfSqlNode的代码:apply方法中的evaluateBoolean方法,将对<if>语句进行判断,返回结果。如果结果为真,则把条件添加到contents 

/**
* @author Clinton Begin
*/
public class IfSqlNode implements SqlNode {
private final ExpressionEvaluator evaluator;
private final String test;
private final SqlNode contents; public IfSqlNode(SqlNode contents, String test) {
this.test = test;
this.contents = contents;
this.evaluator = new ExpressionEvaluator();
} @Override
public boolean apply(DynamicContext context) {
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
} }

  mybatis中定义的SQL节点如下。

  

  RawSqlSource:对于不包含<if>等条件判断,替换#{}变为? 在创建RawSqlSource对象时执行这项操作

  DynamicSqlSource: 对sql中包含${}参数的会转换为该对象

  •   SqlSource的转换是在初始化加载时完成。那真正的参数是何时转换为SQL?

  SimpleExecutor中,创建PrepareStateMent的过程。

  

  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt
= prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
   //创建、初始化PreparedStatement
stmt = handler.prepare(connection, transaction.getTimeout());
  //设置参数
handler.parameterize(stmt);
return stmt;
}

  离我们想知道的真相越来越近了,来看具体的参数化过程

  StateMentHandler.parameterize

  RoutingStatementHandler用来做路由器:根据实际的StatementType做路由

  我们来看PreparedStatementHadler

    public void parameterize(Statement statement) throws SQLException {
this.parameterHandler.setParameters((PreparedStatement)statement);
}

  

                    
public void setParameters(PreparedStatement ps) {       
  ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());     
  //获得所有的参数        
List
<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
        
  
if (parameterMappings != null) {
            
    for(int i = 0; i < parameterMappings.size(); ++i) {
               
      ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                
      if (parameterMapping.getMode() != ParameterMode.OUT) {

        String propertyName = parameterMapping.getProperty();
Object value;
if (this.boundSql.hasAdditionalParameter(propertyName)) {
value = this.boundSql.getAdditionalParameter(propertyName);
} else if (this.parameterObject == null) {
value = null;
            //如果有typeHandler则用TypeHandler处理参数
            //一些基础类型是有typeHandler的
} else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
value = this.parameterObject;
} else {
            //如果没有typeHandler,通过反射,获得Bean参数中的值
MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
value = metaObject.getValue(propertyName);
}
            //根据参数的JDBCtype找到TypeHandler,设置到PrePareStatement中
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = this.configuration.getJdbcTypeForNull();
} try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException var10) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
} catch (SQLException var11) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var11, var11);
}
}
}
}
}

  TypeHandler的继承关系如下:

  

   

  我们查看其中的BigDecimalTypeHandler的源码

  

public class BigDecimalTypeHandler extends BaseTypeHandler<BigDecimal> {

  @Override
public void setNonNullParameter(PreparedStatement ps, int i, BigDecimal parameter, JdbcType jdbcType)
throws SQLException {
ps.setBigDecimal(i, parameter);
} @Override
public BigDecimal getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getBigDecimal(columnName);
} @Override
public BigDecimal getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return rs.getBigDecimal(columnIndex);
} @Override
public BigDecimal getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return cs.getBigDecimal(columnIndex);
}
}

  会调用JDBCAPI中的相应方法获得正确的值

  

mybatis关于ORM的使用以及设计(三)[参数对象转换为SQL语言]的更多相关文章

  1. mybatis关于ORM的使用以及设计(二)[DaoInterface 转换 Mapper代理对象]

    第一节中,分析了Mybatis的ORM框架的初始化,这篇来分析SQL执行过程中,对象->SQL是如何转换的 其中包含两种映射思想 ①DAO接口->Mapper实例 ②执行DAO的方法时,参 ...

  2. mybatis关于ORM的使用以及设计(一)[ORM的初始化]

    ORM WIKI中的解释.画重点 Object-relational mapping (ORM, O/RM, and O/R mapping tool) in computer science is ...

  3. c# 轻量级 ORM 框架 之 DBHelper 实现 (三)

    周末了比较清闲,把自己的orm框架整理了下,开源了. 已经做出来的东西通常感觉有些简单,一些新手或许听到"框架"一类的词觉得有些"高深",简单来说orm就是把a ...

  4. [simple-orm-mybaits]基于Mybatis的ORM封装介绍

    目录 前言 ORM框架现状 Mybatis优缺点 simple-orm-mybatis设计思路介绍 simple-orm-mybatis使用说明 simple-orm-mybatis实际使用 推荐最佳 ...

  5. ORM表之间高级设计

    ORM表之间高级设计 一.表的继承 # db_test1 # 一.基表 # Model类的内部配置Meta类要设置abstract=True, # 这样的Model类就是用来作为基表 # 多表:Boo ...

  6. JdbcTemplate 、Mybatis、ORM 、Druid 、HikariCP 、Hibernate是什么?它们有什么关系?

    JdbcTemplate .Mybatis.ORM .Druid .HikariCP .Hibernate是什么?它们有什么关系? 学完Spring和SpringMVC之后,就急于求成的开始学习起Sp ...

  7. JavaScript框架设计(三) push兼容性和选择器上下文

    JavaScript框架设计(三) push兼容性和选择器上下文 博主很久没有更博了. 在上一篇 JavaScript框架设计(二) 中实现了最基本的选择器,getId,getTag和getClass ...

  8. MyBatis学习(三)、动态SQL语句

    三.动态SQL语句 有些时候,sql语句where条件中,需要一些安全判断,例如按某一条件查询时如果传入的参数是空,此时查询出的结果很可能是空的,也许我们需要参数为空时,是查出全部的信息.使用Orac ...

  9. 游戏UI框架设计(三) : 窗体的层级管理

    游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反 ...

随机推荐

  1. C#action和func的使用

    以前我都是通过定义一个delegate来写委托的,但是最近看一些外国人写的源码都是用action和func方式来写,当时感觉对这很陌生所以看起源码也觉得陌生,所以我就花费时间来学习下这两种方式,然后发 ...

  2. Gravatar 头像使用

    Gravatar :如果在Gravatar的服务器上放置了你自己的头像,那么在任何支持Gravatar的blog或者留言本上留言时,只要提供你与这个头像关联的email地址,就能够显示出你的Grava ...

  3. 西门子SCL读写DB数据

    数据块间接寻址方式,仅供参考. STATUS_1:= DB11.DW[COUNTER]; //字节间接寻址STATUS_2:= DB12.DX[WNO, BITNO]; //位间接寻址,用户改变WNO ...

  4. [LeetCode&Python] Problem 101. Symmetric Tree

    Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For e ...

  5. socket 简单了解

    网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.   建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API),对TCP/IP的 ...

  6. ROS使用小知识点

    输入 rosrun rqt_graph rqt_graph 可以打开一个界面观察节点与话题的关系 绿色和蓝色的是节点 红色的是话题 查看ros中额的tf转换信息 rosrun rqt_tf_tree ...

  7. java native 笔记

    java中native的用法   前言: 在查看 Object.java 文件时,发现有一个 方法比较特殊(不认得,看不懂...)  private static native void regist ...

  8. 图解HTTP阅读笔记(1)-网络基础TCP/IP

    1.TCP/IP协议族 TCP/IP这个概念对大家来说很熟悉,之前我的了解它只是一个协议.今天阅读才知道TCP/IP实际上是一个协议族,其中HTTP协议属于该协议族的一个子集.图1是TCP/IP协议族 ...

  9. java语言入门

    Java语言的介绍: Java是一种可以撰写跨平台应用程序的面向对象的程序设计语言. 它最初被命名为Oak,目标设定在家用电器等小型系统的编程语言,来解决诸如电视机.电话.闹钟.烤面包机等家用电器的控 ...

  10. JPA、Hibernate框架、通用mapper

    JPA是描述对象-关系表的映射关系,将运行期实体对象持久化到数据库中,提出以面向对象方式操作数据库的思想. Hibernate框架核心思想是ORM-实现自动的关系映射.缺点:由于关联操作提出Hql语法 ...