perf: optimize FieldQueryManager.queryFields

This commit is contained in:
开源海哥 2023-07-16 11:23:37 +08:00
parent 75a1df800e
commit a1aae02118
7 changed files with 72 additions and 210 deletions

View File

@ -15,6 +15,9 @@
*/
package com.mybatisflex.core.field;
import com.mybatisflex.core.util.FieldWrapper;
import sun.jvm.hotspot.oops.FieldType;
import java.io.Serializable;
/**
@ -25,7 +28,7 @@ public class FieldQuery implements Serializable {
private String className;
private String fieldName;
private FieldType fieldType = FieldType.AUTO;
private FieldWrapper fieldWrapper;
private boolean prevent;
private QueryBuilder queryBuilder;
@ -45,12 +48,12 @@ public class FieldQuery implements Serializable {
this.fieldName = fieldName;
}
public FieldType getFieldType() {
return fieldType;
public FieldWrapper getFieldWrapper() {
return fieldWrapper;
}
public void setFieldType(FieldType fieldType) {
this.fieldType = fieldType;
public void setFieldWrapper(FieldWrapper fieldWrapper) {
this.fieldWrapper = fieldWrapper;
}
public boolean isPrevent() {
@ -79,17 +82,6 @@ public class FieldQuery implements Serializable {
this.fieldQuery.setFieldName(fieldName);
}
/**
* 设置属性类型可选默认自动识别
*
* @param fieldType 属性类型
* @return 构建者
*/
public Builder<T> fieldType(FieldType fieldType) {
this.fieldQuery.setFieldType(fieldType);
return this;
}
/**
* 阻止对嵌套类属性的查询只对 {@link FieldType#COLLECTION}
* {@link FieldType#ENTITY} 两种属性类型有效
@ -124,7 +116,8 @@ public class FieldQuery implements Serializable {
return this;
}
protected FieldQuery build() {
protected FieldQuery build(Class<?> entityClass) {
this.fieldQuery.setFieldWrapper(FieldWrapper.of(entityClass, fieldQuery.fieldName));
return this.fieldQuery;
}

View File

@ -67,8 +67,8 @@ public class FieldQueryBuilder<T> implements Serializable {
return (FieldQuery.Builder<R>) builder;
}
public FieldQuery build() {
return builder.build();
public FieldQuery build(Class<?> entityClass) {
return builder.build(entityClass);
}
}

View File

@ -22,13 +22,8 @@ import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.core.util.CollectionUtil;
import com.mybatisflex.core.util.FieldWrapper;
import org.apache.ibatis.reflection.TypeParameterResolver;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
/**
@ -44,87 +39,68 @@ public class FieldQueryManager {
public static void queryFields(BaseMapper<?> mapper, Collection<?> entities, Map<String, FieldQuery> fieldQueryMap) {
for (Object entity : entities) {
Class<?> entityClass = entity.getClass();
List<Field> allFields = ClassUtil.getAllFields(entityClass);
for (Field field : allFields) {
String mapKey = entityClass.getName() + '#' + field.getName();
FieldQuery fieldQuery = fieldQueryMap.get(mapKey);
// 属性包含对应的查询包装器
if (fieldQuery != null) {
@SuppressWarnings("unchecked")
QueryWrapper queryWrapper = fieldQuery.getQueryBuilder().build(entity);
FieldType fieldType = fieldQuery.getFieldType();
if (fieldType == FieldType.AUTO) {
fieldType = FieldType.determineFieldType(field);
fieldQueryMap.forEach((key, fieldQuery) -> {
//不是当前类的内容
if (!key.startsWith(entity.getClass().getName() + "#")) {
return;
}
QueryWrapper queryWrapper = fieldQuery.getQueryBuilder().build(entity);
Class<?> filedType = fieldQuery.getFieldWrapper().getFieldType();
Object value;
if (Collection.class.isAssignableFrom(filedType)) {
Class<?> mappingType = fieldQuery.getFieldWrapper().getMappingType();
List<?> list = mapper.selectListByQueryAs(queryWrapper, mappingType);
// 转换成 Collection 子类或者空 Collection 对象避免 NPE
value = getCollectionValue(filedType, list);
// 循环查询泛型实体类
if ((!Number.class.isAssignableFrom(mappingType)
|| !String.class.isAssignableFrom(mappingType)
|| !Map.class.isAssignableFrom(mappingType))
&& !fieldQuery.isPrevent()) {
queryFields(mapper, (Collection<?>) value, fieldQueryMap);
}
Class<?> realFieldType = field.getType();
Object value;
switch (fieldType) {
case COLLECTION:
Class<?> genericType = getGenericType(entityClass, field);
List<?> list = mapper.selectListByQueryAs(queryWrapper, genericType);
// 转换成 Collection 子类或者空 Collection 对象避免 NPE
if (list != null) {
value = getCollectionValue(realFieldType, list);
// 循环查询泛型实体类
if ((!Number.class.isAssignableFrom(genericType)
|| !String.class.isAssignableFrom(genericType)
|| !Map.class.isAssignableFrom(genericType))
&& !fieldQuery.isPrevent()) {
queryFields(mapper, (Collection<?>) value, fieldQueryMap);
}
} else {
value = new ArrayList<>();
}
break;
case ENTITY:
value = mapper.selectOneByQueryAs(queryWrapper, realFieldType);
// 循环查询嵌套类
if (!fieldQuery.isPrevent()) {
queryFields(mapper, Collections.singletonList(value), fieldQueryMap);
}
break;
case MAP:
List<Row> rows = mapper.selectRowsByQuery(queryWrapper);
// 转换成 Map 子类或者空 Map 对象避免 NPE
if (rows != null && !rows.isEmpty() && rows.get(0) != null) {
value = getMapValue(realFieldType, rows.get(0));
} else {
value = new HashMap<>();
}
break;
case BASIC:
value = mapper.selectObjectByQueryAs(queryWrapper, realFieldType);
break;
case ARRAY:
Class<?> componentType = realFieldType.getComponentType();
List<?> objects = mapper.selectListByQueryAs(queryWrapper, componentType);
value = getArrayValue(componentType, objects);
break;
default:
value = null;
break;
} else if (Map.class.isAssignableFrom(filedType)) {
List<Row> rows = mapper.selectRowsByQuery(queryWrapper);
// 转换成 Map 子类或者空 Map 对象避免 NPE
if (rows != null && !rows.isEmpty() && rows.get(0) != null) {
value = getMapValue(filedType, rows.get(0));
} else {
value = new HashMap<>();
}
// 属性查询出来的值不为 null 为属性设置值
if (value != null) {
FieldWrapper.of(entityClass, fieldQuery.getFieldName()).set(value, entity);
} else if (filedType.isArray()) {
Class<?> componentType = filedType.getComponentType();
List<?> objects = mapper.selectListByQueryAs(queryWrapper, componentType);
value = getArrayValue(componentType, objects);
}
// 实体类
else {
value = mapper.selectOneByQueryAs(queryWrapper, filedType);
// 循环查询嵌套类
if (!fieldQuery.isPrevent()) {
queryFields(mapper, Collections.singletonList(value), fieldQueryMap);
}
}
}
// 属性查询出来的值不为 null 为属性设置值
if (value != null) {
fieldQuery.getFieldWrapper().set(value, entity);
}
});
}
}
private static Class<?> getGenericType(Class<?> entityClass, Field field) {
Type genericType = TypeParameterResolver.resolveFieldType(field, entityClass);
if (genericType instanceof ParameterizedType) {
return (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0];
} else {
throw FlexExceptions.wrap("Can not resolve generic type %s in field %s", genericType, field.getName());
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static Object getCollectionValue(Class<?> fieldType, Collection value) {
if (value == null) {
if (fieldType == List.class) {
return Collections.emptyList();
} else if (fieldType == Set.class) {
return Collections.emptySet();
}
}
if (ClassUtil.canInstance(fieldType.getModifiers())) {
Collection collection = (Collection) ClassUtil.newInstance(fieldType);
collection.addAll(value);
@ -139,7 +115,7 @@ public class FieldQueryManager {
return new HashSet<>(value);
}
throw FlexExceptions.wrap("Unsupported collection type.");
throw FlexExceptions.wrap("Unsupported collection type: " + fieldType);
}
@SuppressWarnings({"rawtypes", "unchecked"})

View File

@ -1,109 +0,0 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.field;
import com.mybatisflex.core.util.CollectionUtil;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.*;
import java.time.chrono.JapaneseDate;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Set;
/**
* 属性类型
*
* @author 王帅
* @since 2023-07-15
*/
public enum FieldType {
/**
* Map 对象
*/
MAP,
/**
* 自动推断
*/
AUTO,
/**
* 数组
*/
ARRAY,
/**
* 基本数据类型
*/
BASIC,
/**
* 实体类
*/
ENTITY,
/**
* 集合
*/
COLLECTION;
private static final Set<Class<?>> BASIC_TYPES = CollectionUtil.newHashSet(
int.class, Integer.class,
short.class, Short.class,
long.class, Long.class,
float.class, Float.class,
double.class, Double.class,
boolean.class, Boolean.class,
Date.class, java.sql.Date.class, Time.class, Timestamp.class,
Instant.class, LocalDate.class, LocalDateTime.class, LocalTime.class, OffsetDateTime.class, OffsetTime.class, ZonedDateTime.class,
Year.class, Month.class, YearMonth.class, JapaneseDate.class,
byte[].class, Byte[].class, Byte.class,
BigInteger.class, BigDecimal.class,
char.class, String.class, Character.class
);
/**
* 自动推断属性类型
*
* @param field 属性
* @return 属性类型
* @see FieldType#AUTO
*/
public static FieldType determineFieldType(Field field) {
Class<?> fieldType = field.getType();
if (Collection.class.isAssignableFrom(fieldType)) {
return COLLECTION;
} else if (Map.class.isAssignableFrom(fieldType)) {
return MAP;
} else if (fieldType.isArray()) {
return ARRAY;
} else if (BASIC_TYPES.contains(fieldType)
|| fieldType.isEnum()) {
return BASIC;
} else {
return ENTITY;
}
}
}

