工程经验 - Java 字段映射

1051

背景:最近在写业务的时候,发现有个关于字段映射的流程在很多模块中是很多人都必须要写的,而且每个人写法也不统一,很多流程是重复性的劳动,所以在考虑能否将这种字段映射抽象出来,并统一规范,让大家按照这个流程走

功能:应用于领域模型类,从枚举中自动获取枚举的字段值,支持设置默认值

Ps:由于公司原因,不透露具体的业务应用。以下应用属于个人 YY 的应用场景

一、场景

在社交业务中,每个模块的接口需要带上一个话题字段提交到后端,方便后端拿到这个字段统计话题热度

二、设计

依赖于 Hutool 和 Guava 的一些基础方法

2.1 接口

抽象接口,规范话题枚举的实现

public interface BaseField {
    String getFieldName();
}

2.2 接口领域模型

抽象共同需要的字段到父类,并在其中写自动转换获取枚举字段的抽象

@Data
public abstract class BaseTopicCondition<T> {

    private String topic;

    public static <C extends BaseTopicCondition<?>,
            E extends Enum<E>> void setTopicProperty(Class<E> enumClass,
                                                     C condition,
                                                     boolean defaultNull,
                                                     String defaultFieldName) {
        if (!defaultNull) {
            condition.setTopic(defaultFieldName);
        } else {
            setTopicProperty(enumClass, condition);
        }
    }

    public static <C extends BaseTopicCondition<?>,
            E extends Enum<E>> void setTopicProperty(Class<E> enumClass, C condition) {
        E topicFieldEnum = Enums.getIfPresent(enumClass, condition.getTopic()).orNull();
        Object topicFieldValue = Objects.nonNull(topicFieldEnum) ? 
          ReflectUtil.getFieldValue(topicFieldEnum, TopicPropConstant.TOPIC_FIELD_NAME) : null;
        condition.setTopic(Objects.nonNull(topicFieldValue) ? topicFieldValue.toString() : null);
    }
}

接口领域模型继承基础类,并设置自动获取需要的枚举字段方法

@Data
@EqualsAndHashCode(callSuper = true)
public class ProgramBookTopicCondition extends BaseTopicCondition<ProgramBook> {

    private Integer bookId;
    private Integer userId;

    @Getter
    @AllArgsConstructor
    public enum BookTopicField implements BaseField {
        CPP_TOPIC("cpp_book_topic"),
        JAVA_TOPIC("java_book_topic"),
        RUST_TOPIC("rust_book_topic"),
        OTHER_TOPIC("other_book_topic");
        private final String fieldName;
    }

    public static void setFieldProperty(ProgramBookTopicCondition condition) {
        BaseTopicCondition.setTopicProperty(BookTopicField.class,
                                            condition,
                                            false,
                                            BookTopicField.OTHER_TOPIC.fieldName);
    }
}

2.3 其他实体

编程书籍实体

@Data
public class ProgramBook {
    // 编程书籍实体
}

字段名称常量

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TopicPropConstant {
    public static final String TOPIC_FIELD_NAME = "fieldName";
}

三、总结

由于字段的获取之前写法不一,于是之前被安全组扫出了 SQL 注入漏洞,但方案的缺点是映射的表和实体不支持联表查的结果