美文网首页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));
            }
        }
    }
}

相关文章

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

    业务项目中经常有跨表对象或者跨服务的对象,对象之间使用Id关联,但是返回到调用方时,又需要根据id补充完整的关联对...

  • 彻底理解Spring容器和应用上下文

    有了Spring之后,通过依赖注入的方式,我们的业务代码不用自己管理关联对象的生命周期。业务代码只需要按照业务本身...

  • OC底层面试

    关联对象补充 上节课我们在探索关联对象设置流程,在_object_set_associative_referenc...

  • MiniMall:整合Lombok简化接口对象代码

    Lombok是一个用于简化接口对象代码的工具,针对接口对象中的样板代码进行简化。下面看两段代码来体会一下,假设现在...

  • 对象关联

    对象关联 [TOC] 含义 内存图 代码

  • NSRuntime使用篇

    使用总结runtime开源代码 对象的关联: 设置关联值 void objc_setAssociatedObjec...

  • SQL简单教学

    一、简单展示 示例一、两表关联 业务描述:需要将表B补充到表A中。关联的字段为部门编码(dep_no) 处理步骤:...

  • Javascript-基本知识(三)

    Javascript继承 1.原型对象补充: 01 构造函数有一个相关联的原型对象,这个原型对象默认是一个空对象{...

  • OC面试基础(3)— 关联对象AssociationObject

    主要分析在runtime中关联对象操作是如何实现的,数据对象时如何保存的及关联对象的释放。 1、代码举例 给一个已...

  • 2017-03-27 面向对象预习

    原型对象补充 构造函数有一个相关联的原型对象,这个原型对象默认是一个空对象{} 构造函数的原型对象本身是 Obje...

网友评论

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

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