mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 00:58:24 +08:00
perf: optimize FieldQueryManager.queryFields
This commit is contained in:
parent
75a1df800e
commit
a1aae02118
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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"})
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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('/', '.');
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user