热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

mybatis查询语句揭秘之封装数据

这篇文章主要给大家介绍了关于mybatis查询语句揭秘之封装数据的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用mybatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

一、前言

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

二、分析

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

@Override
 public  List 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 parameterMappings = boundSql.getParameterMappings();
 if (parameterMappings != null) {
 for (int i = 0; i 

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

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

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

/**
 * mybatis数据处理有单结果集和多结果集处理,一般多结果集出现存储过程中,如果存储过程中写了两条select语句,如
 * select * from user , select * from classes 这种情况这里不做介绍,因为本人用的不多,理解的也不是很透彻。
 * 这里不多做介绍,这里只针对简单映射做一个大概介绍
 *
 */
public List handleResultSets(Statement stmt) throws SQLException {
 ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
 // 保存查询结果
 final List multipleResults = new ArrayList<>();

 int resultSetCount = 0;
 // 获取第一条数据
 ResultSetWrapper rsw = getFirstResultSet(stmt);
 // 如果不是多结果集映射,一般resultMaps的大小为1
 // resultMap中存储的有类的字段属性,数据库字段名称等信息
 List 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 

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

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List 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<&#63;> 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<&#63;> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
 throws SQLException {
 DefaultResultContext 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() &#63; rowValue : null;
 }
 return rowValue;
 }

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

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
 throws SQLException {
 // 获取数据字段名
 final List mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
 boolean foundValues = false;
 // 获取的数据就是resultMap节点中配置的result节点,有多个result节点,这个集合大小就是多少
 // 里面存储的是属性名/字段名等信息
 final List 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()// 1
  || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))// 2
  || 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给你操作了,具体的,大家可以自行了解这个类,只能说这个类的功能很强大。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。


推荐阅读
  • PHP 编程疑难解析与知识点汇总
    本文详细解答了 PHP 编程中的常见问题,并提供了丰富的代码示例和解决方案,帮助开发者更好地理解和应用 PHP 知识。 ... [详细]
  • 本文详细介绍了如何使用libpq库与PostgreSQL后端建立连接。通过探讨PQconnectdb()函数的工作原理及其在实际应用中的使用方法,帮助读者理解并掌握建立高效、稳定的数据库连接的关键步骤。 ... [详细]
  • 本文详细介绍了HTML中标签的使用方法和作用。通过具体示例,解释了如何利用标签为网页中的缩写和简称提供完整解释,并探讨了其在提高可读性和搜索引擎优化方面的优势。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 使用C#开发SQL Server存储过程的指南
    本文介绍如何利用C#在SQL Server中创建存储过程,涵盖背景、步骤和应用场景,旨在帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 本文探讨了适用于Spring Boot应用程序的Web版SQL管理工具,这些工具不仅支持H2数据库,还能够处理MySQL和Oracle等主流数据库的表结构修改。 ... [详细]
  • 本文详细介绍了如何通过多种编程语言(如PHP、JSP)实现网站与MySQL数据库的连接,包括创建数据库、表的基本操作,以及数据的读取和写入方法。 ... [详细]
  • 在当前众多持久层框架中,MyBatis(前身为iBatis)凭借其轻量级、易用性和对SQL的直接支持,成为许多开发者的首选。本文将详细探讨MyBatis的核心概念、设计理念及其优势。 ... [详细]
  • 在使用 DataGridView 时,如果在当前单元格中输入内容但光标未移开,点击保存按钮后,输入的内容可能无法保存。只有当光标离开单元格后,才能成功保存数据。本文将探讨如何通过调用 DataGridView 的内置方法解决此问题。 ... [详细]
  • 本文详细介绍了如何在 Linux 平台上安装和配置 PostgreSQL 数据库。通过访问官方资源并遵循特定的操作步骤,用户可以在不同发行版(如 Ubuntu 和 Red Hat)上顺利完成 PostgreSQL 的安装。 ... [详细]
  • 如何在PostgreSQL中查看数据表
    本文将指导您使用pgAdmin工具连接到PostgreSQL数据库,并展示如何浏览和查找其中的数据表。通过简单的步骤,您可以轻松访问所需的表结构和数据。 ... [详细]
  • 利用存储过程构建年度日历表的详细指南
    本文将介绍如何使用SQL存储过程创建一个完整的年度日历表。通过实例演示,帮助读者掌握存储过程的应用技巧,并提供详细的代码解析和执行步骤。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
  • 在使用SQL Server进行动态SQL查询时,如果遇到LIKE语句无法正确返回预期结果的情况,通常是因为参数传递方式不当。本文将详细探讨这一问题,并提供解决方案及相关的技术背景。 ... [详细]
author-avatar
PearlLisa_Shanghai_901
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有