美文网首页Java基础
简化补充关联对象的业务代码

简化补充关联对象的业务代码

作者: 十毛tenmao | 来源:发表于2019-11-25 23:46 被阅读0次

    业务项目中经常有跨表对象或者跨服务的对象,对象之间使用Id关联,但是返回到调用方时,又需要根据id补充完整的关联对象。这种模式非常常用,所以写了一个工具类,简化了这个步骤

    场景描述

    问题中有分类信息,但是默认保存在库表中的只有分类Id(categoryId),但是接口返回给调用方的时候,需要补充完整的Category信息

    • Question.java
    @Data
    public class Question {
        private Integer id;
        /**
         * 分类ID.
         */
        private Integer categoryId;
    
        private Category category;
    
        /**
         * 问题.
         */
        private String question;
    }
    
    • Category.java
    @Data
    public class Category {
        private Integer id;
        private String name;
    }
    

    默认情况下,从数据库中查询问题列表时,只有categoryId,没有CategoryCategory需要根据id再进行远程调用获取(有的是可以通过联表查询)

    • 使用方式
    SupplementUtil<Question, Integer, Category> supplement = SupplementUtil.<Question, Integer, Category>builder()
            .idProviderForTarget(Question::getCategoryId)
            .idProviderForObj(Category::getId)
            .objSetter(Question::setCategory)
            .build();
    supplement.supplementWithConverter(questions, categoryManager::getByList);
    

    使用这种方式可以减少很多胶水代码

    SupplementUtil定义

    • SupplementUtil.java
    /**
     * 对象补充器.
     * 通过Target中的ID,填充ID对应的对象Obj到Target
     *
     * @param <Target> 被补充的目标对象
     * @param <Id>     补充对象的ID
     * @param <Obj>    补充的对象
     * @author tenmao
     * @since 2019/11/13
     */
    @Builder
    public class SupplementUtil<Target, Id, Obj> {
        /**
         * Target中获取Obj的Id的方法.
         */
        @NonNull
        private Function<Target, Id> idProviderForTarget;
    
        /**
         * Obj中获取Id的方法.
         */
        @NonNull
        private Function<Obj, Id> idProviderForObj;
    
        /**
         * Target中设置Obj的方法.
         */
        @NonNull
        private BiConsumer<Target, Obj> objSetter;
    
        /**
         * 是否要求ID对应的Obj一定存在.
         */
        private boolean requireExists = true;
    
        /**
         * 如果ID赌赢的Obj不存在,则使用该默认值.
         */
        private Function<Id, Obj> defaultObj = null;
    
        /**
         * 使用ID到Obj的转换器来补充目标对象Targets
         *
         * @param targets   目标对象
         * @param converter ID到Obj的转换器
         */
        public void supplementWithConverter(List<Target> targets, Function<List<Id>, List<Obj>> converter) {
            //获取所有Id
            List<Id> ids = targets.stream().map(idProviderForTarget).filter(Objects::nonNull).collect(Collectors.toList());
            if (ids.isEmpty()) {
                return;
            }
    
            //获取Id列表对应的Obj列表,并转换为Map
            Map<Id, Obj> objMap = converter.apply(ids).stream().collect(Collectors.toMap(idProviderForObj, Function.identity()));
    
            supplementWithMap(targets, objMap);
        }
    
        /**
         * 使用ID到Obj的Map来补充目标对象Targets
         *
         * @param targets 目标对象
         * @param objMap  ID到Obj的Map
         */
        public void supplementWithMap(List<Target> targets, Map<Id, Obj> objMap) {
            batchSupplement(targets, idProviderForTarget, objSetter, objMap, requireExists, defaultObj);
        }
    
        /**
         * 批量根据ID扩充对象信息.
         *
         * @param targets       扩充对象的目标对象
         * @param idProvider    ID提供方法
         * @param objSetter     对象设置器
         * @param objMap        ID到对象的Map
         * @param requireExists 是否要求必须(如果是,但是没有存在,则会抛出异常{@link IllegalArgumentException})
         * @param defaultObj    如果没有匹配到则使用默认值
         * @param <Target>      扩充对象的目标对象
         * @param <Id>          ID
         * @param <Obj>         ID对应的对象
         */
        public static <Target, Id, Obj> void batchSupplement(List<Target> targets,
                                                             Function<Target, Id> idProvider,
                                                             BiConsumer<Target, Obj> objSetter,
                                                             Map<Id, Obj> objMap,
                                                             boolean requireExists,
                                                             Function<Id, Obj> defaultObj) {
            if (targets == null || idProvider == null || objSetter == null || objMap == null) {
                throw new IllegalArgumentException("parameter for supplement should not be empty");
            }
            for (Target target : targets) {
                Id id = idProvider.apply(target);
                if (id != null) {
                    Obj o = objMap.get(id);
                    if (o == null && requireExists) {
                        throw new IllegalArgumentException(String.format("id:%s cannot convert to object", id));
                    }
                    objSetter.accept(target, o != null ? o : defaultObj.apply(id));
                }
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:简化补充关联对象的业务代码

        本文链接:https://www.haomeiwen.com/subject/fstswctx.html