转载请注明出处。。。

一、前言

继上一篇mybatis查询语句的背后,这一篇主要围绕着mybatis查询的后期操作,即跟数据库交互的时候。由于本人也是一边学习源码一边记录,内容难免有错误或不足之处,还望诸位指正,本文只可当参考作用。谨记!

二、分析

继上一篇博文的查询例子,mybatis在最后的查询最终会走SimpleExecutor类的doQuery方法,

  @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为routingStatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
// 虽然是执行的routingStatementHandler.query,但返回结果的还是PreparedStatementHandler处理
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
} private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 使用了代理模式,也可以理解为对connection进行了一层包装,这里的作用就是加了log处理
Connection connection = getConnection(statementLog);
//进行预编译,即类似jdbc的 sql,如 select * from user where id=?
stmt = handler.prepare(connection, transaction.getTimeout());
// 对执行查询的sql进行参数设置
handler.parameterize(stmt);
return stmt;
}

关于 handler.prepare的作用这里简单介绍下,不做代码分析。

会设置fetchSize,作用就是一次性从数据库抓取数据,好像默认值是10条,如果每次只抓取一条,则进行rs.next的时候,会再次查库。

如果是insert操作,并且数据库主键自增且还设置了可以返回主键,则会还做获取主键的操作。

先从设置参数说起,也就是handler.parameterize。先看下源码,具体位置在DefaultParameterHandler类里面

 @Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 获取配置文件里面的sql参数信息,如sql为select * from user where id=#{userId,jdbcType=INTEGER}
// ParameterMapping 记录了参数名也就是userId,还有记录了对应的jdbc类型,还有对应的javaType等等,具体可以debug看下
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();
// 如果为true,那么sql参数中有类似 user.name 格式
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 类似一个工具类,它里面有一个反射工厂,可以专门解析一个类的信息,如字段的setter/getter/属性信息,这里不做多余介绍
// 1、下面详细介绍
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);// 取值
}
// 获取对应的typeHandler,一般情况不设置的话,基本都是ObjectTypeHandler
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);
}
}
}
}
}

对于上述代码中的一部分这里负责将parameterObject的里面的值整出来(也就是传入的参数),如果参数是map结构,就从map里面取值,如果不是,如单个非javabean参数,则直接取值,如果是单个javabean,则通过metaObject类转换成一个BeanWrapper,进行取值

这段代码也就负责对预编译后的sql设置参数,这里逻辑主要是围绕以下步骤进行得,

获取参数名,获取参数值,获取参数类型,然后做进行设值操作

  /**
* mybatis数据处理有单结果集和多结果集处理,一般多结果集出现存储过程中,如果存储过程中写了两条select语句,如
* select * from user , select * from classes 这种情况这里不做介绍,因为本人用的不多,理解的也不是很透彻。
* 这里不多做介绍,这里只针对简单映射做一个大概介绍
*
*/
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
// 保存查询结果
final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0;
// 获取第一条数据
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 如果不是多结果集映射,一般resultMaps的大小为1
// resultMap中存储的有类的字段属性,数据库字段名称等信息
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 校验数据的正确性
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集映射
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 处理slect 标签的resultSets属性,多个用逗号隔开,个人几乎没用过,略过
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
} return collapseSingleResultList(multipleResults);
}

以上代码就是为结果映射做一个铺垫,重点是在hanleResultSet方法里,

 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {// 针对简单映射,parentMapping是为Null的
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
// 默认使用defaultResultHandler,如需使用自定义的,则可在传参加入resultHandler接口实现类
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
// 处理结果,结果存在resultHandler里
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
public 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);
}
}
 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
// 跳过多少行,到达指定记录位置,如在传参的时候传入了rowBounds,则会根据该类的offset值跳到指定记录位置
skipRows(resultSet, rowBounds);
// shouldProcessMoreRows 用来检测是否能继续对后续的结果进行映射
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
//用来处理resultMap节点中配置了discriminator节点,这里忽略掉
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
// 得到的结果就是sql执行后的一行记录,如返回User对象信息,则rowValue就代表一个user实例,里面已经有值了
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
//保存数据
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
// 创建对象,可以理解为对resultMap节点的type属性值,进行了反射处理,得到了一个对象,但属性值都是默认值。
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
//是否需要自动映射,有三种映射,分别为None,partial,full,默认第二种,处理非嵌套映射,可通过autoMappingBehavior 配置
if (shouldApplyAutomaticMappings(resultMap, false)) {
// 映射resultMap中未明确指定的列,如类中含有username属性,但是resultMap中没配置,则通过这个进行数据映射,还是可以查询到结果
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
// 处理resultMap中指定的列
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
// 如果没查询到结果,但配置可返回空对象(指的是没有设置属性值得对象),则返回空对象,否则返回null
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}

这里只介绍resultMap中有明确指定的列

 private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// 获取数据字段名
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
// 获取的数据就是resultMap节点中配置的result节点,有多个result节点,这个集合大小就是多少
// 里面存储的是属性名/字段名等信息
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
// 是否有嵌套映射
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
column = null;
}
// 针对1来说一般常与嵌套查询配合使用
// 2 判断属性基本映射
// 3 多结果集的一个处理
if (propertyMapping.isCompositeResult()//
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))//
|| propertyMapping.getResultSet() != null) {// 3
// 获取当前column字段对于的值,有用到typeHandler来进行参数的一个转换
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix); //获取类的属性字段名
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERRED) {// 类似占位符。处理懒加载数据
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// 进行设置属性值
metaObject.setValue(property, value);
}
}
}
return foundValues;
}

