From 8f1ef267c47884418f34aafdeebabc70b160e8fe Mon Sep 17 00:00:00 2001 From: admin <570810310@qq.com> Date: Mon, 24 May 2021 10:16:57 +0800 Subject: [PATCH] https://gitee.com/baomidou/mybatis-plus/issues/I3SE8R --- .../yulichang/base/MPJBaseServiceImpl.java | 26 +++ .../yulichang/injector/MPJSqlInjector.java | 38 ++++ .../yulichang/toolkit/ReflectionKit.java | 196 ++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 src/main/java/com/github/yulichang/toolkit/ReflectionKit.java diff --git a/src/main/java/com/github/yulichang/base/MPJBaseServiceImpl.java b/src/main/java/com/github/yulichang/base/MPJBaseServiceImpl.java index 4160747..3ff87f7 100644 --- a/src/main/java/com/github/yulichang/base/MPJBaseServiceImpl.java +++ b/src/main/java/com/github/yulichang/base/MPJBaseServiceImpl.java @@ -3,6 +3,7 @@ package com.github.yulichang.base; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.yulichang.interfaces.MPJBaseJoin; +import com.github.yulichang.toolkit.ReflectionKit; import java.util.List; import java.util.Map; @@ -11,8 +12,33 @@ import java.util.Map; * @author yulichang * @see ServiceImpl */ +@SuppressWarnings("unchecked") public class MPJBaseServiceImpl, T> extends ServiceImpl implements MPJBaseService { + /** + * mybatis plus 3.4.3 bug + *

+ * https://gitee.com/baomidou/mybatis-plus/issues/I3SE8R + *

+ * https://gitee.com/baomidou/mybatis-plus/commit/7210b461b23211e6b95ca6de2d846aa392bdc28c + */ + @Override + protected Class currentMapperClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 0); + } + + /** + * mybatis plus 3.4.3 bug + *

+ * https://gitee.com/baomidou/mybatis-plus/issues/I3SE8R + *

+ * https://gitee.com/baomidou/mybatis-plus/commit/7210b461b23211e6b95ca6de2d846aa392bdc28c + */ + @Override + protected Class currentModelClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 1); + } + @Override public DTO selectJoinOne(Class clazz, MPJBaseJoin wrapper) { return baseMapper.selectJoinOne(clazz, wrapper); diff --git a/src/main/java/com/github/yulichang/injector/MPJSqlInjector.java b/src/main/java/com/github/yulichang/injector/MPJSqlInjector.java index bd63805..4b97a16 100644 --- a/src/main/java/com/github/yulichang/injector/MPJSqlInjector.java +++ b/src/main/java/com/github/yulichang/injector/MPJSqlInjector.java @@ -2,10 +2,20 @@ package com.github.yulichang.injector; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; +import com.baomidou.mybatisplus.core.mapper.Mapper; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils; import com.github.yulichang.method.*; +import com.github.yulichang.toolkit.ReflectionKit; +import org.apache.ibatis.builder.MapperBuilderAssistant; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import java.util.List; +import java.util.Set; /** * SQL 注入器 @@ -15,6 +25,7 @@ import java.util.List; */ @ConditionalOnMissingBean(DefaultSqlInjector.class) public class MPJSqlInjector extends DefaultSqlInjector { + private static final Log logger = LogFactory.getLog(MPJSqlInjector.class); @Override public List getMethodList(Class mapperClass) { @@ -27,4 +38,31 @@ public class MPJSqlInjector extends DefaultSqlInjector { list.add(new SelectJoinMapsPage()); return list; } + + /** + * mybatis plus 3.4.3 bug + *

+ * https://gitee.com/baomidou/mybatis-plus/issues/I3SE8R + *

