Java教程

关于数据包装的一些想法

本文主要是介绍关于数据包装的一些想法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

最近也是在写后台管理系统,系统中的列表展示总是少不了要展示各种字段,其中包括本表的字段,还有涉及其他表的外键字段,因为在本表中那些外键字段存储的一般是ID,但展示在页面的则需要是名称,所以对这些数据也是需要进行查询

对于这些数据,我就以我的经验来看,大概可以用以下几种方式来进行包装

1、连表查询

这是最简单,直接在sql上进行关联查询,这种方式算是比较普遍的,但这种写法的话就是sql较为臃肿,特别是关联字段比较多的,用这种方式会让后续的维护扩展有一定的难度

2、装饰器模式

第二个就是使用java的设计模式,对返回的数据列表进行包装,以下就是对包装的抽象类,具体就是通过继承这个类来对数据进行操作查询

这种方式主要是通过调用公用方法进行对数据的查询,将连表查询中的外键关联拆分出来,实现一定的解耦,而且对查询数据的复用性更好

public  abstract class BaseControllerWrapper<T> {

    private T t;
    private List<T> list;

    public BaseControllerWrapper(T t){
        this.t=t;
    }

    public BaseControllerWrapper(List<T> list){
        this.list=list;
    }

    public T wrap(){
        if(t!=null){
            this.wrapTheObject(this.t);
            return this.t;
        }
        return null;
    }

    public List<T> wrapList(){
        if(this.list!=null && !this.list.isEmpty()){
            for (T t1 : this.list) {
                this.wrapTheObject(t1);
            }
            return this.list;
        }
        return null;
    }

    public abstract T wrapTheObject(T t);
}

3、 AOP

这个实现方式的话,我的想法就是通过反射去执行的查询方法来达到数据包装的结果。具体实现就是

(1)先定义一个注解,用来标识数据包装

@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataFill {

    //需要包装的字段id
    String[] field() default {};

    //包装对象的实体类全类名
    String[] targetEntity() default {};

    //包装的映射字段
    String[] targetField() default {};

    //忽略包装的字段
    String[] ignoreField() default {};
}

(2) 定义字段包装常量池

(3)写AOP切面

protected void handleDataFill(final JoinPoint joinPoint, Object object) {
        //获取注解
        DataFill dataFill = getAnnotationLog(joinPoint);
        if (dataFill == null) {
            return;
        }
        //从常量池获取的目标实体类
        Map<String, String> targetEntity = new HashMap<>(DataFillConstants.getTargetEntity());
        //从常量池获取的目标映射字段
        Map<String, String> targetField = new HashMap<>(DataFillConstants.getTargetField());
        //忽略字段不进行包装,暂不考虑多线程情况
        if (dataFill.ignoreField().length>0){
            String[] ignoreField = dataFill.ignoreField();
            for (String s : ignoreField) {
                if (targetEntity.containsKey(s) && targetField.containsKey(s)){
                    targetEntity.remove(s);
                    targetField.remove(s);
                }
            }
        }
        //解析  需一一对应
        if (dataFill.field().length > 0
                && dataFill.targetEntity().length == dataFill.field().length
                && dataFill.targetField().length == dataFill.field().length
        ) {
            String[] fields = dataFill.field();
            for (int i = 0; i < fields.length; i++) {
                //若常用包装数据含有,则替换
                targetEntity.put(fields[i], dataFill.targetEntity()[i]);
                targetField.put(fields[i], dataFill.targetField()[i]);
            }
        }
        //TODO 过滤当前主键
        //数据处理
        if (object instanceof com.github.pagehelper.Page) {
            List<Object> list = ((Page) object).getResult();
            List<Map<String, Object>> mapList = BeanUtils.beansToMaps(list);
            for (Map<String, Object> map : mapList) {
                handleData(map, targetEntity, targetField);
            }
        } else if (object instanceof ArrayList) {
            List<Map<String, Object>> mapList = BeanUtils.beansToMaps((List<Object>) object);
            for (Map<String, Object> map : mapList) {
                handleData(map, targetEntity, targetField);
            }
        } else if (object instanceof BaseEntity) {
            Map<String, Object> map = BeanUtils.beanToMap(object);
            handleData(map,targetEntity,targetField);
        }

    }


    private void handleData(Map<String, Object> map, Map<String, String> targetEntity, Map<String, String> targetField) {
        Set<String> entityKey = targetEntity.keySet();
        Iterator<String> iterator = entityKey.iterator();
        //遍历是否包含需要包装的字段
        try {
            while (iterator.hasNext()) {
                String key = iterator.next();
                //是否包含实体类与映射字段
                if (map.containsKey(key) && targetEntity.containsKey(key) && map.get(key) != null) {
                    //指定实体类,读取该包下的Iservice接口中根据id查询的方法
                    //TODO 拆解成动态传入
                    Class<?> clazz = Class.forName(targetEntity.get(key));
                    Object methodData = MethodUtils.getMethodData(clazz, Long.valueOf(map.get(key).toString()));
                    if (methodData == null) {
                        continue;
                    }
                    Map<String, Object> bean = BeanUtils.beanToMap(methodData);
                    map.put(targetField.get(key), bean.get(targetField.get(key)));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private DataFill getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(DataFill.class);
        }
        return null;
    }

(4)通过反射执行方法工具类

大致思路就是这样,这种局限性比较大,固定了包结构与指定方法,不过好处就是写的代码量大大减少,在查询列表的方法直接加个注解就能实现包装,功能再强大一点也是可以的,比如说动态传参、动态方法那些,但我觉得没有必要,太多功能的话,执行逻辑会复杂很多,得不偿失,这个的应用场景只是为关联字段做包装,其他逻辑的就需要再写了

以上的话大概就是我写过的对数据列表的三种包装方式,工作年限不高,资历尚浅,还是需要多多歇息

这篇关于关于数据包装的一些想法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!