当前位置: 首页 > 面试题库 >

如何自定义ModelMapper

吴浩皛
2023-03-14
问题内容

我想使用ModelMapper将实体转换为DTO并返回。通常,它可以工作,但是如何自定义它。它有太多选择,因此很难弄清楚从哪里开始。最佳做法是什么?

我会在下面亲自回答,但如果另一个答案更好,我会接受。


问题答案:

首先这是一些链接

  • modelmapper入门
  • API文档
  • 博客文章
  • 随机代码示例

我对mm的印象是它的设计很好。该代码很扎实,阅读起来很愉快。但是,该文档非常简洁,仅包含很少的示例。api也令人困惑,因为似乎有十种方法可以执行任何操作,而没有迹象表明您为什么会以一种或另一种方式进行操作。

有两种选择:推土机是最受欢迎的,而Orika在易用性方面得到了很好的评价。

假设您仍然想使用mm,这就是我所学到的。

主要类,ModelMapper应该是您应用中的一个单例。对我来说,这意味着使用Spring的@Bean。对于简单的情况,它开箱即用。例如,假设您有两个类:

class DogData
{
    private String name;
    private int mass;
}

class DogInfo
{
    private String name;
    private boolean large;
}

与适当的吸气剂/设置者。你可以这样做:

    ModelMapper mm = new ModelMapper();
    DogData dd = new DogData();
    dd.setName("fido");
    dd.setMass(70);
    DogInfo di = mm.map(dd, DogInfo.class);

并且“名称”将从dd复制到di。

自定义mm的方法有很多,但是首先您需要了解它的工作方式。

mm对象的每个有序类型对都包含一个TypeMap,例如 将是两个TypeMap。

每个 TypeMap都 包含一个带有映射列表的 PropertyMap 。因此,在示例中,mm将自动创建一个TypeMap
,其中包含具有单个映射的PropertyMap。

我们可以写这个

    TypeMap<DogData, DogInfo> tm = mm.getTypeMap(DogData.class, DogInfo.class);
    List<Mapping> list = tm.getMappings();
    for (Mapping m : list)
    {
        System.out.println(m);
    }

它会输出

PropertyMapping[DogData.name -> DogInfo.name]

调用 mm.map() 就是这样做的,

  1. 查看 TypeMap是否 存在,如果不存在,则为 源/目标类型创建TypeMap。
  2. 调用TypeMap Condition ,如果它返回FALSE,则不执行任何操作并停止
  3. 如有必要,调用TypeMap Provider 构造一个新的目标对象
  4. 如果TypeMap PreConverter 有一个,则调用它
  5. 请执行以下任一操作:
    • 如果TypeMap具有 自定义Converter ,则将其命名
    • 或者,生成一个 PropertyMap (基于 配置标志 以及添加的所有 自定义映射 )并使用它(注意:TypeMap还具有可选的自定义Pre / PostPropertyConverters, 我认为这 将在 每个映射 之前和之后运行。)
  6. 调用TypeMap PostConverter( 如果有一个)

警告:此流程图已记录在案,但我不得不猜测很多,因此它可能并不完全正确!

您可以自定义此过程的 每个步骤 。但是最常见的两个是

  • 步骤5a。–编写自定义TypeMap转换器,或
  • 步骤5b。–编写自定义属性映射。

这是 自定义TypeMap转换器 的示例:

    Converter<DogData, DogInfo> myConverter = new Converter<DogData, DogInfo>()
    {
        public DogInfo convert(MappingContext<DogData, DogInfo> context)
        {
            DogData s = context.getSource();
            DogInfo d = context.getDestination();
            d.setName(s.getName());
            d.setLarge(s.getMass() > 25);
            return d;
        }
    };

    mm.addConverter(myConverter);

注意 转换器是 单向的 。如果要自定义DogInfo到DogData,则必须编写另一个。

这是 自定义PropertyMap 的示例:

    Converter<Integer, Boolean> convertMassToLarge = new Converter<Integer, Boolean>()
    {
        public Boolean convert(MappingContext<Integer, Boolean> context)
        {
            // If the dog weighs more than 25, then it must be large
            return context.getSource() > 25;
        }
    };

    PropertyMap<DogData, DogInfo> mymap = new PropertyMap<DogData, DogInfo>()
    {
        protected void configure()
        {
            // Note: this is not normal code. It is "EDSL" so don't get confused
            map(source.getName()).setName(null);
            using(convertMassToLarge).map(source.getMass()).setLarge(false);
        }
    };

    mm.addMappings(mymap);

