Skip to content

Commit

Permalink
Merge pull request #7 from FuriousPws002/06-result-set-handler-with-r…
Browse files Browse the repository at this point in the history
…esult-map

06 result set handler with result map
  • Loading branch information
FuriousPws002 authored Apr 15, 2024
2 parents 8218983 + 9551364 commit 25f7af7
Show file tree
Hide file tree
Showing 14 changed files with 475 additions and 9 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
[2.解析xml中静态sql的mapper](https://github.com/FuriousPws002/mini-mybatis/wiki/2.%E8%A7%A3%E6%9E%90xml%E4%B8%AD%E9%9D%99%E6%80%81sql%E7%9A%84mapper "Markdown") <br>
[3.执行静态sql](https://github.com/FuriousPws002/mini-mybatis/wiki/3.%E6%89%A7%E8%A1%8C%E9%9D%99%E6%80%81sql "Markdown") <br>
[4.参数绑定](https://github.com/FuriousPws002/mini-mybatis/wiki/4.%E5%8F%82%E6%95%B0%E7%BB%91%E5%AE%9A "Markdown") <br>
[5.resultType结果集处理](https://github.com/FuriousPws002/mini-mybatis/wiki/5.resultType%E7%BB%93%E6%9E%9C%E9%9B%86%E5%A4%84%E7%90%86 "Markdown") <br>
[5.resultType结果集处理](https://github.com/FuriousPws002/mini-mybatis/wiki/5.resultType%E7%BB%93%E6%9E%9C%E9%9B%86%E5%A4%84%E7%90%86 "Markdown") <br>
[6.resultMap结果集处理](https://github.com/FuriousPws002/mini-mybatis/wiki/6.resultMap%E7%BB%93%E6%9E%9C%E9%9B%86%E5%A4%84%E7%90%86 "Markdown") <br>
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package org.apache.ibatis.builder;


import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;

/**
* @author furious 2024/4/7
Expand Down Expand Up @@ -43,11 +48,45 @@ public void addMappedStatement(String id, SqlCommandType sqlCommandType, SqlSour
configuration.addMappedStatement(statement);
}

public void addMappedStatement(String id, SqlCommandType sqlCommandType, SqlSource sqlSource, Class<?> resultTypeClass, String resultMap) {
id = currentNamespace + "." + id;
MappedStatement statement = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.resultMap(getStatementResultMap(resultTypeClass, resultMap))
.build();
configuration.addMappedStatement(statement);
}


public ResultMapping buildResultMapping(Class<?> resultType, String property, String column, String nestedResultMap) throws Exception {
Field field = FieldUtils.getDeclaredField(resultType, property, true);
Class<?> propertyType = field.getType();
final TypeHandler<?> typeHandler = configuration.getTypeHandlerRegistry().getTypeHandler(propertyType);
ResultMapping resultMapping = new ResultMapping(configuration, property, column, typeHandler);
resultMapping.setNestedResultMapId(nestedResultMap);
return resultMapping;
}

public ResultMap addResultMap(String id, Class<?> type, List<ResultMapping> resultMappings) {
ResultMap resultMap = new ResultMap(configuration, type);
resultMap.setId(currentNamespace + "." + id);
resultMap.setResultMappings(resultMappings);
configuration.addResultMap(resultMap);
return resultMap;
}

private ResultMap getStatementResultMap(Class<?> resultType) {
if (Objects.isNull(resultType)) {
return getStatementResultMap(resultType, null);
}

private ResultMap getStatementResultMap(Class<?> resultType, String resultMap) {
if (Objects.isNull(resultType) && Objects.isNull(resultMap)) {
return null;
}
return new ResultMap(configuration, resultType);
if (Objects.nonNull(resultType)) {
return new ResultMap(configuration, resultType);
}
return configuration.getResultMap(currentNamespace + "." + resultMap);
}

}
45 changes: 45 additions & 0 deletions src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package org.apache.ibatis.builder.xml;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
Expand Down Expand Up @@ -43,6 +47,7 @@ private void configurationElement(XNode context) {
if (Objects.isNull(namespace) || namespace.isEmpty()) {
throw new BuilderException("mapper namespace can not be empty");
}
resultMapElements(context.evalNodes("resultMap"));
buildStatementFromContext(context.evalNodes("insert|select"));
} catch (Exception e) {
throw new BuilderException("parse mapper xml error", e);
Expand All @@ -55,4 +60,44 @@ private void buildStatementFromContext(List<XNode> list) {
statementParser.parseStatementNode();
}
}

private void resultMapElements(List<XNode> list) {
for (XNode resultMapNode : list) {
try {
resultMapElement(resultMapNode);
} catch (Exception ignored) {
}
}
}

private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode, Collections.emptyList(), null);
}

private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception {
String type = resultMapNode.getAttribute("type", resultMapNode.getAttribute("ofType"));
Class<?> typeClass = resolveClass(type);
List<ResultMapping> resultMappings = new ArrayList<>(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass));
}
String id = resultMapNode.getAttribute("id", resultMapNode.id());
return builderAssistant.addResultMap(id, typeClass, resultMappings);
}

private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType) throws Exception {
String property = context.getAttribute("property");
String column = context.getAttribute("column");
String nestedResultMap = processNestedResultMappings(context, Collections.emptyList(), resultType);
return builderAssistant.buildResultMapping(resultType, property, column, nestedResultMap);
}

private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings, Class<?> enclosingType) throws Exception {
if (Objects.equals(context.getName(), "collection")) {
ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);
return resultMap.getId();
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public void parseStatementNode() {
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
SqlSource sqlSource = configuration.getLanguageDriver().createSqlSource(configuration, context, null);
String resultType = context.getAttribute("resultType");
String resultMap = context.getAttribute("resultMap");
Class<?> resultTypeClass = resolveClass(resultType);
builderAssistant.addMappedStatement(id, sqlCommandType, sqlSource,resultTypeClass);
builderAssistant.addMappedStatement(id, sqlCommandType, sqlSource, resultTypeClass, resultMap);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package org.apache.ibatis.executor.resultset;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
Expand Down Expand Up @@ -54,9 +63,80 @@ private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Obj
}

private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults) throws SQLException {
handleRowValuesForSimpleResultMap(rsw, resultMap, multipleResults);
if (resultMap.hasNestedResultMaps()) {
handleRowValuesForNestedResultMap(rsw, resultMap, multipleResults);
} else {
handleRowValuesForSimpleResultMap(rsw, resultMap, multipleResults);
}
}

private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults) throws SQLException {
ResultSet resultSet = rsw.getResultSet();
// nested resultmaps
Map<String, Object> nestedResultObjects = new HashMap<>();
while (!resultSet.isClosed() && resultSet.next()) {
String rowKey = createRowKey(rsw, resultMap);
Object partialObject = nestedResultObjects.get(rowKey);
Object rowValue = getRowValue(rsw, resultMap, rowKey, partialObject, nestedResultObjects);
if (Objects.isNull(partialObject)) {
multipleResults.add(rowValue);
}
}
}

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String combinedKey, Object partialObject, Map<String, Object> nestedResultObjects) throws SQLException {
Object rowValue = partialObject;
if (Objects.nonNull(rowValue)) {
applyNestedResultMappings(rsw, resultMap, rowValue, nestedResultObjects, combinedKey);
} else {
rowValue = createResultObject(resultMap);
applyAutomaticMappings(rsw, resultMap, rowValue);
applyNestedResultMappings(rsw, resultMap, rowValue, nestedResultObjects, combinedKey);
nestedResultObjects.put(combinedKey, rowValue);
}

return rowValue;
}

private void applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, Object parentRowValue, Map<String, Object> nestedResultObjects, String parentRowKey) {
if (Objects.isNull(resultMap.getResultMappings())) {
return;
}
for (ResultMapping resultMapping : resultMap.getResultMappings()) {
final String nestedResultMapId = resultMapping.getNestedResultMapId();
if (StringUtils.isEmpty(nestedResultMapId)) {
continue;
}
try {
ResultMap nestedResultMap = configuration.getResultMap(nestedResultMapId);
String rowKey = createRowKey(rsw, nestedResultMap);
String combinedKey = rowKey + parentRowKey;
Object rowValue = nestedResultObjects.get(combinedKey);
boolean knownValue = rowValue != null;
rowValue = getRowValue(rsw, nestedResultMap, combinedKey, rowValue, nestedResultObjects);
if (rowValue != null && !knownValue) {
linkObjects(parentRowValue, resultMapping, rowValue);
}
} catch (SQLException e) {
throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'. Cause: " + e, e);
}
}
}

private void linkObjects(Object parentRowValue, ResultMapping resultMapping, Object rowValue) {
try {
List old = (List<?>) FieldUtils.readField(parentRowValue, resultMapping.getProperty(), true);
Field field = FieldUtils.getDeclaredField(parentRowValue.getClass(), resultMapping.getProperty(), true);
Object list = field.getType().newInstance();
Method add = List.class.getDeclaredMethod("add", Object.class);
add.invoke(list, rowValue);
old.addAll(((List) list));
} catch (Exception e) {
ExceptionUtils.rethrow(e);
}
}


private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults) throws SQLException {
ResultSet resultSet = rsw.getResultSet();
while (!resultSet.isClosed() && resultSet.next()) {
Expand All @@ -65,14 +145,19 @@ private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap r
}
}

private String createRowKey(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
Object rowValue = getRowValue(rsw, resultMap);
return rsw.hashCode() + ToStringBuilder.reflectionToString(rowValue, ToStringStyle.NO_CLASS_NAME_STYLE);
}

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
Object rowValue = createResultObject(resultMap);
applyAutomaticMappings(rsw, rowValue);
applyAutomaticMappings(rsw, resultMap, rowValue);
return rowValue;
}

private boolean applyAutomaticMappings(ResultSetWrapper rsw, Object rowValue) throws SQLException {
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, rowValue);
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, Object rowValue) throws SQLException {
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, rowValue);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
Expand All @@ -90,8 +175,20 @@ private boolean applyAutomaticMappings(ResultSetWrapper rsw, Object rowValue) th
return foundValues;
}

private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, Object rowValue) {
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, Object rowValue) {
List<UnMappedColumnAutoMapping> autoMapping = new ArrayList<>();
//若存在resultMapping,使用resultMapping
if (Objects.nonNull(resultMap.getResultMappings()) && !resultMap.getResultMappings().isEmpty()) {
return resultMap.getResultMappings().stream()
.filter(m -> StringUtils.isEmpty(m.getNestedResultMapId()))
.map(m -> {
UnMappedColumnAutoMapping auto = new UnMappedColumnAutoMapping(m.getColumn(), m.getProperty(), m.getTypeHandler());
// if(Objects.isNull(auto.typeHandler)){
//
// }
return auto;
}).collect(Collectors.toList());
}
List<String> columnLabels = rsw.getColumnLabels();
for (String columnLabel : columnLabels) {
Field field = FieldUtils.getDeclaredField(rowValue.getClass(), columnLabel, true);
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/org/apache/ibatis/mapping/ResultMap.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.apache.ibatis.mapping;

import java.util.List;
import java.util.Objects;

import org.apache.ibatis.session.Configuration;

/**
Expand All @@ -13,6 +16,13 @@ public class ResultMap {
*/
private Class<?> type;

private String id;
private List<ResultMapping> resultMappings;
/**
* 是否包含嵌套resultMap
*/
private boolean hasNestedResultMaps;

public ResultMap() {
}

Expand All @@ -36,4 +46,25 @@ public Class<?> getType() {
public void setType(Class<?> type) {
this.type = type;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public List<ResultMapping> getResultMappings() {
return resultMappings;
}

public void setResultMappings(List<ResultMapping> resultMappings) {
this.resultMappings = resultMappings;
this.hasNestedResultMaps = resultMappings.stream().anyMatch(m -> Objects.nonNull(m.getNestedResultMapId()));
}

public boolean hasNestedResultMaps() {
return hasNestedResultMaps;
}
}
Loading

0 comments on commit 25f7af7

Please sign in to comment.