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

网友投稿 373 2023-01-09


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 < 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 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 < 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 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 DefaultResultruWxFiQXHandler(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 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 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给你操作了,具体的,大家可以自行了解这个类,只能说这个类的功能很强大。

总结

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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:自动化接口测试框架有哪些(接口自动化和接口测试的区别)
下一篇:微服务网关需求怎么写(微服务网关的主要功能)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~