文档章节

Mybatis 执行查询的整个流程

learn_more
 learn_more
发布于 2016/02/25 17:05
字数 1354
阅读 754
收藏 10


先来一张图,看看整个流程。。。。。


1、使用Spring-Mybatis , SqlSessionTemplate 进行数据库操作

RowBounds row = new RowBounds(1,10);// 分页参数
List<Map<String, Object>> list = commonRepository.selectList("GradeMapper.getGrade", params,row);


sqlSessionTemplate.selectList(statement, parameter,rowBounds); // 调用代理


2、public class DefaultSqlSession implements SqlSession ,通过代理可以调用默认使用DefaultSqlSession进行数据库操作,真正的操作又是委托给 Executor

@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();
  }
}


3、public class CachingExecutor implements Executor , 查询首先要做的就是判断缓存中是否存在,如果有就直接返回,否则就从数据中查询,这里的缓存是二级缓存

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
  Cache cache = ms.getCache();
  if (cache != null) {
    flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, parameterObject, boundSql);
      List<E> list = (List<E>) tcm.getObject(cache, key);
      if (list == null) {
        list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578 and #116
     }
    return list;
   }
  }
  return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}


4、public abstract class BaseExecutor implements Executor  , 这里的缓存是一级缓存

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
    localCache.removeObject(key);
  }
  localCache.putObject(key, list);
  if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
  }
 return list;
}


6、public class SimpleExecutor extends BaseExecutor 如果缓存中没有数据只能从数据库中获取,数据库获取数据需要如下几步

@Override
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()); // 首先创建Statement,其次赋值Params
    return handler.<E>query(stmt, resultHandler); // 执行数据操作
  } finally {
    closeStatement(stmt); // 关闭数据库连接
  }
}


7、stmt = prepareStatement(handler, ms.getStatementLog());   创建Statement,其次赋值Params

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);// 获取数据库连接
  stmt = handler.prepare(connection); // 创建Statement
  handler.parameterize(stmt); // 赋值Params
  return stmt;
}

1)prepare 设置他的超时以及批量数,以及选择 Statement 的形式

statement = instantiateStatement(connection);
setStatementTimeout(statement);
setFetchSize(statement);
return statement;

2) 选择 Statement 形式,可滚动的结果集,mappedStatement.getResultSetType() 就是我们配置<select resultSetType> 属性

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
  String sql = boundSql.getSql();
  if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { // 设置获取主键属性的值
    String[] keyColumnNames = mappedStatement.getKeyColumns();
    if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
  } else if (mappedStatement.getResultSetType() != null) { // 如果设置了滚动类型
    return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  } else { // 默认构建
    return connection.prepareStatement(sql);
  }
}

3)handler.parameterize(stmt) 设置参数,参数设置的重点在于参数类型的获取,也就是 JdbcType 的作用

@Override
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,一般XML中不需要配置这个参数,系统可以自动匹配
        JdbcType jdbcType = parameterMapping.getJdbcType(); 
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          // 这里如果jdbcType=null,那么获取的就是UnknownTypeHandler
          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);
        }
      }
    }
  }
}


8、public class PreparedStatementHandler extends BaseStatementHandler 执行 Statement execute 

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute(); // 执行sql语句
  return resultSetHandler.<E> handleResultSets(ps); // 处理结果集
}


9、public class DefaultResultSetHandler implements ResultSetHandler  处理结果集

1)判断是否有嵌套的 ResultMap ,这个也是在 <select resultMap>设置的

private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) { // 如果有嵌套
        ensureNoRowBounds();
        checkResultHandler();
        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else { // 如果没有嵌套
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

2)假设没有嵌套的ResultMap ,那么进行结果集遍历,也就是Mybatis的分页

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
  DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
  //首先跳过不需要的记录数
  skipRows(rsw.getResultSet(), rowBounds);
  // 滚动结果集,直到 limit
  while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) { 
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
    //获取行中的数据
    Object rowValue = getRowValue(rsw, discriminatedResultMap);
    // 解析值并存储
    storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()); 
  }
}

3)skipRows(rsw.getResultSet(), rowBounds) 首先跳过不需要的记录数

