反射操作ReflectionUtils及批量插入操作
ReflectionUtils
是Spring框架
中的反射工具类,它提供了一系列静态方法,可以方便地进行类、对象、方法、字段等反射操作。
工具类
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);
});