From 78214c11f1411547c4980a3a005d8e1635256e6f Mon Sep 17 00:00:00 2001
From: Suomm <1474983351@qq.com>
Date: Wed, 7 Jun 2023 15:03:24 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=87=AA=E5=8A=A8?=
=?UTF-8?q?=E6=98=A0=E5=B0=84=E5=AE=9E=E4=BD=93=E7=B1=BB=E4=B8=AD=E7=9A=84?=
=?UTF-8?q?=20association=20=E5=92=8C=20collection=20=E7=B1=BB=E5=9E=8B?=
=?UTF-8?q?=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../core/mybatis/FlexConfiguration.java | 81 ++++++---
.../com/mybatisflex/core/table/TableInfo.java | 172 ++++++++++++++++--
.../core/table/TableInfoFactory.java | 16 +-
3 files changed, 225 insertions(+), 44 deletions(-)
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java
index 99ad428b..5c700fa0 100644
--- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/mybatis/FlexConfiguration.java
@@ -1,17 +1,17 @@
-/**
- * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+/*
+ * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.mybatisflex.core.mybatis;
@@ -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,14 +177,40 @@ 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 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())
.fetchSize(ms.getFetchSize())
@@ -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 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
*
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfo.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfo.java
index 28c1db8f..13730c7a 100644
--- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfo.java
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfo.java
@@ -1,17 +1,17 @@
-/**
- * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+/*
+ * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.mybatisflex.core.table;
@@ -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 onUpdateListeners;
private List onSetListeners;
+ /**
+ * @deprecated 该功能有更好的方式实现,此属性可能会被移除。
+ */
+ @Deprecated
private Map> joinTypes;
+ /**
+ * 对应 MapperXML 配置文件中 {@code } 标签下的 {@code } 标签。
+ */
+ private Map> associationType;
+
+ /**
+ * 对应 MapperXML 配置文件中 {@code } 标签下的 {@code } 标签。
+ */
+ private Map> collectionType;
+
+
private final ReflectorFactory reflectorFactory = new BaseReflectorFactory() {
@Override
public Reflector findForClass(Class> type) {
@@ -119,11 +135,11 @@ public class TableInfo {
return tableName;
}
- public String getWrapSchemaAndTableName(IDialect dialect){
- if (StringUtil.isNotBlank(schema)){
- return dialect.wrap(dialect.getRealSchema(schema)) +"." + dialect.wrap(dialect.getRealTable(tableName));
- }else {
- return dialect.wrap(dialect.getRealTable(tableName));
+ public String getWrapSchemaAndTableName(IDialect dialect) {
+ if (StringUtil.isNotBlank(schema)) {
+ return dialect.wrap(dialect.getRealSchema(schema)) + "." + dialect.wrap(dialect.getRealTable(tableName));
+ } else {
+ return dialect.wrap(dialect.getRealTable(tableName));
}
}
@@ -296,6 +312,36 @@ public class TableInfo {
joinTypes.put(fieldName, clazz);
}
+ public Map> getAssociationType() {
+ return associationType;
+ }
+
+ public void setAssociationType(Map> associationType) {
+ this.associationType = associationType;
+ }
+
+ public void addAssociationType(String fieldName, Class> clazz) {
+ if (associationType == null) {
+ associationType = new HashMap<>();
+ }
+ associationType.put(fieldName, clazz);
+ }
+
+ public Map> getCollectionType() {
+ return collectionType;
+ }
+
+ public void setCollectionType(Map> collectionType) {
+ this.collectionType = collectionType;
+ }
+
+ public void addCollectionType(Field field, Class> genericClass) {
+ if (collectionType == null) {
+ collectionType = new HashMap<>();
+ }
+ collectionType.put(field, genericClass);
+ }
+
void setColumnInfoList(List 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 resultMappings = new ArrayList<>();
@@ -727,6 +777,90 @@ public class TableInfo {
return new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
}
+ public List buildResultMapList(Configuration configuration) {
+ String resultMapId = entityClass.getName();
+ List resultMaps = new ArrayList<>();
+ List resultMappings = new ArrayList<>();
+
+ // 标签下的 标签映射
+ 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);
+ }
+ }
+
+ // 标签下的 标签映射
+ 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);
+ }
+
+ // 标签下的 标签映射
+ if (associationType != null) {
+ associationType.forEach((fieldName, fieldType) -> {
+ // 获取嵌套类型的信息,也就是 javaType 属性
+ TableInfo tableInfo = TableInfoFactory.ofEntityClass(fieldType);
+ // 构建嵌套类型的 ResultMap 对象,也就是 标签下的内容
+ // 这里是递归调用,直到嵌套类型里面没有其他嵌套类型或者集合类型为止
+ List resultMapList = tableInfo.buildResultMapList(configuration);
+ // 寻找是否有嵌套 ResultMap 引用
+ Optional 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);
+ });
+ }
+
+ // 标签下的 标签映射
+ if (collectionType != null) {
+ collectionType.forEach((field, genericClass) -> {
+ // 获取集合泛型类型的信息,也就是 ofType 属性
+ TableInfo tableInfo = TableInfoFactory.ofEntityClass(genericClass);
+ // 构建嵌套类型的 ResultMap 对象,也就是 标签下的内容
+ // 这里是递归调用,直到集合类型里面没有其他嵌套类型或者集合类型为止
+ List resultMapList = tableInfo.buildResultMapList(configuration);
+ // 寻找是否有嵌套 ResultMap 引用
+ Optional 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 resultMappings, String name) {
for (ResultMapping resultMapping : resultMappings) {
diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfoFactory.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfoFactory.java
index 61e1ebc5..4c1ab6aa 100644
--- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfoFactory.java
+++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/table/TableInfoFactory.java
@@ -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;
}