+ * https://gitee.com/baomidou/mybatis-plus/commit/7210b461b23211e6b95ca6de2d846aa392bdc28c + */ + @Override + public void inspectInject(MapperBuilderAssistant builderAssistant, Class mapperClass) { + Class modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0); + if (modelClass != null) { + String className = mapperClass.toString(); + Set mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration()); + if (!mapperRegistryCache.contains(className)) { + List methodList = this.getMethodList(mapperClass); + if (CollectionUtils.isNotEmpty(methodList)) { + TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass); + // 循环注入自定义方法 + methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo)); + } else { + logger.debug(mapperClass.toString() + ", No effective injection method was found."); + } + mapperRegistryCache.add(className); + } + } + } } diff --git a/src/main/java/com/github/yulichang/toolkit/ReflectionKit.java b/src/main/java/com/github/yulichang/toolkit/ReflectionKit.java new file mode 100644 index 0000000..f08daf4 --- /dev/null +++ b/src/main/java/com/github/yulichang/toolkit/ReflectionKit.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2011-2021, baomidou (jobob@qq.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.github.yulichang.toolkit; + +import com.baomidou.mybatisplus.core.toolkit.*; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; +import org.springframework.core.GenericTypeResolver; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; + +/** + * 反射工具类,提供反射相关的快捷操作 + * + * @author Caratacus + * @author hcl + * @since 2016-09-22 + */ +public final class ReflectionKit { + private static final Log logger = LogFactory.getLog(ReflectionKit.class); + /** + * class field cache + */ + private static final Map, List> CLASS_FIELD_CACHE = new ConcurrentHashMap<>(); + + private static final Map, Class> PRIMITIVE_WRAPPER_TYPE_MAP = new IdentityHashMap<>(8); + + private static final Map, Class> PRIMITIVE_TYPE_TO_WRAPPER_MAP = new IdentityHashMap<>(8); + + static { + PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class); + PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class); + for (Map.Entry, Class> entry : PRIMITIVE_WRAPPER_TYPE_MAP.entrySet()) { + PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(entry.getValue(), entry.getKey()); + } + } + + /** + * 获取字段值 + * + * @param entity 实体 + * @param fieldName 字段名称 + * @return 属性值 + */ + public static Object getFieldValue(Object entity, String fieldName) { + Class cls = entity.getClass(); + Map fieldMaps = getFieldMap(cls); + try { + Field field = fieldMaps.get(fieldName); + Assert.notNull(field, "Error: NoSuchField in %s for %s. Cause:", cls.getSimpleName(), fieldName); + field.setAccessible(true); + return field.get(entity); + } catch (ReflectiveOperationException e) { + throw ExceptionUtils.mpe("Error: Cannot read field in %s. Cause:", e, cls.getSimpleName()); + } + } + + /** + *

+ * 反射对象获取泛型 + *

+ * + * @param clazz 对象 + * @param genericIfc 所属泛型父类 + * @param index 泛型所在位置 + * @return Class + */ + public static Class getSuperClassGenericType(final Class clazz, final Class genericIfc, final int index) { + Class[] typeArguments = GenericTypeResolver.resolveTypeArguments(ClassUtils.getUserClass(clazz), genericIfc); + return null == typeArguments ? null : typeArguments[index]; + } + + /** + *

+ * 获取该类的所有属性列表 + *

+ * + * @param clazz 反射类 + */ + public static Map getFieldMap(Class clazz) { + List fieldList = getFieldList(clazz); + return CollectionUtils.isNotEmpty(fieldList) ? fieldList.stream().collect(Collectors.toMap(Field::getName, field -> field)) : Collections.emptyMap(); + } + + /** + *

+ * 获取该类的所有属性列表 + *

+ * + * @param clazz 反射类 + */ + public static List getFieldList(Class clazz) { + if (Objects.isNull(clazz)) { + return Collections.emptyList(); + } + return CollectionUtils.computeIfAbsent(CLASS_FIELD_CACHE, clazz, k -> { + Field[] fields = k.getDeclaredFields(); + List superFields = new ArrayList<>(); + Class currentClass = k.getSuperclass(); + while (currentClass != null) { + Field[] declaredFields = currentClass.getDeclaredFields(); + Collections.addAll(superFields, declaredFields); + currentClass = currentClass.getSuperclass(); + } + /* 排除重载属性 */ + Map fieldMap = excludeOverrideSuperField(fields, superFields); + /* + * 重写父类属性过滤后处理忽略部分,支持过滤父类属性功能 + * 场景:中间表不需要记录创建时间,忽略父类 createTime 公共属性 + * 中间表实体重写父类属性 ` private transient Date createTime; ` + */ + return fieldMap.values().stream() + /* 过滤静态属性 */ + .filter(f -> !Modifier.isStatic(f.getModifiers())) + /* 过滤 transient关键字修饰的属性 */ + .filter(f -> !Modifier.isTransient(f.getModifiers())) + .collect(Collectors.toList()); + }); + } + + /** + *

+ * 排序重置父类属性 + *

+ * + * @param fields 子类属性 + * @param superFieldList 父类属性 + */ + public static Map excludeOverrideSuperField(Field[] fields, List superFieldList) { + // 子类属性 + Map fieldMap = Stream.of(fields).collect(toMap(Field::getName, identity(), + (u, v) -> { + throw new IllegalStateException(String.format("Duplicate key %s", u)); + }, + LinkedHashMap::new)); + superFieldList.stream().filter(field -> !fieldMap.containsKey(field.getName())) + .forEach(f -> fieldMap.put(f.getName(), f)); + return fieldMap; + } + + /** + * 判断是否为基本类型或基本包装类型 + * + * @param clazz class + * @return 是否基本类型或基本包装类型 + */ + public static boolean isPrimitiveOrWrapper(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); + return (clazz.isPrimitive() || PRIMITIVE_WRAPPER_TYPE_MAP.containsKey(clazz)); + } + + public static Class resolvePrimitiveIfNecessary(Class clazz) { + return (clazz.isPrimitive() && clazz != void.class ? PRIMITIVE_TYPE_TO_WRAPPER_MAP.get(clazz) : clazz); + } + + /** + * 设置可访问对象的可访问权限为 true + * + * @param object 可访问的对象 + * @param 类型 + * @return 返回设置后的对象 + */ + public static T setAccessible(T object) { + return AccessController.doPrivileged(new SetAccessibleAction<>(object)); + } + +}