View File

@ -44,7 +44,7 @@ public class LambdaUtil {
public static <T> String getClassName(LambdaGetter<T> getter) {
SerializedLambda lambda = getSerializedLambda(getter);
String className = lambda.getImplClass();
String className = getImplClass(lambda);
return className.replace('/', '.');
}

View File

@ -186,10 +186,13 @@ public class MapperUtil {
for (Consumer<FieldQueryBuilder<R>> consumer : consumers) {
FieldQueryBuilder<R> fieldQueryBuilder = new FieldQueryBuilder<>();
consumer.accept(fieldQueryBuilder);
FieldQuery fieldQuery = fieldQueryBuilder.build();
FieldQuery fieldQuery = fieldQueryBuilder.build(list.get(0).getClass());
String className = fieldQuery.getClassName();
String fieldName = fieldQuery.getFieldName();
String mapKey = className + '#' + fieldName;
fieldQueryMap.put(mapKey, fieldQuery);
}

View File

@ -16,7 +16,6 @@
package com.mybatisflex.test.mapper;
import com.mybatisflex.core.field.FieldType;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.test.model.*;
@ -141,7 +140,7 @@ class UserMapperTest {
@Test
void testComplexSelectListFields() {
List<UserInfo> userInfos = userMapper.selectListByQueryAs(QueryWrapper.create(), UserInfo.class,
c -> c.field(UserInfo::getIdNumber).fieldType(FieldType.BASIC).queryWrapper(userInfo ->
c -> c.field(UserInfo::getIdNumber).queryWrapper(userInfo ->
QueryWrapper.create()
.select(ID_CARD.ID_NUMBER)
.from(ID_CARD)
@ -194,7 +193,7 @@ class UserMapperTest {
List<UserInfo> userInfos2 = userMapper.selectListWithRelationsByQueryAs(queryWrapper2, UserInfo.class);
List<UserInfo> userInfos3 = userMapper.selectListByQueryAs(QueryWrapper.create(), UserInfo.class,
c -> c.field(UserInfo::getIdNumber).fieldType(FieldType.BASIC).queryWrapper(userInfo ->
c -> c.field(UserInfo::getIdNumber).queryWrapper(userInfo ->
QueryWrapper.create()
.select(ID_CARD.ID_NUMBER)
.from(ID_CARD)