pm.configure函数确实很时髦。这不是实际的代码。伪EDSL代码以某种方式被解释。例如,setter的参数无关紧要,它只是一个占位符。您可以在这里做很多事情,例如

  • when(condition).map(getter).setter
  • when(condition).skip()。setter –安全地忽略字段。
  • using(converter).map(getter).setter –自定义字段转换器
  • with(provider).map(getter).setter –自定义字段构造函数

注意: 自定义映射 添加 到默认的映射,这样你就 不会 需要,例如,指定

            map(source.getName()).setName(null);

在您的自定义PropertyMap.configure()中。

在此示例中,我必须编写一个 Converter
以将Integer映射到Boolean。在大多数情况下,这是没有必要的,因为mm会自动将Integer转换为String等。

有人告诉您您也可以使用Java 8 lambda表达式创建映射。我尝试过,但我不知道。

最终建议和最佳实践

默认情况下,mm使用MatchingStrategies.STANDARD是危险的。它很容易选择错误的映射并引起奇怪的,难以发现的错误。如果明年有人将新列添加到数据库中怎么办?所以不要这样做。确保使用STRICT模式:

    mm.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

始终编​​写单元测试,并确保所有映射都经过验证。

    DogInfo di = mm.map(dd, DogInfo.class);
    mm.validate();   // make sure nothing in the destination is accidentally skipped

修复所有验证失败,mm.addMappings()如下所示。

将所有映射放置在创建mm单例的中心位置。



 类似资料:
  • 主要内容:前记,1.自定义视图,2.自定义异常,3.自定义异常的原理前记 在前面的文章中, 表示了视图解析的原理和异常解析器的解析原理。 这篇通过如何自定义视图和自定义异常处理和自定义异常处理的原理进行说明。 这里说明一下, 自定义的视图和自定义的异常都是会代替容器默认的组件的, 异常还好说, 就是不符合就抛, 视图的话需要注意一下优先级, 可以在自定义的视图解析器上加上注解。 1.自定义视图 这里原理就是添加一个视图和视图解析器, 然后放入容器中, 最后访问相应

  • 本文向大家介绍如何自定义filter?相关面试题,主要包含被问及如何自定义filter?时的应答技巧和注意事项,需要的朋友参考一下 在模块下挂在一个filter()方法,第一个参数传入过滤器的名字,第二个参数是回调函数,处理过滤方法的详细内容,最后返回结果,这样外部就可以根据过滤器的名字调用了

  • 我想创建一个自定义的矩形进度条与白色背景色。进度条中有一个文本居中,用于定义进度条的高度。还有另一个黑色背景色的视图,其宽度从左侧开始增长,具体取决于进度。我有这个,但不起作用: 编辑:好的,我有这个: 唯一的问题是:如何告诉LinearLayout与TextView一样高?

  • 我有多个web api项目(微服务),我想只使用一个swagger-ui链接来公开它们。为了撰写本文,我将每个web api项目称为EndpointA和EndpointB。 我创建了一个swagger-ui项目,并将每个endpoint添加到该项目中。 我的swagger-ui项目 我还尝试使用直接在每个endpoint中设置前缀,但我不知道如何设置。

  • TensorFlow GraphDef based models (typically created via the Python API) may be saved in one of following formats: TensorFlow SavedModel Frozen Model Session Bundle Tensorflow Hub module All of above f

  • 问题内容: 我们使用log4j 1.2.x登录我们的产品,并希望在不久的将来迁移到log4j2.x。我们已实现的功能之一是将系统信息和其他重要参数记录在生成的每个新翻转日志文件上。在log4j 1.2.x中实现的方式是扩展了log4j类并覆盖了该方法,下面是实现的部分代码段 现在,当我们要迁移到log4j2时,我们正在寻找一种实现相同功能的新解决方案。但是,正如我看到的log4j2的源代码与旧的源