mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 17:18:24 +08:00
feat: 支持自动映射实体类中的 association 和 collection 类型。
This commit is contained in:
parent
9b08c9a7c5
commit
78214c11f1
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -27,7 +27,6 @@ import com.mybatisflex.core.row.RowMapper;
|
||||
import com.mybatisflex.core.table.EntityWrapperFactory;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.table.TableInfoFactory;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import org.apache.ibatis.executor.CachingExecutor;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
@ -36,15 +35,15 @@ import org.apache.ibatis.executor.keygen.NoKeyGenerator;
|
||||
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.ResultMap;
|
||||
import org.apache.ibatis.mapping.*;
|
||||
import org.apache.ibatis.session.*;
|
||||
import org.apache.ibatis.transaction.Transaction;
|
||||
import org.apache.ibatis.util.MapUtil;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -178,13 +177,39 @@ public class FlexConfiguration extends Configuration {
|
||||
|
||||
String resultMapId = tableInfo.getEntityClass().getName();
|
||||
|
||||
ResultMap resultMap;
|
||||
/*ResultMap resultMap;
|
||||
if (hasResultMap(resultMapId)) {
|
||||
resultMap = getResultMap(resultMapId);
|
||||
} else {
|
||||
resultMap = tableInfo.buildResultMap(this);
|
||||
this.addResultMap(resultMap);
|
||||
}*/
|
||||
|
||||
// 变量名与属性名区分开
|
||||
List<ResultMap> resultMapList;
|
||||
if (hasResultMap(resultMapId)) {
|
||||
resultMapList = new ArrayList<>();
|
||||
fillResultMapList(resultMapId, resultMapList);
|
||||
} else {
|
||||
resultMapList = tableInfo.buildResultMapList(this);
|
||||
for (ResultMap resultMap : resultMapList) {
|
||||
if (!hasResultMap(resultMap.getId())) {
|
||||
addResultMap(resultMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 这里解释一下为什么要反转这个集合:
|
||||
*
|
||||
* MyBatis 在解析 ResultMaps 的时候,是按照顺序一个一个进行解析的,对于有嵌套
|
||||
* 的 ResultMap 对象,也就是 nestResultMap 需要放在靠前的位置,首先解析。
|
||||
*
|
||||
* 而我们进行递归 buildResultMapList 也好,fillResultMapList 也好,都是
|
||||
* 非嵌套 ResultMap 在集合最开始的位置,所以要反转一下集合,将 hasNestedResultMaps
|
||||
* 的 ResultMap 对象放到集合的最前面。
|
||||
*/
|
||||
Collections.reverse(resultMapList);
|
||||
|
||||
return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
|
||||
.resource(ms.getResource())
|
||||
@ -198,7 +223,8 @@ public class FlexConfiguration extends Configuration {
|
||||
.lang(ms.getLang())
|
||||
.resultOrdered(ms.isResultOrdered())
|
||||
.resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
|
||||
.resultMaps(CollectionUtil.newArrayList(resultMap)) // 替换resultMap
|
||||
//.resultMaps(CollectionUtil.newArrayList(resultMap)) // 替换resultMap
|
||||
.resultMaps(resultMapList)
|
||||
.resultSetType(ms.getResultSetType())
|
||||
.flushCacheRequired(ms.isFlushCacheRequired())
|
||||
.useCache(ms.isUseCache())
|
||||
@ -206,6 +232,19 @@ public class FlexConfiguration extends Configuration {
|
||||
.build();
|
||||
}
|
||||
|
||||
private void fillResultMapList(String resultMapId, List<ResultMap> resultMapList) {
|
||||
ResultMap resultMap = this.getResultMap(resultMapId);
|
||||
resultMapList.add(resultMap);
|
||||
if (resultMap.hasNestedResultMaps()) {
|
||||
for (ResultMapping resultMapping : resultMap.getResultMappings()) {
|
||||
String nestedResultMapId = resultMapping.getNestedResultMapId();
|
||||
if (nestedResultMapId != null) {
|
||||
fillResultMapList(nestedResultMapId, resultMapList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新的、已替换主键生成器的 MappedStatement
|
||||
*
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -39,6 +39,7 @@ import org.apache.ibatis.session.Configuration;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.apache.ibatis.util.MapUtil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@ -96,9 +97,24 @@ public class TableInfo {
|
||||
private List<UpdateListener> onUpdateListeners;
|
||||
private List<SetListener> onSetListeners;
|
||||
|
||||
/**
|
||||
* @deprecated 该功能有更好的方式实现,此属性可能会被移除。
|
||||
*/
|
||||
@Deprecated
|
||||
private Map<String, Class<?>> joinTypes;
|
||||
|
||||
|
||||
/**
|
||||
* 对应 MapperXML 配置文件中 {@code <resultMap>} 标签下的 {@code <association>} 标签。
|
||||
*/
|
||||
private Map<String, Class<?>> associationType;
|
||||
|
||||
/**
|
||||
* 对应 MapperXML 配置文件中 {@code <resultMap>} 标签下的 {@code <collection>} 标签。
|
||||
*/
|
||||
private Map<Field, Class<?>> collectionType;
|
||||
|
||||
|
||||
private final ReflectorFactory reflectorFactory = new BaseReflectorFactory() {
|
||||
@Override
|
||||
public Reflector findForClass(Class<?> type) {
|
||||
@ -296,6 +312,36 @@ public class TableInfo {
|
||||
joinTypes.put(fieldName, clazz);
|
||||
}
|
||||
|
||||
public Map<String, Class<?>> getAssociationType() {
|
||||
return associationType;
|
||||
}
|
||||
|
||||
public void setAssociationType(Map<String, Class<?>> associationType) {
|
||||
this.associationType = associationType;
|
||||
}
|
||||
|
||||
public void addAssociationType(String fieldName, Class<?> clazz) {
|
||||
if (associationType == null) {
|
||||
associationType = new HashMap<>();
|
||||
}
|
||||
associationType.put(fieldName, clazz);
|
||||
}
|
||||
|
||||
public Map<Field, Class<?>> getCollectionType() {
|
||||
return collectionType;
|
||||
}
|
||||
|
||||
public void setCollectionType(Map<Field, Class<?>> collectionType) {
|
||||
this.collectionType = collectionType;
|
||||
}
|
||||
|
||||
public void addCollectionType(Field field, Class<?> genericClass) {
|
||||
if (collectionType == null) {
|
||||
collectionType = new HashMap<>();
|
||||
}
|
||||
collectionType.put(field, genericClass);
|
||||
}
|
||||
|
||||
void setColumnInfoList(List<ColumnInfo> columnInfoList) {
|
||||
this.columnInfoList = columnInfoList;
|
||||
this.columns = new String[columnInfoList.size()];
|
||||
@ -660,6 +706,10 @@ public class TableInfo {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 该功能有更好的方式实现,此方法可能会被移除。
|
||||
*/
|
||||
@Deprecated
|
||||
public ResultMap buildResultMap(Configuration configuration) {
|
||||
String resultMapId = entityClass.getName();
|
||||
List<ResultMapping> resultMappings = new ArrayList<>();
|
||||
@ -727,6 +777,90 @@ public class TableInfo {
|
||||
return new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
|
||||
}
|
||||
|
||||
public List<ResultMap> buildResultMapList(Configuration configuration) {
|
||||
String resultMapId = entityClass.getName();
|
||||
List<ResultMap> resultMaps = new ArrayList<>();
|
||||
List<ResultMapping> resultMappings = new ArrayList<>();
|
||||
|
||||
// <resultMap> 标签下的 <result> 标签映射
|
||||
for (ColumnInfo columnInfo : columnInfoList) {
|
||||
ResultMapping mapping = new ResultMapping.Builder(configuration, columnInfo.property,
|
||||
columnInfo.column, columnInfo.propertyType)
|
||||
.jdbcType(columnInfo.getJdbcType())
|
||||
.typeHandler(columnInfo.buildTypeHandler())
|
||||
.build();
|
||||
resultMappings.add(mapping);
|
||||
|
||||
//add property mapper for sql: select xxx as property ...
|
||||
if (!Objects.equals(columnInfo.getColumn(), columnInfo.getProperty())) {
|
||||
ResultMapping propertyMapping = new ResultMapping.Builder(configuration, columnInfo.property,
|
||||
columnInfo.property, columnInfo.propertyType)
|
||||
.jdbcType(columnInfo.getJdbcType())
|
||||
.typeHandler(columnInfo.buildTypeHandler())
|
||||
.build();
|
||||
resultMappings.add(propertyMapping);
|
||||
}
|
||||
}
|
||||
|
||||
// <resultMap> 标签下的 <id> 标签映射
|
||||
for (IdInfo idInfo : primaryKeyList) {
|
||||
ResultMapping mapping = new ResultMapping.Builder(configuration, idInfo.property,
|
||||
idInfo.column, idInfo.propertyType)
|
||||
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
|
||||
.jdbcType(idInfo.getJdbcType())
|
||||
.typeHandler(idInfo.buildTypeHandler())
|
||||
.build();
|
||||
resultMappings.add(mapping);
|
||||
}
|
||||
|
||||
// <resultMap> 标签下的 <association> 标签映射
|
||||
if (associationType != null) {
|
||||
associationType.forEach((fieldName, fieldType) -> {
|
||||
// 获取嵌套类型的信息,也就是 javaType 属性
|
||||
TableInfo tableInfo = TableInfoFactory.ofEntityClass(fieldType);
|
||||
// 构建嵌套类型的 ResultMap 对象,也就是 <association> 标签下的内容
|
||||
// 这里是递归调用,直到嵌套类型里面没有其他嵌套类型或者集合类型为止
|
||||
List<ResultMap> resultMapList = tableInfo.buildResultMapList(configuration);
|
||||
// 寻找是否有嵌套 ResultMap 引用
|
||||
Optional<ResultMap> nestedResultMap = resultMapList.stream()
|
||||
.filter(e -> fieldType.getName().equals(e.getId()))
|
||||
.findFirst();
|
||||
// 处理嵌套类型 ResultMapping 引用
|
||||
nestedResultMap.ifPresent(resultMap -> resultMappings.add(new ResultMapping.Builder(configuration, fieldName)
|
||||
.javaType(fieldType)
|
||||
.nestedResultMapId(resultMap.getId())
|
||||
.build()));
|
||||
// 全部添加到 ResultMap 集合当中
|
||||
resultMaps.addAll(resultMapList);
|
||||
});
|
||||
}
|
||||
|
||||
// <resultMap> 标签下的 <collection> 标签映射
|
||||
if (collectionType != null) {
|
||||
collectionType.forEach((field, genericClass) -> {
|
||||
// 获取集合泛型类型的信息,也就是 ofType 属性
|
||||
TableInfo tableInfo = TableInfoFactory.ofEntityClass(genericClass);
|
||||
// 构建嵌套类型的 ResultMap 对象,也就是 <collection> 标签下的内容
|
||||
// 这里是递归调用,直到集合类型里面没有其他嵌套类型或者集合类型为止
|
||||
List<ResultMap> resultMapList = tableInfo.buildResultMapList(configuration);
|
||||
// 寻找是否有嵌套 ResultMap 引用
|
||||
Optional<ResultMap> nestedResultMap = resultMapList.stream()
|
||||
.filter(e -> genericClass.getName().equals(e.getId()))
|
||||
.findFirst();
|
||||
// 处理嵌套类型 ResultMapping 引用
|
||||
nestedResultMap.ifPresent(resultMap -> resultMappings.add(new ResultMapping.Builder(configuration, field.getName())
|
||||
.javaType(field.getType())
|
||||
.nestedResultMapId(resultMap.getId())
|
||||
.build()));
|
||||
// 全部添加到 ResultMap 集合当中
|
||||
resultMaps.addAll(resultMapList);
|
||||
});
|
||||
}
|
||||
|
||||
resultMaps.add(new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build());
|
||||
|
||||
return resultMaps;
|
||||
}
|
||||
|
||||
private static boolean existColumn(List<ResultMapping> resultMappings, String name) {
|
||||
for (ResultMapping resultMapping : resultMappings) {
|
||||
|
||||
@ -212,11 +212,19 @@ public class TableInfoFactory {
|
||||
&& !fieldType.isEnum() // 类型不是枚举
|
||||
&& !defaultSupportColumnTypes.contains(fieldType) //默认的自动类型不包含该类型
|
||||
) {
|
||||
if (!Map.class.isAssignableFrom(fieldType)
|
||||
&& !Collection.class.isAssignableFrom(fieldType)
|
||||
&& !fieldType.isArray()) {
|
||||
tableInfo.addJoinType(field.getName(), fieldType);
|
||||
// 集合嵌套
|
||||
if (Collection.class.isAssignableFrom(fieldType)) {
|
||||
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
|
||||
Type actualTypeArgument = genericType.getActualTypeArguments()[0];
|
||||
tableInfo.addCollectionType(field, (Class<?>) actualTypeArgument);
|
||||
}
|
||||
// 实体类嵌套
|
||||
else if (!Map.class.isAssignableFrom(fieldType)
|
||||
&& !fieldType.isArray()) {
|
||||
// tableInfo.addJoinType(field.getName(), fieldType);
|
||||
tableInfo.addAssociationType(field.getName(), fieldType);
|
||||
}
|
||||
// 不支持的类型直接跳过
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user