一, 前言
继上一篇 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, 那么参数中有类似 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 / 属性信息, 这里不做多余介绍
- // 这里负责将 parameterObject 的里面的值整出来 (也就是传入的参数), 实际上代码执行到了这, parameterObject 就是一个 map 结构
- 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);
- }
- }
- }
- }
- }
这段代码也就负责对预编译后的 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()// 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 给你操作了, 具体的, 大家可以自行了解这个类, 只能说这个类的功能很强大.
以上就是本文全部内容,
-------------------------------------------------------------------------------------------------------------------------- 分界线 ----------------------------------------------------------------------------------------------
来源: https://www.cnblogs.com/qm-article/p/10588627.html