前提
在使用数据库ORM
框架的时候,通过一些配置,使得ORM
能够将查出来的数据转为我们想要的Java对象,虽然大致原理是通过Java本身的反射机制获取相应的方法,生成对象、注入值到对象相应的field
,其原理就类似java注解原生模仿@ModelAttribute功能
分析Mybatis如何将数据库查出的数据转为Java对象
在xml中定一个了ResultMap用于映射Java对象与数据库表字段的关系,因此在MyBatis中用ResultMap对象去保存xml中写的resultmap,而Class<?> type
这个属性就是代表查出的数据库数据要转为哪个Java对象
1 2 3 4 5 6
| public class ResultMap { ... private Class<?> type; ... }
|
第一步
这是将数据库数据转为Java对象最终的方法!该方法通过ResultSetWrapper
、ResultMap
创建一个成员属性都为默认值的Java对象,在进行映射时,有两次机会,第一次是判断是否可以自动映射将查出来的数据自动注入到实体中,如果可以,则将数据注入到metaObject
中;第二次是如果没法通过自动映射解决,则通过xml
文件中配置的resultMap
进行数据映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null); if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { final MetaObject metaObject = configuration.newMetaObject(rowValue); boolean foundValues = this.useConstructorMappings; if (shouldApplyAutomaticMappings(resultMap, false)) { foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null; } return rowValue; }
|
第二步
创建出一个对应的Java对象,同时根据懒加载策略进行相应的对象代理;如果存在懒加载策略,则返回的是一个代理对象,用于拦截方法(一般是属性的get
方法),通过方法拦截实现懒加载,具体懒加载的实现可以看这篇:MyBatis延迟加载的简易实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException { this.useConstructorMappings = false; final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>(); final List<Object> constructorArgs = new ArrayList<Object>(); Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix); if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping : propertyMappings) { if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); break; } } } this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); return resultObject; }
|
第三步
其核心实现是通过反射实现,而从传参可知,MyBatis支持在没有无参构造函数的情况下能够通过有参的构造函数去反射实例化一个对象出来,因为Java语言里,一切都是对象,因此类的构造函数也是一个对象——Constructor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException { final Class<?> resultType = resultMap.getType(); final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory); final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings(); if (hasTypeHandlerForResultObject(rsw, resultType)) { return createPrimitiveResultObject(rsw, resultMap, columnPrefix); } else if (!constructorMappings.isEmpty()) { return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix); } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) { return objectFactory.create(resultType); } else if (shouldApplyAutomaticMappings(resultMap, false)) { return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix); } throw new ExecutorException("Do not know how to create an instance of " + resultType); }
|
第四步
通过前面的几个步骤获得了一个初始化的对象,然后需要把数据库查出来的数据注入到对象中去
applyAutomaticMappings
applyAutomaticMappings
函数是将查出来的数据注入到Java对象中的field
中的一种方法,它的策略是Java对象的属性名能够与数据库字段名称自动化映射过去。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; if (!autoMapping.isEmpty()) { for (UnMappedColumnAutoMapping mapping : autoMapping) { final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); if (value != null) { foundValues = true; } if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) { metaObject.setValue(mapping.property, value); } } } return foundValues; }
|
applyPropertyMappings
mybatis最重要的特性就是通过xml配置Java对象与数据库表实现字段映射的关系,并且无论自动映射成功与否,applyPropertyMappings
都会执行,通过xml中定义的映射关系将查处的数据注入到Java对象中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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; final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping : propertyMappings) { String column = prependPrefix(propertyMapping.getColumn(), columnPrefix); if (propertyMapping.getNestedResultMapId() != null) { column = null; } if (propertyMapping.isCompositeResult() || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) || propertyMapping.getResultSet() != null) { 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; }
|