Title

反射操作ReflectionUtils及批量插入操作

ReflectionUtilsSpring框架中的反射工具类,它提供了一系列静态方法,可以方便地进行类、对象、方法、字段等反射操作。

工具类

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 反射工具类
 * 定义工具方法使用反射创建对象,调用方法。好处:一次编写多次调用(一劳永逸)
 */
public class ReflectUtils {

    public static final String SERIALVERSIONUID = "serialVersionUID";
    public static final String UPDATE_TIME = "update_time";
    public static final String ID = "id";

    /**
     * 定义静态工具方法,使用反射创建对象
     * 步骤:
     * 1 调用Class.forName(className)方法,获取Class对象。反射入口
     * 2 调用Class的getDeclaredConstructor方法获取构造器
     * 3 调用构造器的newInstance方法创建对象
     * @param className 以字符串表示的类名称,通常是包名+类名
     * @param <T> 该方法支持泛型
     * @return 泛型(不确定的类型)
     */
    public static <T> T createObject(String className) throws Exception {
        Class<?> clazz = Class.forName(className);
        Constructor<?> structor = clazz.getDeclaredConstructor();
        return (T)structor.newInstance();
    }

    /**
     * 使用反射创建带有参数对象
     * @param className 以字符串表示的类名称,通常是包名+类名
     * @param params 参数类型列表
     * @param values 参数实际值列表
     * @param <T> 方法支持泛型
     * @return 返回结果是泛型(不确定的类型)
     */
    public static <T> T createObject(String className,Class<?>[] params,Object... values) throws Exception{
        Class<?> clazz = Class.forName(className);
        Constructor<?> struct = clazz.getDeclaredConstructor(params);
        return (T)struct.newInstance(values);
    }

    /**
     *
     * 使用反射完成方法调用(无参)
     * 步骤:
     * 1 根据方法名称获取方法签名(信息)
     * 2 使用反射调用方法
     * @param  type 对象的实例
     * @param  methodName 方法名称
     * 备注:该方法支持两个泛型
     *  T 参数是一个泛型类型
     *  R 返回值是一个泛型
     */
    public static <T,R>  R invokeMethod(T type,String methodName) throws Exception {
        Class<?> clazz = type.getClass();
        // 根据方法名称获取方法签名
        Method method = clazz.getDeclaredMethod(methodName);
        return (R)method.invoke(type);
    }

    /**
     * 使用反射调用带有参数的方法
     * 步骤:
     * 1 获取Class对象
     * 2 根据方法名称获取方法签名
     * 3 调用Method的invoke方法,完成方法调用,并填写参数类型和参数名称
     * 4 返回方法调用的结果
     * @param obj 反射创建的对象
     * @param methodName 方法名称
     * @param params 参数类型列表
     * @param value 参数值列表
     * @param <T> 参数支持泛型
     * @param <R> 返回值支持泛型
     * @return 返回方法调用的结果
     */
    public static <T,R> R invokeMethod(T obj,String methodName,Class[] params,Object...value) throws Exception{
        Class<?> clazz = obj.getClass();
        Method method = clazz.getDeclaredMethod(methodName, params);
        // 编译错误:invoke方法返回Object类型,但是我定义的方法返回R,需要将返回类型强制转换为R类型
        // return method.invoke(type,value);
        return (R) method.invoke(obj,value);
    }

    /**
     * 使用反射访问私有属性(暴力破解)
     * 步骤:
     * 1 获取Class对象
     * 2 根据属性名称获取属性签名Field
     * 3 设置属性签名的可访问性为true
     * 4 调用set方法访问私有属性(为私有属性赋值)
     * @param obj 对象
     * @param fieldName 属性名称
     * @param value 属性值
     * @param <T> 参数支持泛型,此时表示参数类型不确定
     * @throws Exception
     */
    public static <T> void accessField(T obj,String fieldName,String value) throws Exception {
        Class<?> clazz = obj.getClass();
        Field stuNameField = clazz.getDeclaredField(fieldName);
        // 设置属性可访问性为true,就能够访问私有属性
        stuNameField.setAccessible(true);
        // 为私有属性赋值
        stuNameField.set(obj,value);
    }

    /**
     * 获取对象字段名、字段值
     */
    public static <T> Map<String, Object> getFieldNameValueMap(T t) {
        Field[] fields = ReflectUtil.getFields(t.getClass());
        Map<String, Object> map = new HashMap<>(16);
        Arrays.stream(fields).forEach(o -> {
            if (SERIALVERSIONUID.equals(o.getName()) || ID.equals(o.getName())) {
                return;
            }
            if (UPDATE_TIME.equals(o.getName())) {
                map.put(o.getName(), new Date());
                return;
            }
            map.put(o.getName(), ReflectUtil.getFieldValue(t, o));
        });
        return map;
    }

    /**
     * 获取字段名
     */
    public static <T> List<String> getFieldNameList(Class<T> clazz) {
        Field[] fields = ReflectUtil.getFields(clazz);
        return Arrays.stream(fields).map(Field::getName)
                .filter(name -> !SERIALVERSIONUID.equals(name)).collect(Collectors.toList());
    }