或许有人奇怪为啥没看到查询的对象有set操作,值就到了对象里面去了,这里全是metaObject给你操作了,具体的,大家可以自行了解这个类,只能说这个类的功能很强大。

以上就是本文全部内容,

--------------------------------------------------------------------------------------------------------------------------分界线----------------------------------------------------------------------------------------------

mybatis查询语句的背后之封装数据的更多相关文章

  1. mybatis查询语句的背后之参数解析

    转载请注明出处... 一.前言 通过前面我们也知道,通过getMapper方式来进行查询,最后会通过mapperMehod类,对接口中传来的参数也会在这个类里面进行一个解析,随后就传到对应位置,与sq ...

  2. mybatis查询语句的背后

    转载请注明出处... 一.前言 在先了解mybatis查询之前,先大致了解下以下代码的为查询做了哪些铺垫,在这里我们要事先了解,myabtis会默认使用DefaultSqlSessionFactory ...

  3. Hive通过查询语句向表中插入数据注意事项

    最近在学习使用Hive(版本0.13.1)的过程中,发现了一些坑,它们或许是Hive提倡的比关系数据库更加自由的体现(同时引来一些问题),或许是一些bug.总而言之,这些都需要使用Hive的开发人员额 ...

  4. Hive通过查询语句向表中插入数据过程中发现的坑

    前言 近期在学习使用Hive(版本号0.13.1)的过程中,发现了一些坑,它们也许是Hive提倡的比关系数据库更加自由的体现(同一时候引来一些问题).也许是一些bug.总而言之,这些都须要使用Hive ...

  5. hibernate生成查询语句但查不到数据

    hibernate能够生成查询语句 说明它已经进行了查询操作 返回结果数据记录为0  很可能出现的情况 是  :该查询未未访问到指定数据库表. 当使用的数据库为oracle数据库时,你会在bean配置 ...

  6. mybatis 查询语句(按条件查询)

    <select id="getAllDitch" parameterType="xxx.xx.entity.CheckDitch" resultType= ...

  7. mybatis查询语句获取自增主键

    第一种方式: 主键回填useGeneratedKeys 代表采用JDBC的Statment对象的getGeneratedKeys方法返回主键keyProperty 代表将用哪个POJO的属性去匹配这个 ...

  8. mybatis查询如何返回List<Map>类型数据

    只要设定resultType而不设定resultMap就可以了:   <selectid="selectByPage"parameterType="java.uti ...

  9. SQL Server调优系列进阶篇(查询语句运行几个指标值监测)

    前言 上一篇我们分析了查询优化器的工作方式,其中包括:查询优化器的详细运行步骤.筛选条件分析.索引项优化等信息. 本篇我们分析在我们运行的过程中几个关键指标值的检测. 通过这些指标值来分析语句的运行问 ...

随机推荐

  1. django 中自定义过滤器

    多参数过滤器

  2. 第九单元 利用vi编辑器创建和编辑正文文件

    vi编辑器简介  什么是vi vi编辑器的操作模式 vi编辑器的3种基本模式 在vi编辑器中光标的移动 移动光标位置的键与光标移动间的关系 进入插入模式 从命令行模式进入插入模式的命令 在命令行模式下 ...

  3. Nginx的核心功能及应用实战

    反向代理功能及配置: 反向代理(Reverse Proxy)方式是指以代理服务器来接受 internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给interne ...

  4. jquery checkbox勾选/取消勾选只能操作一次的诡异问题

    第一次执行,没问题,但第二次执行就有问题了,选择不了 解决办法:把attr()换成prop() $("#CheckedAll").click(function () { if ($ ...

  5. sass基础—具体编译步骤及对应命令:详细

    /*基础语法*/h1{ color: red;} /*变量定义*/ $color: red; /*嵌套*/body{ header{ } footer{ }} /*mixin函数*/@mixin al ...

  6. BZoj 2301 Problem b(容斥定理+莫比乌斯反演)

    2301: [HAOI2011]Problem b Time Limit: 50 Sec  Memory Limit: 256 MB Submit: 7732  Solved: 3750 [Submi ...

  7. Leetcode刷题第001天

    一.合并两个有序链表 [题目]206. 反转链表 /** * Definition for singly-linked list. * struct ListNode { * int val; * L ...

  8. IPMI无法执行命令

    IPMI无法执行命令 https://www.cnblogs.com/EricDing/p/8995263.html http://www.cnblogs.com/heidsoft/p/4014301 ...

  9. C#学习-类和结构

    类和结构体,对两者进行比较 语法上的区别在于,定义类要使用关键字class,而定义结构体则使用关键字struct; 结构体中不可对声明字段进行初始化,但类可以: 如果没有为类显式地定义构造函数,C#编 ...

  10. 2018项目UML设计-课堂实战

    1. 团队信息 队名:小白吃队 成员: 卢泽明 031602328 蔡文斌 031602301 葛亮 031602617 刘浩 031602423 张扬 031602345 李泓 031602321 ...