MyBatis对JDBC做了很好的封装,其中一个吸引人的地方就是能够对从数据库内查询出来的表的记录集映射生成一系列JavaBean,供应用程序使用。今天跟着源码一层一层探讨一下MyBatis把数据库记录集映射到POJO对象的一个简要的过程。
1. DefaultResultSetHandler类
处理结果集的主要实现类,先从这个方法看
- private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
- try {
- if(parentMapping != null) {
- this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping);
- } else if(this.resultHandler == null) {
- DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory);
- this.<span style="color:#ff0000;">handleRowValues</span>(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null);
- multipleResults.add(defaultResultHandler.getResultList());
- } else {
- this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null);
- }
- } finally {
- this.closeResultSet(rsw.getResultSet());
- }
- }
不管方法handleRowValues里面调用的层次多深,最终把结果集ResultSet经过处理,得到了需要的那些POJO对象并存储到一个List里面。
2. 来看一下方法handleRowValues方法:
- public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
- if(resultMap.hasNestedResultMaps()) {
- this.ensureNoRowBounds();
- this.checkResultHandler();
- this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
- } else {
- this.<span style="color:#ff0000;">handleRowValuesForSimpleResultMap</span>(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<Object>();
- skipRows(rsw.getResultSet(), rowBounds);
- while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
- ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
- Object rowValue =<span style="color:#ff0000;"> getRowValue</span>(rsw, discriminatedResultMap);
- storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
- }
- }
- private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
- if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
- if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
- rs.absolute(rowBounds.getOffset());
- }
- } else {
- for (int i = 0; i < rowBounds.getOffset(); i++) {
- rs.next();
- }
- }
- }
- private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
- final ResultLoaderMap lazyLoader = new ResultLoaderMap();
- Object resultObject = <span style="color:#ff0000;">createResultObject</span>(rsw, resultMap, lazyLoader, null);
- if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
- final MetaObject <span style="color:#ff0000;">metaObject</span> = configuration.newMetaObject(resultObject);
- boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
- if (shouldApplyAutomaticMappings(resultMap, false)) {
- foundValues = <span style="color:#ff0000;">applyAutomaticMappings</span>(rsw, resultMap, metaObject, null) || foundValues;
- }
- foundValues = <span style="color:#ff0000;">applyPropertyMappings</span>(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
- foundValues = lazyLoader.size() > 0 || foundValues;
- resultObject = foundValues ? resultObject : null;
- return resultObject;
- }
- return resultObject;
- }
3. 注意上述方法的第三行,进一步打开createResultObject方法看看:
- private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
- final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
- final List<Object> constructorArgs = new ArrayList<Object>();
- final Object resultObject = <span style="color:#ff0000;">createResultObject</span>(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
- if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
- final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
- for (ResultMapping propertyMapping : propertyMappings) {
- // issue gcode #109 && issue #149
- if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
- return <span style="color:#ff0000;">configuration.getProxyFactory().createProxy</span>(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
- }
- }
- }
- return resultObject;
- }
- 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 <span style="color:#ff0000;">createParameterizedResultObject</span>(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);
- }
- Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType, List<ResultMapping> constructorMappings,
- List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) {
- boolean foundValues = false;
- for (ResultMapping constructorMapping : constructorMappings) {
- final Class<?> parameterType = constructorMapping.getJavaType();
- final String column = constructorMapping.getColumn();
- final Object value;
- try {
- if (constructorMapping.getNestedQueryId() != null) {
- value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
- } else if (constructorMapping.getNestedResultMapId() != null) {
- final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
- value = getRowValue(rsw, resultMap);
- } else {
- final TypeHandler<?> typeHandler = constructorMapping.getTypeHandler();
- value = typeHandler.getResult(rsw.getResultSet(), prependPrefix(column, columnPrefix));
- }
- } catch (ResultMapException e) {
- throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
- } catch (SQLException e) {
- throw new ExecutorException("Could not process result for mapping: " + constructorMapping, e);
- }
- constructorArgTypes.add(parameterType);
- constructorArgs.add(value);
- foundValues = value != null || foundValues;
- }
- return foundValues ? objectFactory.create(resultType, constructorArgTypes, constructorArgs) : null;
- }
- private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
- List<UnMappedColumnAutoMapping> autoMapping = <span style="color:#ff0000;">createAutomaticMappings</span>(rsw, resultMap, metaObject, columnPrefix);
- boolean foundValues = false;
- if (autoMapping.size() > 0) {
- for (UnMappedColumnAutoMapping mapping : autoMapping) {
- final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
- // issue #377, call setter on nulls
- if (value != null || configuration.isCallSettersOnNulls()) {
- if (value != null || !mapping.primitive) {
- metaObject.setValue(mapping.property, value);
- }
- foundValues = true;
- }
- }
- }
- return foundValues;
- }
- 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) {
- // the user added a column attribute to a nested result map, ignore it
- 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);
- // issue #541 make property optional
- final String property = propertyMapping.getProperty();
- // issue #377, call setter on nulls
- if (value != DEFERED
- && property != null
- && (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive()))) {
- metaObject.setValue(property, value);
- }
- if (property != null && (value != null || value == DEFERED)) {
- foundValues = true;
- }
- }
- }
- return foundValues;
- }
- private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
- final String mapKey = resultMap.getId() + ":" + columnPrefix;
- List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
- if (autoMapping == null) {
- autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
- final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
- for (String columnName : unmappedColumnNames) {
- String propertyName = columnName;
- if (columnPrefix != null && !columnPrefix.isEmpty()) {
- // When columnPrefix is specified,
- // ignore columns without the prefix.
- if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
- propertyName = columnName.substring(columnPrefix.length());
- } else {
- continue;
- }
- }
- final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
- if (property != null && metaObject.hasSetter(property)) {
- final Class<?> propertyType = metaObject.getSetterType(property);
- if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
- final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
- autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
- } else {
- configuration.getAutoMappingUnknownColumnBehavior()
- .doAction(mappedStatement, columnName, property, propertyType);
- }
- } else{
- configuration.getAutoMappingUnknownColumnBehavior()
- .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
- }
- }
- autoMappingsCache.put(mapKey, autoMapping);
- }
- return autoMapping;
- }
首先把resultMap中取得的列名转换为大写字母,再截取它的前缀(去除特殊字符),把这个前缀和要映射到的对象的属性进行比对,符合的就映射过去,即对POJO对象注入对应属性值。这里应该不受到字母大小写的影响。
当然,鉴于本人的水平是在有限,做的这个解释还非常模糊,读者有兴趣可以自行研究一下源码。