Java反射实现关系映射填充字段

需求描述

实体类AttributeVO中, name与value是一对一关系

数据库中只存储某个状态值,返回给前端需要转化为相应的文字

例如:   is_deleted           1:删除        0:未删除
        confirm_status       1:已确认      0:未确认    -1:不确认
        pay_status           1:已付款      0:未付款    -1:已退款

假设现在有一个类 ,name字段与value字段对应,但是现在只知道value的值,需要根据value的值和映射关系来填充name值,其中实体类例如

@Data
class AttrubuteVO {
    //名称;描述;
    private String name;
    //状态值;
    private Integer value;

}

其中映射关系放在枚举类当中(也可以存储在数据库中,建造数据字典方便修改,后面需要加入持久层逻辑,这里只实现枚举版本)

@Getter
public enum SysYesNoEnum {

    YES(1, "yes"),  

    NO(0, "no");  

    private Integer value;  
    private String name;
}

如果映射关系比教简单,可以不用枚举,在接下来的注解上添加映射关系

定义注解

我们定义两个注解,一个放在值上,另一个放在名称上

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AttrName {

    /**
     * 与AttrValue注解对应的key
     * @return key值
     */
    String key();

    /**
     * 值列表
     * @return 值列表
     */
    String[] names() default {};

    /**
     * 可以指定枚举类来作为值映射
     * @return 枚举类
     */
    Class<?> enumClass() default Void.class;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AttrValue {

    /**
     * 与AttrName注解对应的key
     * @return key值
     */
    String key();

    /**
     * 值列表
     * @return 值列表
     */
    String[] values() default {};
}

填充工具类

将添加注解的字段放在List中,方便后续使用

通过反射,获取注解中或枚举中的映射关系,将映射关系存储在Map当中

获取类字段后,循环列表,循环字段,判断是否标注注解,并且获取到映射关系

    /**
     * 将集合中的实体类中,根据注解@AttrValue标注字段填充@AttrName字段
     * 1.在注解中指定映射关系
     *  AttrValue注解中的values数组个数必须和AttrName中的names个数相同
     *  并且值与名称需要保持序号一致
     * 2.使用枚举类映射关系
     *   枚举类中,需要有name和value两个字段,并且有对应的Get方法
     *   在AttrName注解中 enumClass 参数填写对应的枚举类
     *   AttrValue与AttrName以key作为映射关系
     * @param sourceList List实体类
     * @param <T> 泛型
     */
    public static <T> void fillListAttribute(List<T> sourceList)  {
        if (sourceList.isEmpty()) {
            return;
        }
        //将标注注解的字段名放在List中
        List<Field> nameList = new ArrayList<>();
        List<Field> valueList = new ArrayList<>();
        Map<String, String[]> nameMap = new HashMap<>();
        Map<String, String[]> valueMap = new HashMap<>();
        Class<?> clazz = sourceList.get(0).getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //判断字段是否标注注解
            AttrName attrName = field.getAnnotation(AttrName.class);
            AttrValue attrValue = field.getAnnotation(AttrValue.class);
            if (Objects.isNull(attrName) && Objects.isNull(attrValue)) {
                continue;
            }
            // 同一个字段标注两个注解
            if (Objects.nonNull(attrName) && Objects.nonNull(attrValue)) {
                throw new RuntimeException("@AttrName和@AttrValue不能标注在同一个字段上");
            }
            //名称
            if (Objects.nonNull(attrName)) {
                String key = attrName.key();
                nameList.add(field);
                String[] names = attrName.names();
                if (names.length == 0) {
                    //如果注解的names没有值,就获取注解的值
                    getEnumNameValues(attrName.enumClass(), key, nameMap, valueMap);
                } else {
                    nameMap.put(key, names);
                }
            }
            //值
            if (Objects.nonNull(attrValue)) {
                valueList.add(field);
                String[] values = attrValue.values();
                if (values.length == 0) {
                    continue;
                }
                String key = attrValue.key();
                valueMap.put(key, values);
            }
        }
        //映射值
        mappingField(sourceList, clazz, nameList, valueList, nameMap, valueMap);
    }

