diff --git a/docs/zh/base/field-query.md b/docs/zh/base/field-query.md index ee8cb2ae..a9ca4648 100644 --- a/docs/zh/base/field-query.md +++ b/docs/zh/base/field-query.md @@ -29,7 +29,6 @@ QueryWrapper queryWrapper = QueryWrapper.create() List
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( diff --git a/mybatis-flex-codegen/src/test/java/com/mybatisflex/codegen/test/GeneratorTest.java b/mybatis-flex-codegen/src/test/java/com/mybatisflex/codegen/test/GeneratorTest.java index 2efebe31..2c256448 100644 --- a/mybatis-flex-codegen/src/test/java/com/mybatisflex/codegen/test/GeneratorTest.java +++ b/mybatis-flex-codegen/src/test/java/com/mybatisflex/codegen/test/GeneratorTest.java @@ -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(); diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/BaseMapper.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/BaseMapper.java index b220ef16..88fa07ca 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/BaseMapper.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/BaseMapper.java @@ -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 { 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 { } finally { MappedStatementTypes.clear(); } - entityMetaObject.setValue(fieldQuery.getField(), value); + fieldWrapper.set(value, entity); } }); @@ -498,29 +488,24 @@ public interface BaseMapper { 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 { } finally { MappedStatementTypes.clear(); } - - - entityMetaObject.setValue(fieldQuery.getField(), value); + fieldWrapper.set(value, entity); } }); @@ -694,12 +677,12 @@ public interface BaseMapper { * @param queryWrapper 查询条件 * @return page 数据 */ - default Page paginate(Page page, QueryWrapper queryWrapper) { - return paginateAs(page, queryWrapper, null); + default Page paginate(Page page, QueryWrapper queryWrapper, Consumer>... consumers) { + return paginateAs(page, queryWrapper, null, consumers); } - default Page paginateAs(Page page, QueryWrapper queryWrapper, Class asType) { + default Page paginateAs(Page page, QueryWrapper queryWrapper, Class asType, Consumer>... consumers) { List selectColumns = CPI.getSelectColumns(queryWrapper); List orderBys = CPI.getOrderBys(queryWrapper); @@ -780,7 +763,7 @@ public interface BaseMapper { // 调用内部方法,不走代理,需要主动设置 MappedStatementType // fixed https://gitee.com/mybatis-flex/mybatis-flex/issues/I73BP6 MappedStatementTypes.setCurrentType(asType); - List records = selectListByQueryAs(queryWrapper, asType); + List records = selectListByQueryAs(queryWrapper, asType, consumers); page.setRecords(records); } finally { MappedStatementTypes.clear(); diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/field/FieldQuery.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/field/FieldQuery.java index e0d662a6..372f7fdd 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/field/FieldQuery.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/field/FieldQuery.java @@ -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; diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/field/FieldQueryBuilder.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/field/FieldQueryBuilder.java index 47d74353..f147265f 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/field/FieldQueryBuilder.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/field/FieldQueryBuilder.java @@ -38,10 +38,10 @@ public class FieldQueryBuilder implements Serializable { return field(LambdaUtil.getFieldName(fn)); } - public FieldQueryBuilder type(Class mappingType){ - fieldQuery.setMappingType(mappingType); - return this; - } +// public FieldQueryBuilder type(Class mappingType){ +// fieldQuery.setMappingType(mappingType); +// return this; +// } public FieldQueryBuilder queryWrapper(QueryBuilder fun){ fieldQuery.setQueryWrapper(fun.build(entity)); diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/ClassUtil.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/ClassUtil.java index 33c9619a..d634adb3 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/ClassUtil.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/ClassUtil.java @@ -181,17 +181,23 @@ public class ClassUtil { public static List getAllFields(Class cl) { List fields = new ArrayList<>(); - doGetFields(cl, fields, null); + doGetFields(cl, fields, null, false); return fields; } public static List getAllFields(Class cl, Predicate predicate) { List fields = new ArrayList<>(); - doGetFields(cl, fields, predicate); + doGetFields(cl, fields, predicate, false); return fields; } - private static void doGetFields(Class cl, List fields, Predicate predicate) { + public static Field getFirstField(Class cl, Predicate predicate) { + List fields = new ArrayList<>(); + doGetFields(cl, fields, predicate, true); + return fields.isEmpty() ? null : fields.get(0); + } + + private static void doGetFields(Class cl, List fields, Predicate 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 getAllMethods(Class cl) { List methods = new ArrayList<>(); - doGetMethods(cl, methods, null); + doGetMethods(cl, methods, null, false); return methods; } public static List getAllMethods(Class cl, Predicate predicate) { List methods = new ArrayList<>(); - doGetMethods(cl, methods, predicate); + doGetMethods(cl, methods, predicate, false); return methods; } + public static Method getFirstMethod(Class cl, Predicate predicate) { + List methods = new ArrayList<>(); + doGetMethods(cl, methods, predicate, true); + return methods.isEmpty() ? null : methods.get(0); + } - private static void doGetMethods(Class cl, List methods, Predicate predicate) { + + private static void doGetMethods(Class cl, List methods, Predicate 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 Class getJdkProxySuperClass(Class clazz) { diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/EnumWrapper.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/EnumWrapper.java index 75522b7e..691c6d76 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/EnumWrapper.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/EnumWrapper.java @@ -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> { public EnumWrapper(Class enumClass) { this.enumClass = enumClass; - List 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 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; } } } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/FieldWrapper.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/FieldWrapper.java new file mode 100644 index 00000000..feda5030 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/util/FieldWrapper.java @@ -0,0 +1,103 @@ +/** + * 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.util; + +import java.lang.reflect.*; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class FieldWrapper { + + public static Map, Map> cache = new ConcurrentHashMap<>(); + + private Class fieldType; + private Class mappingType; + private Method method; + + public static FieldWrapper of(Class clazz, String fieldName) { + Map 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; + } +} diff --git a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/EntityTestStarter.java b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/EntityTestStarter.java index 317c04e5..05af3146 100644 --- a/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/EntityTestStarter.java +++ b/mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/EntityTestStarter.java @@ -71,15 +71,14 @@ public class EntityTestStarter { RowUtil.printPretty(rowList); -// List 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 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);