   /**
     * 获取字段注解值
     */
    public static <T> List<String> getAnnotationNameValueList(Class<T> clazz, Class<? extends Annotation> annotationClass) {
        Field[] fields = ReflectUtil.getFields(clazz);
        return Arrays.stream(fields).filter(o -> o.isAnnotationPresent(annotationClass) && !SERIALVERSIONUID.equals(o.getName()))
            .map(o -> AnnotationUtil.getAnnotationValue(o, annotationClass).toString()).collect(Collectors.toList());
    }

    /**
     * 获取注解的字段名
     */
    public static <T> List<String> getAnnotationFieldNameList(Class<T> clazz, Class<? extends Annotation> annotationClass) {
        Field[] fields = ReflectUtil.getFields(clazz);
        return Arrays.stream(fields).filter(o -> o.isAnnotationPresent(annotationClass) && !SERIALVERSIONUID.equals(o.getName()))
            .map(Field::getName).collect(Collectors.toList());
    }

    /**
     * 获取多记录数据
     */
    public static <T> List<List<Object>> getFieldValueLists(List<String> fieldNameList, List<T> list) {
        List<List<Object>> dataList = new ArrayList<>(1000);
        list.forEach(o -> {
            List<Object> fieldValueList = new LinkedList<>();
            fieldNameList.forEach(name -> {
                if (UPDATE_TIME.equals(name)) {
                    fieldValueList.add(new Date());
                    return;
                }
                fieldValueList.add(ReflectUtil.getFieldValue(o, name));
            });
            dataList.add(fieldValueList);
        });
        return dataList;
    }

    /**
     * 获取实体表名
     */
    public static <T> String getTableName(T t) {
        if (t instanceof Class) {
            return AnnotationUtil.getAnnotationValue((Class) t, TableName.class).toString();
        }
        return AnnotationUtil.getAnnotationValue(t.getClass(), TableName.class).toString();
    }

   /**
     * 字段名驼峰转下划线
     *
     * @param fieldNameList
     * @return
     */
    private List<String> toUnderlineCase(List<String> fieldNameList) {
        return fieldNameList.stream().map(StrUtil::toUnderlineCase).collect(Collectors.toList());
    }
}

在Java中,使用return语句在forEach循环中无法直接退出整个forEach循环。这是因为forEach方法内部的迭代操作是由函数式接口 Consumer 的实现来执行的,并且该接口没有提供直接控制循环流程的机制。
当在forEach循环中使用return语句时,它只会终止当前迭代并跳到下一次迭代,而不会结束整个forEach循环。后续的元素仍会被处理
这是因为forEach循环是一个遍历操作,它通常是基于迭代器(Iterator)或流(Stream)的内部实现,它会按顺序逐个处理每个元素,而不具备像普通循环那样的控制流程的能力。
如果希望在某些条件下完全退出循环,可以使用传统的for循环或while循环,其中可以使用break语句来实现循环的提前终止。

使用 mybatis 批量插入

serviceImpl:

    /**
     * 批量插入
     *
     * @param list
     * @param clazz
     */
    @Override
    public <T> int batchSave(List<T> list, Class<T> clazz) {
        // 获取插入的表字段
        List<String> fieldNameList = ReflectUtils.getAnnotationNameValueList(clazz, JsonProperty.class);
        // 获取插入的表名
        String tableName = ReflectUtils.getTableName(clazz);
        // 获取字段名
        List<String> fieldNames = ReflectUtils.getAnnotationFieldNameList(clazz, JsonProperty.class);
        List<List<Object>> fieldValueLists = ReflectUtils.getFieldValueLists(fieldNames, list);
        return dataMapper.batchSave(fieldNameList, fieldValueLists, tableName);
    }

mapper:

/**
 *
 * 批量插入
 */
int batchSave(@Param("fieldNameList") List<String> fieldNameList, @Param("fieldValueLists") List<List<Object>> fieldValueLists, @Param("tableName") String tableName);
/**
 *
 * 清理表所有数据
 */
@Delete("TRUNCATE TABLE ${tableName}")
void clear(@Param("tableName") String tableName);

xml:

    <insert id="batchSave">
        insert into
        ${tableName}
        <foreach collection="fieldNameList" item="item" index="index" open="(" separator="," close=")">${item}</foreach>
        values
        <foreach collection="fieldValueLists" item="item" separator=",">
            <foreach collection="item" item="valueItem" index="index" open="(" separator="," close=")">
                #{valueItem}
            </foreach>
        </foreach>
    </insert>

调用

// 查询需要批量插入数据总量 sum
// 计算插入的页数 (这里每次1000)
Page page = new Page(1,1000);
int pageNum = sum % 1000 == 0 ? sum / 1000 : sum / 1000 + 1;
Stream.iterate(1, no -> no + 1).limit(pageNum).forEach(no -> {
  // 设置页数
  page.setPage(no);
  // 根据设置的分页信息查询数据
  List<Entity> data=this.baseMapper.selectPage(page,new QueryWrapper());
  // 批量插入
  int i = baseService.batchSave(data, Entity.class);
});
posted @ 2024-10-09 10:37  快乐小洋人  阅读(83)  评论(0编辑  收藏  举报