这篇文章主要看下mybatis是如何进行结果映射的。
以前提到过,addMapper的过程当中,将结果映射的配置保存在了ResultMap中。下面来看下这个类的属性吧。java
private Configuration configuration;//全局配置 private String id;//惟一标识 private Class<?> type;//实体类 private List<ResultMapping> resultMappings;//嵌套查询的相关配置 private List<ResultMapping> idResultMappings; private List<ResultMapping> propertyResultMappings; private Set<String> mappedColumns;//嵌套查询的参数字段 private Set<String> mappedProperties;//嵌套查询的属性名 private boolean hasNestedResultMaps;//是否含有嵌套的ResultMap private boolean hasNestedQueries;//是否含有嵌套的子查询
本篇文章,仍是基于那个最简单的查询来分析。所以,讲不到嵌套查询。所以ResultMap中就主要用到id,type这两个属性。如下是mapper.ProductMapper的一个抽象方法:mybatis
@Select("select * from product where id = #{id}") Product detail(long id);
这个方法对应的ResultMap的id为"mapper.ProductMapper.detail-long"。type为Product.class。app
ResultMap的构建是在addMapper的过程当中。代码入口在MapperAnnotationBuilder的parseResultMap方法中。ide
private String parseResultMap(Method method) { Class<?> returnType = getReturnType(method);//经过反射获取方法返回类型 ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);//获取ConstructorArgs注解 Results results = method.getAnnotation(Results.class);//获取Results注解,嵌套查询主要是分析这个注解 TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class); String resultMapId = generateResultMapName(method);//拼接resultMap的id applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);//构建ResultMap return resultMapId; }
能够看到这个过程主要是经过反射来获取的各个字段。咱们上面的例子比较简单,没有注解,因此,只有id和type字段有值。正如前面讲的addMapper以后,这个ResultMap便存在了configuration中的resultMaps了。ui
继续上一篇文章,上一篇文章,讲到查询除告终果,剩下DefaultResultSetHandler将结果按照配置映射好。一下为DefaultResultSetHandler的handleResultSet方法。this
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException { try { if (parentMapping != null) {//为嵌套查询时 handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping); } else { if (resultHandler == null) {//没有指定resultHandler则使用默认的 DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory); 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()); } }
rsw是将jdbc的返回结果包裹了一层,方便处理。resultMap即是这个查询对应的ResultMap,multipleResults则存放最终结果。parentMapping只有在嵌套查询时才会有值。
能够看到咱们的案例parentMapping为null,resultHandle也为null。 能够看到映射数据走的是handleRowValues方法。
而handleRowValues中又判断了是否含有嵌套查询,当没有嵌套查询时执行handleRowValuesForSimpleResultMap方法。code
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);//逻辑分页,去掉多余行 while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {//遍历数据行 ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);//处理Discriminate注解,本文不考虑 Object rowValue = getRowValue(rsw, discriminatedResultMap);//生成映射对象 storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());//将映射对象保存在resultHandler中。 } }
生成映射对象分红了两步,第一步构建映射对象,第二步是为字段赋值。构建映射对象主要用的objectFactory,这个对象保存在configuration中。默认类是DefaultObjectFactory。对象
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { try { Constructor<T> constructor; if (constructorArgTypes == null || constructorArgs == null) {//当没有传入构造器参数时 constructor = type.getDeclaredConstructor();//获取无参构造器 if (!constructor.isAccessible()) {//保证无参构造器可以访问 constructor.setAccessible(true); } return constructor.newInstance();//构建对象并返回 } constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));//获取参数类型对应的构造器 if (!constructor.isAccessible()) { constructor.setAccessible(true); } return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));//传入参数构建对象并返回 } catch (Exception e) {//错误处理,省略 } }
DefaultObjectFactory类主要就是经过这个方法构造出对象的。代码就主要是经过反射。
如今生成告终果对象了,但每一个字段都是空的,getRowValue中会调用applyAutomaticMappingst方法将查询结果赋值到各个字段中。递归
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);//将每一个字段值和对应的属性生成UnMappedColumnAutoMapping对象,并存到list中。 boolean foundValues = false; if (!autoMapping.isEmpty()) { for (UnMappedColumnAutoMapping mapping : autoMapping) {//遍历每一个UnMappedColumnAutoMapping final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);//获取值 if (value != null) { foundValues = true; } if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) { // gcode issue #377, call setter on nulls (value is not 'found') metaObject.setValue(mapping.property, value);//将对应的属性填充上值 } } } return foundValues; }
createAutomaticMappings的过程比较繁琐,主要就是遍历结果,将jdbc的字段和对象的属性名对应起来。就不具体看了。 最后看看 storeObject是如何将结果保存在resultHandler中的吧。storeObject本质是调用的ResultHandler的handleResult方法。接下来看看DefaultResultHandler类吧。ip
public class DefaultResultHandler implements ResultHandler<Object> { private final List<Object> list;//保存最终结果 public DefaultResultHandler() { list = new ArrayList<Object>(); } @SuppressWarnings("unchecked") public DefaultResultHandler(ObjectFactory objectFactory) { list = objectFactory.create(List.class); } @Override public void handleResult(ResultContext<? extends Object> context) { list.add(context.getResultObject());//将上下文中正在处理的对象添加到list中 } public List<Object> getResultList() { return list; } }
能够看到默认的ResultHandler很是简单。就是一个list,用add方法将每一个结果添加进来。每一个结果是经过context.getResultObject()获得的。接下来看下DefaultResultContext类。
public class DefaultResultContext<T> implements ResultContext<T> { private T resultObject;//最后一个保存的数据 private int resultCount;//保存的结果个数 private boolean stopped;//是否中止 public DefaultResultContext() { resultObject = null; resultCount = 0; stopped = false; } @Override public T getResultObject() { return resultObject; } @Override public int getResultCount() { return resultCount; } @Override public boolean isStopped() { return stopped; } public void nextResultObject(T resultObject) { resultCount++; this.resultObject = resultObject; } @Override public void stop() { this.stopped = true; } }
能够看到DefaultResultContext很是简单,只是保存了下单个数据,以及处理过的结果个数。
只考虑这种简单查询的话,生成映射结果的流程并不复杂,最核心的两步就是经过反射生成结果对象、填充各个字段的值。
若考虑嵌套查询的话,能够看到ResultMap中保存了子查询的信息在resultMappings等字段中,能够在里面取出子查询的配置,而后执行子查询的方法,子查询的执行过程其实和父查询都是用以前分析的execute.query方法。有一点相似于递归。
若考虑延迟加载的话,mybatis的延迟加载主要使用:Javassist,Cglib实现