private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
    if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
      rs.absolute(rowBounds.getOffset());
    }
  } else { 
  // 没有设置滚动类型时默认就是 TYPE_FORWARD_ONLY ,这里就算是这个值,也可以使用 absolute ,为什么不用呢?
    for (int i = 0; i < rowBounds.getOffset(); i++) {
      rs.next();
    }
  }
}

4)shouldProcessMoreRows(resultContext, rowBounds) 是否已经到了Limit

private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException {
    return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
}

5)getRowValue(rsw, discriminatedResultMap) 从结果集的每一行中获取数据,同样这里也需要类型转换,ResultMap 中可以配置 JavaType JdbcType 对获取数据类型是有点作用的。

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
  // 获取类型转换器和以前一样
  if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) { 
    final MetaObject metaObject = configuration.newMetaObject(resultObject);
    boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
    }
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    resultObject = foundValues ? resultObject : null;
    return resultObject;
  }
  return resultObject;
}

6)storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet()) 存储数据

private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
  if (parentMapping != null) {
    linkToParents(rs, parentMapping, rowValue);
  } else {
    callResultHandler(resultHandler, resultContext, rowValue);
  }


© 著作权归作者所有

learn_more
粉丝 93
博文 240
码字总数 210196
作品 0
深圳
程序员
私信 提问
Mybatis一二级缓存实现原理与使用指南

Mybatis 与 Hibernate 一样,支持一二级缓存。一级缓存指的是 Session 级别的缓存,即在一个会话中多次执行同一条 SQL 语句并且参数相同,则后面的查询将不会发送到数据库,直接从 Session ...

丁威
09/23
0
0
Mybatis 执行流程以及其核心类

上图是Mybatis的执行流程,由此我们可以看出Mybatis的核心类有4个,分别是、、、。 :读取配置信息(XML文件),创建SqlSessionFactory,建造者模式,方法级别生命周期; :创建Sqlsession,工...

xiaolyuh
10/15
15
0
自己手写一个Mybatis框架(简化)

继上一篇手写SpringMVC之后,我最近趁热打铁,研究了一下Mybatis。MyBatis框架的核心功能其实不难,无非就是动态代理和jdbc的操作,难的是写出来可扩展,高内聚,低耦合的规范的代码。本文完...

我叫刘半仙
2018/03/07
2.9K
3
mybatis源码分析缓存

关于mybatis的缓存网上有很多相关的文章,本人首先是看过文章,但是本着学习态度和加深理解,以下是个人跟踪源码的记录。 缓存分三种,1)一级缓2)二级缓存(一般指myatis内置的实现缓存方式)...

xuklc
2018/03/05
10
0
mybatis--总结

1.mybatis简介 mybatis是一个基于java的持久层框架,支持普通sql查询/存储过程/对象映射,它使用xml配置或者注解配置来做映射,将普通java对象映射到数据库中。 2.mybatis功能分层 2.1api接口...

求是科技
2016/10/12
46
0

没有更多内容

加载失败,请刷新页面

加载更多

开发中常用的正则表达式

为了能够更好地理解如何在C#环境中使用正则表达式,这里整理了一些常用的正则表达式: 罗马数字: string p1 = "^m*(d?c{0,3}|c[dm])" + "(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$"; string t1 =......

木庄
4分钟前
1
0
【.NET程序打包】VS2019使用Installer Projects打包

C#—使用Installer Projects打包桌面应用程序 前言 打包桌面应用程序实在是一个不常使用的东西,偶尔使用起来经常会忘东忘西的耽误时间,因此,这篇文章多以图片记录过程,也是用于备忘。 下...

_Somuns
8分钟前
2
0
自定义注解,使用动态代理解决网站的字符集编码问题

第1章设置环境 安装操作系统,安装备份(镜像): JDK: 设置环境变量Eclipse:解压即可 Eclipse自身解压目录不包括中文 代码工作空间目录不包括中文Tomcat:解压不要包含中文目录M...

蓝来杯往
13分钟前
2
0
Solr中的字段类型field type

Solr含有多种字段类型,可用的字段类型基本都定义在了包org.apache.solr.schema中,列举如下: 类 说明 BinaryField 二进制数据 BoolField 布尔值,其中’t’/’T’/’1’都是true Collatio...

gantaos
27分钟前
3
0
《JAVA核心知识》学习笔记 (21. JAVA 算法)

21. JAVA 算法

Shingfi
34分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部