如何自定义ModelMapper [英] How to customize ModelMapper

查看:291
本文介绍了如何自定义ModelMapper的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to use ModelMapper to convert entity to DTO and back. Mostly it works, but how do I customize it. It has has so many options that it's hard to figure out where to start. What's best practice?

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

I'll answer it myself below, but if another answer is better I'll accept it.

推荐答案

首先是一些链接

  • modelmapper getting started
  • api doc
  • blog post
  • random code examples

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

My impression of mm is that it is very well engineered. The code is solid and a pleasure to read. However, the documentation is very terse, with very few examples. Also the api is confusing because there seems to be 10 ways to do anything, and no indication of why you’d do it one way or another.

有两种选择:推土机是最受欢迎的,而 Orika 获得了易于使用的良好评价.

There are two alternatives: Dozer is the most popular, and Orika gets good reviews for ease of use.

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

Assuming you still want to use mm, here’s what I’ve learned about it.

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

The main class, ModelMapper, should be a singleton in your app. For me, that meant a @Bean using Spring. It works out of the box for simple cases. For example, suppose you have two classes:

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

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

带有适当的吸气剂/设置剂.您可以这样做:

with appropriate getters/setters. You can do this:

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

,名称"将从dd复制到di.

and the "name" will be copied from dd to di.

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

There are many ways to customize mm, but first you need to understand how it works.

mm对象的每个有序类型对都包含一个TypeMap,例如< DogInfo,DogData>和< DogData,DogInfo>将是两个TypeMap.

The mm object contains a TypeMap for each ordered pair of types, such as <DogInfo, DogData> and <DogData, DogInfo> would be two TypeMaps.

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

Each TypeMap contains a PropertyMap with a list of mappings. So in the example the mm will automatically create a TypeMap<DogData, DogInfo> that contains a PropertyMap that has a single mapping.

我们可以写这个

    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 是否存在,如果尚未为< S创建TypeMap, D>源/目标类型
  2. 调用TypeMap 条件,如果返回FALSE,则不执行任何操作并停止
  3. 如有必要,调用TypeMap Provider 构造一个新的目标对象
  4. 调用TypeMap PreConverter (如果有的话)
  5. 执行以下操作之一:
    • 如果TypeMap具有自定义转换器,则将其命名为
    • 或生成一个 PropertyMap (基于配置标志以及添加的所有自定义映射),并使用它 (请注意:TypeMap还具有可选的自定义Pre/PostPropertyConverters,我认为 将在每个映射之前和之后运行.)
  1. see if the TypeMap exists yet, if not create the TypeMap for the <S, D> source/destination types
  2. call the TypeMap Condition, if it returns FALSE, do nothing and STOP
  3. call the TypeMap Provider to construct a new destination object if necessary
  4. call the TypeMap PreConverter if it has one
  5. do one of the following:
    • if the TypeMap has a custom Converter, call it
    • or, generate a PropertyMap (based on Configuration flags plus any custom mappings that were added), and use it (Note: the TypeMap also has optional custom Pre/PostPropertyConverters that I think will run at this point before and after each mapping.)

注意事项:此流程图为各种文档,但我不得不猜测很多,所以可能并非全部正确!

Caveat: This flowchart is sort of documented but I had to guess a lot, so it might not be all correct!

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

You can customize every single step of this process. But the two most common are

  • 步骤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,则必须写另一个.

Note the converter is one-way. You have to write another if you want to customize DogInfo to 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的参数无关紧要,它只是一个占位符.您可以在这里做很多事情,例如

The pm.configure function is really funky. It’s not actual code. It is dummy EDSL code that gets interpreted somehow. For instance the parameter to the setter is not relevant, it is just a placeholder. You can do lots of stuff in here, such as

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

注意,自定义映射已添加到默认映射,因此您不需要 例如指定

Note the custom mappings are added to the default mappings, so you do not need, for example, to specify

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

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

in your custom PropertyMap.configure().

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

In this example, I had to write a Converter to map Integer to Boolean. In most cases this will not be necessary because mm will automatically convert Integer to String, etc.

有人告诉您,您也可以使用Java 8 lambda 表达式创建映射.我试过了,但我想不通.

I'm told you can also create mappings using Java 8 lambda expressions. I tried, but I could not figure it out.

最终建议和最佳做法

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

By default mm uses MatchingStrategies.STANDARD which is dangerous. It can easily choose the wrong mapping and cause strange, hard to find bugs. And what if next year someone else adds a new column to the database? So don't do it. Make sure you use STRICT mode:

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

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

Always write unit tests and ensure that all mappings are validated.

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

使用mm.addMappings()修复所有验证失败,如上所示.

Fix any validation failures with mm.addMappings() as shown above.

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

Put all your mappings in a central place, where the mm singleton is created.

这篇关于如何自定义ModelMapper的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