获取到映射关系后,循环列表,获取value字段,根据映射关系,得到name值并赋值

    private static <T> void mappingField(List<T> sourceList, Class<?> clazz, List<Field> nameList, List<Field> valueList, Map<String, String[]> nameMap, Map<String, String[]> valueMap) {
        for (T t : sourceList) {
            //以value注解上的key作为唯一标识与name对应
            Map<String, String> keyValueMap = new HashMap<>();
            //读取value
            for (Field valueField : valueList) {
                PropertyDescriptor pd = null;
                try {
                    pd = new PropertyDescriptor(valueField.getName(), t.getClass());
                } catch (IntrospectionException e) {
                    throw new RuntimeException("未找到对应字段:" + valueField.getName());
                }
                Method readMethod = pd.getReadMethod();
                //获取值
                Object invoke = null;
                try {
                    //设置私有变量访问权限
                    valueField.setAccessible(true);
                    readMethod.setAccessible(true);
                    invoke = readMethod.invoke(t);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException("未找到对应字段:" + valueField.getName());
                }
                AttrValue annotation = valueField.getAnnotation(AttrValue.class);
                String key = annotation.key();
                //如果key、字段值、
                if (strIsBlank(key) || Objects.isNull(invoke)) {
                    break;
                }
                keyValueMap.put(annotation.key(), invoke.toString());
            }
            //映射name
            for (Field nameField : nameList) {
                PropertyDescriptor pd = null;
                try {
                    pd = new PropertyDescriptor(nameField.getName(), t.getClass());
                } catch (IntrospectionException e) {
                    throw new RuntimeException("未找到对应字段");
                }
                Method wirteMethod = pd.getWriteMethod();

                AttrName annotation = nameField.getAnnotation(AttrName.class);
                //如果key为空或者获取不到value,直接跳过
                String key = annotation.key();
                if (strIsBlank(key)) {
                    break;
                }
                String value = keyValueMap.get(key);
                String[] values = valueMap.get(key);
                if (strIsBlank(value) || values.length == 0) {
                    break;
                }
                int i;
                for (i = 0; i < values.length; i++) {
                    if (values[i].equals(value)) {
                        break;
                    }
                }
                //根据相应的索引获取name
                String[] names = nameMap.get(key);
                String name = names[i];
                //设置值
                try {
                    //设置私有变量访问权限
                    wirteMethod.setAccessible(true);
                    wirteMethod.invoke(t, name);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException("注解 设置值失败");
                }
            }
        }
    }

获取枚举类中的映射关系

    /**
     * 获取枚举类的所有name字段的值
     * @param clazz 枚举类
     * @return 所有name字段的值
     */
    private static void getEnumNameValues(Class<?> clazz, String key , Map<String, String[]> nameMap,
                                          Map<String, String[]> valueMap) {
        if (!clazz.isEnum()) {
            throw new RuntimeException(clazz.getName() + "不是枚举类型");
        }
        Method getName = null;
        Method getValue = null;
        try {
            getName = clazz.getMethod("getName");
            getValue = clazz.getMethod("getValue");
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("枚举类" + clazz.getName() + "没有getName方法");
        }
        //将所有的name, value字段的值放入list
        List<String> nameList = new ArrayList<>();
        List<String> valueList = new ArrayList<>();
        for (Object enumConstant : clazz.getEnumConstants()) {
            try {
                String name = getName.invoke(enumConstant).toString();
                nameList.add(name);
                String value = getValue.invoke(enumConstant).toString();
                valueList.add(value);
            } catch (InvocationTargetException | IllegalAccessException e){
                throw new RuntimeException("枚举类" + clazz.getName() + "没有name或者value字段");
            }
        }
        nameMap.put(key, nameList.toArray(new String[]{}));
        valueMap.put(key, valueList.toArray(new String[]{}));
    }

测试结果

无枚举类

@Data
class AttributeTest {

    @AttrName(key = "a", names = {"已付款", "未付款", "已退款"})
    private String name;

    @AttrValue(key = "a", values = {"0", "1", "-1"})
    private Integer value;

}

结果测试

枚举类版本

@Data
class AttributeTest {

    @AttrName(key = "a", enumClass = SysYesNoEnum.class))
    private String name;

    @AttrValue(key = "a")
    private Integer value;

}

结果测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,我们可以通过指定映射来给对应字段赋值。具体来说,我们可以创建一个Map对象,其中的key-value对分别表示字段名和对应的值。 首先,我们需要确保类的字段名和Map中的key保持一致。然后,可以通过遍历Map的方式,将字段值赋给对应的字段。 例如,假设我们有一个类,其中包含字段name和age,我们想要通过指定映射来给这些字段赋值。我们可以这样做: ```java class Person { String name; int age; } public class Main { public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); map.put("name", "John"); map.put("age", 30); Person person = new Person(); // 遍历map,将值赋给对应字段 for (Map.Entry<String, Object> entry : map.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); switch (key) { case "name": person.name = (String) value; break; case "age": person.age = (int) value; break; // 可以根据需要添加其他字段的赋值逻辑 } } // 输出字段值 System.out.println("Name: " + person.name); System.out.println("Age: " + person.age); } } ``` 在上面的示例中,我们创建了一个Map对象,并将字段名和对应的值放入其中。然后,我们创建了一个Person对象,并使用遍历Map的方式来给字段赋值。最后,我们输出了字段的值,可以看到成功通过指定映射赋值给了对应字段。 需要注意的是,这种通过指定映射赋值的方式需要手动编写逻辑来处理不同字段的赋值操作,如果字段较多,可能会比较繁琐。可以根据实际情况选择是否使用这种方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值