feat: 1 分页查询添加关联字段查询;2 添加 FieldWrapper 使得关联字段查询拥有更高的性能;3 添加 ClassUtil.getFirstMethod 方法;4 优化 EnumWrapper 使之逻辑更加清晰简单;4 字段子查询不在需要配置 type

This commit is contained in:
开源海哥 2023-05-26 12:54:39 +08:00
parent cd2ff939e5
commit b9d3a6e16c
9 changed files with 193 additions and 86 deletions

View File

@ -29,7 +29,6 @@ QueryWrapper queryWrapper = QueryWrapper.create()
List<Article> articles = mapper.selectListByQuery(queryWrapper
, fieldQueryBuilder -> fieldQueryBuilder
.field(Article::getCategories) // 或者 .field("categories")
.type(Category.class) //非集合,自动读取 type可以不指定 type
.queryWrapper(article -> QueryWrapper.create()
.select().from(CATEGORY)
.where(CATEGORY.id.in(

View File

@ -19,7 +19,6 @@ package com.mybatisflex.codegen.test;
import com.mybatisflex.codegen.Generator;
import com.mybatisflex.codegen.config.GlobalConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Test;
import java.util.function.UnaryOperator;
@ -82,7 +81,7 @@ public class GeneratorTest {
generator.generate();
}
@Test
// @Test
public void testCodeGen2() {
//配置数据源
HikariDataSource dataSource = new HikariDataSource();

View File

@ -24,14 +24,9 @@ import com.mybatisflex.core.provider.EntitySqlProvider;
import com.mybatisflex.core.query.*;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.util.CollectionUtil;
import com.mybatisflex.core.util.ConvertUtil;
import com.mybatisflex.core.util.ObjectUtil;
import com.mybatisflex.core.util.StringUtil;
import com.mybatisflex.core.util.*;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.builder.annotation.ProviderContext;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.io.Serializable;
import java.util.*;
@ -426,29 +421,24 @@ public interface BaseMapper<T> {
consumer.accept(fieldQueryBuilder);
FieldQuery fieldQuery = fieldQueryBuilder.build();
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
MetaObject entityMetaObject = SystemMetaObject.forObject(entity);
Class<?> setterType = entityMetaObject.getSetterType(fieldQuery.getField());
Class<?> mappingType = fieldQuery.getMappingType();
if (mappingType == null) {
if (setterType.isAssignableFrom(Collection.class)) {
throw new IllegalStateException("Mapping Type can not be null for query Many.");
} else if (setterType.isArray()) {
mappingType = setterType.getComponentType();
} else {
mappingType = setterType;
}
FieldWrapper fieldWrapper = FieldWrapper.of(entity.getClass(), fieldQuery.getField());
if (fieldWrapper == null) {
throw new IllegalStateException("Can not find field \"" + fieldQuery.getField() + "\" in class: " + entity.getClass());
}
Class<?> fieldType = fieldWrapper.getFieldType();
Class<?> mappingType = fieldWrapper.getMappingType();
Object value;
try {
MappedStatementTypes.setCurrentType(mappingType);
if (setterType.isAssignableFrom(List.class)) {
if (fieldType.isAssignableFrom(List.class)) {
value = selectListByQueryAs(childQuery, mappingType);
} else if (setterType.isAssignableFrom(Set.class)) {
} else if (fieldType.isAssignableFrom(Set.class)) {
value = selectListByQueryAs(childQuery, mappingType);
value = new HashSet<>((Collection<?>) value);
} else if (setterType.isArray()) {
} else if (fieldType.isArray()) {
value = selectListByQueryAs(childQuery, mappingType);
value = ((List<?>) value).toArray();
} else {
@ -457,7 +447,7 @@ public interface BaseMapper<T> {
} finally {
MappedStatementTypes.clear();
}
entityMetaObject.setValue(fieldQuery.getField(), value);
fieldWrapper.set(value, entity);
}
});
@ -498,29 +488,24 @@ public interface BaseMapper<T> {
FieldQuery fieldQuery = fieldQueryBuilder.build();
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
MetaObject entityMetaObject = SystemMetaObject.forObject(entity);
Class<?> setterType = entityMetaObject.getSetterType(fieldQuery.getField());
FieldWrapper fieldWrapper = FieldWrapper.of(entity.getClass(), fieldQuery.getField());
Class<?> mappingType = fieldQuery.getMappingType();
if (mappingType == null) {
if (setterType.isAssignableFrom(Collection.class)) {
throw new IllegalStateException("Mapping Type can not be null for query Many.");
} else if (setterType.isArray()) {
mappingType = setterType.getComponentType();
} else {
mappingType = setterType;
}
if (fieldWrapper == null) {
throw new IllegalStateException("Can not find field \"" + fieldQuery.getField() + "\" in class: " + entity.getClass());
}
Class<?> fieldType = fieldWrapper.getFieldType();
Class<?> mappingType = fieldWrapper.getMappingType();
Object value;
try {
MappedStatementTypes.setCurrentType(mappingType);
if (setterType.isAssignableFrom(List.class)) {
if (fieldType.isAssignableFrom(List.class)) {
value = selectListByQueryAs(childQuery, mappingType);
} else if (setterType.isAssignableFrom(Set.class)) {
} else if (fieldType.isAssignableFrom(Set.class)) {
value = selectListByQueryAs(childQuery, mappingType);
value = new HashSet<>((Collection<?>) value);
} else if (setterType.isArray()) {
} else if (fieldType.isArray()) {
value = selectListByQueryAs(childQuery, mappingType);
value = ((List<?>) value).toArray();
} else {
@ -529,9 +514,7 @@ public interface BaseMapper<T> {
} finally {
MappedStatementTypes.clear();
}
entityMetaObject.setValue(fieldQuery.getField(), value);
fieldWrapper.set(value, entity);
}
});
@ -694,12 +677,12 @@ public interface BaseMapper<T> {
* @param queryWrapper 查询条件
* @return page 数据
*/
default Page<T> paginate(Page<T> page, QueryWrapper queryWrapper) {
return paginateAs(page, queryWrapper, null);
default Page<T> paginate(Page<T> page, QueryWrapper queryWrapper, Consumer<FieldQueryBuilder<T>>... consumers) {
return paginateAs(page, queryWrapper, null, consumers);
}
default <R> Page<R> paginateAs(Page<R> page, QueryWrapper queryWrapper, Class<R> asType) {
default <R> Page<R> paginateAs(Page<R> page, QueryWrapper queryWrapper, Class<R> asType, Consumer<FieldQueryBuilder<R>>... consumers) {
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
@ -780,7 +763,7 @@ public interface BaseMapper<T> {
// 调用内部方法不走代理需要主动设置 MappedStatementType
// fixed https://gitee.com/mybatis-flex/mybatis-flex/issues/I73BP6
MappedStatementTypes.setCurrentType(asType);
List<R> records = selectListByQueryAs(queryWrapper, asType);
List<R> records = selectListByQueryAs(queryWrapper, asType, consumers);
page.setRecords(records);
} finally {
MappedStatementTypes.clear();

View File

@ -22,7 +22,7 @@ import java.io.Serializable;
public class FieldQuery implements Serializable {
private String field;
private Class<?> mappingType;
// private Class<?> mappingType;
private QueryWrapper queryWrapper;
public String getField() {
@ -33,13 +33,13 @@ public class FieldQuery implements Serializable {
this.field = field;
}
public Class<?> getMappingType() {
return mappingType;
}
public void setMappingType(Class<?> mappingType) {
this.mappingType = mappingType;
}
// public Class<?> getMappingType() {
// return mappingType;
// }
//
// public void setMappingType(Class<?> mappingType) {
// this.mappingType = mappingType;
// }
public QueryWrapper getQueryWrapper() {
return queryWrapper;

View File

@ -38,10 +38,10 @@ public class FieldQueryBuilder<T> implements Serializable {
return field(LambdaUtil.getFieldName(fn));
}
public FieldQueryBuilder<T> type(Class<?> mappingType){
fieldQuery.setMappingType(mappingType);
return this;
}
// public FieldQueryBuilder<T> type(Class<?> mappingType){
// fieldQuery.setMappingType(mappingType);
// return this;
// }
public FieldQueryBuilder<T> queryWrapper(QueryBuilder<T> fun){
fieldQuery.setQueryWrapper(fun.build(entity));

View File

@ -181,17 +181,23 @@ public class ClassUtil {
public static List<Field> getAllFields(Class<?> cl) {
List<Field> fields = new ArrayList<>();
doGetFields(cl, fields, null);
doGetFields(cl, fields, null, false);
return fields;
}
public static List<Field> getAllFields(Class<?> cl, Predicate<Field> predicate) {
List<Field> fields = new ArrayList<>();
doGetFields(cl, fields, predicate);
doGetFields(cl, fields, predicate, false);
return fields;
}
private static void doGetFields(Class<?> cl, List<Field> fields, Predicate<Field> predicate) {
public static Field getFirstField(Class<?> cl, Predicate<Field> predicate) {
List<Field> fields = new ArrayList<>();
doGetFields(cl, fields, predicate, true);
return fields.isEmpty() ? null : fields.get(0);
}
private static void doGetFields(Class<?> cl, List<Field> fields, Predicate<Field> predicate, boolean firstOnly) {
if (cl == null || cl == Object.class) {
return;
}
@ -200,26 +206,39 @@ public class ClassUtil {
for (Field declaredField : declaredFields) {
if (predicate == null || predicate.test(declaredField)) {
fields.add(declaredField);
if (firstOnly) {
break;
}
}
}
doGetFields(cl.getSuperclass(), fields, predicate);
if (firstOnly && !fields.isEmpty()) {
return;
}
doGetFields(cl.getSuperclass(), fields, predicate, firstOnly);
}
public static List<Method> getAllMethods(Class<?> cl) {
List<Method> methods = new ArrayList<>();
doGetMethods(cl, methods, null);
doGetMethods(cl, methods, null, false);
return methods;
}
public static List<Method> getAllMethods(Class<?> cl, Predicate<Method> predicate) {
List<Method> methods = new ArrayList<>();
doGetMethods(cl, methods, predicate);
doGetMethods(cl, methods, predicate, false);
return methods;
}
public static Method getFirstMethod(Class<?> cl, Predicate<Method> predicate) {
List<Method> methods = new ArrayList<>();
doGetMethods(cl, methods, predicate, true);
return methods.isEmpty() ? null : methods.get(0);
}
private static void doGetMethods(Class<?> cl, List<Method> methods, Predicate<Method> predicate) {
private static void doGetMethods(Class<?> cl, List<Method> methods, Predicate<Method> predicate, boolean firstOnly) {
if (cl == null || cl == Object.class) {
return;
}
@ -228,10 +247,17 @@ public class ClassUtil {
for (Method method : declaredMethods) {
if (predicate == null || predicate.test(method)) {
methods.add(method);
if (firstOnly) {
break;
}
}
}
doGetMethods(cl.getSuperclass(), methods, predicate);
if (firstOnly && !methods.isEmpty()) {
return;
}
doGetMethods(cl.getSuperclass(), methods, predicate, firstOnly);
}
private static <T> Class<T> getJdkProxySuperClass(Class<T> clazz) {

View File

@ -22,7 +22,6 @@ import org.apache.ibatis.util.MapUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -45,31 +44,30 @@ public class EnumWrapper<E extends Enum<E>> {
public EnumWrapper(Class<E> enumClass) {
this.enumClass = enumClass;
List<Field> allFields = ClassUtil.getAllFields(enumClass, field -> field.getAnnotation(EnumValue.class) != null);
if (!allFields.isEmpty()) {
Field enumValueField = ClassUtil.getFirstField(enumClass, field -> field.getAnnotation(EnumValue.class) != null);
if (enumValueField != null) {
hasEnumValueAnnotation = true;
}
if (hasEnumValueAnnotation) {
Field field = allFields.get(0);
String fieldGetterName = "get" + StringUtil.firstCharToUpperCase(enumValueField.getName());
String fieldGetterName = "get" + StringUtil.firstCharToUpperCase(field.getName());
List<Method> allMethods = ClassUtil.getAllMethods(enumClass, method -> {
Method getterMethod = ClassUtil.getFirstMethod(enumClass, method -> {
String methodName = method.getName();
return methodName.equals(fieldGetterName);
return methodName.equals(fieldGetterName) && Modifier.isPublic(method.getModifiers());
});
enumPropertyType = ClassUtil.wrap(field.getType());
enumPropertyType = ClassUtil.wrap(enumValueField.getType());
enums = enumClass.getEnumConstants();
if (allMethods.isEmpty()) {
if (Modifier.isPublic(field.getModifiers())) {
property = field;
if (getterMethod != null) {
if (Modifier.isPublic(enumValueField.getModifiers())) {
property = enumValueField;
} else {
throw new IllegalStateException("Can not find \"" + fieldGetterName + "()\" method in enum: " + enumClass.getName());
}
} else {
getter = allMethods.get(0);
getter = getterMethod;
}
}
}

View File

@ -0,0 +1,103 @@
/**
* 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.util;
import java.lang.reflect.*;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class FieldWrapper {
public static Map<Class<?>, Map<String, FieldWrapper>> cache = new ConcurrentHashMap<>();
private Class<?> fieldType;
private Class<?> mappingType;
private Method method;
public static FieldWrapper of(Class<?> clazz, String fieldName) {
Map<String, FieldWrapper> wrapperMap = cache.get(clazz);
if (wrapperMap == null) {
synchronized (clazz) {
if (wrapperMap == null) {
wrapperMap = new ConcurrentHashMap<>();
cache.put(clazz, wrapperMap);
}
}
}
FieldWrapper fieldWrapper = wrapperMap.get(fieldName);
if (fieldWrapper == null) {
synchronized (clazz) {
fieldWrapper = wrapperMap.get(fieldName);
if (fieldWrapper == null) {
Field findField = ClassUtil.getFirstField(clazz, field -> field.getName().equals(fieldName));
if (findField == null) {
return null;
}
fieldWrapper = new FieldWrapper();
fieldWrapper.fieldType = findField.getType();
fieldWrapper.mappingType = getFieldMappingType(findField);
Method setter = ClassUtil.getFirstMethod(clazz, method ->
method.getParameterCount() == 1
&& Modifier.isPublic(method.getModifiers())
&& method.getName().equals("set" + StringUtil.firstCharToUpperCase(fieldName)));
fieldWrapper.method = setter;
wrapperMap.put(fieldName, fieldWrapper);
}
}
}
return fieldWrapper;
}
private static Class<?> getFieldMappingType(Field field) {
Class<?> fieldType = field.getType();
if (Collection.class.isAssignableFrom(fieldType)) {
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
Type actualTypeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[0];
return (Class<?>) actualTypeArgument;
}
}
if (fieldType.isArray()) {
return field.getType().getComponentType();
}
return fieldType;
}
public void set(Object value, Object to) {
try {
method.invoke(to, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Class<?> getFieldType() {
return fieldType;
}
public Class<?> getMappingType() {
return mappingType;
}
}

View File

@ -71,15 +71,14 @@ public class EntityTestStarter {
RowUtil.printPretty(rowList);
// List<Account> accounts1 = accountMapper.selectListByQuery(QueryWrapper.create()
// , accountFieldQueryBuilder -> accountFieldQueryBuilder
// .field(Account::getArticles)
// .type(Article.class)
// .queryWrapper(entity ->
// select().from(ARTICLE).where(ARTICLE.ACCOUNT_ID.eq(entity.getId()))
// )
// );
// System.out.println(accounts1);
List<Account> accounts1 = accountMapper.selectListByQuery(QueryWrapper.create()
, accountFieldQueryBuilder -> accountFieldQueryBuilder
.field(Account::getArticles)
.queryWrapper(entity ->
select().from(ARTICLE).where(ARTICLE.ACCOUNT_ID.eq(entity.getId()))
)
);
System.out.println(accounts1);
// MyAccountMapper myAccountMapper = bootstrap.getMapper(MyAccountMapper.class);