当前位置:Java -> 我的ModelMapper速查表

我的ModelMapper速查表

正如标题所说,这篇文章将列举我的 ModelMapper 速查表。它不会提供任何深入教程或花哨的描述,只是一些用例。

本文中使用的模型

每当在代码中看到 UserUserDTOLocationDTO,请参考本节。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String firstName;
    private String lastName;
    private List<String> subscriptions;
    private String country;
    private String city;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
    private String firstName;
    private String secondName;
    private String subscriptions;
    private LocationDTO location;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LocationDTO {
    private String country;
    private String city;
}


基本 typeMap 用法和 addMapping

在需要自定义 映射 时,请使用 typeMap 而不是 createTypeMap

示例说明如何将 lastName 字段映射到 secondName 字段。

public UserDTO convert(User user) {
    return modelMapper.typeMap(User.class, UserDTO.class)
            .addMapping(User::getLastName, UserDTO::setSecondName)
            .map(user);
}


实际的 createTypeMapgetTypeMap 用法

如果使用 createTypeMap,确保使用 getTypeMap。很容易忘记,因为每个人都提供了一个带有 createTypeMap 的示例,但调用两次会抛出异常。

public class CreateTypeMapConverter {

    private final ModelMapper modelMapper;

    public CreateTypeMapConverter(ModelMapper modelMapper) {
        this.modelMapper = modelMapper;
        // can be moved to a configuration class
        var typeMap = modelMapper.createTypeMap(User.class, UserDTO.class);
        typeMap.addMapping(User::getLastName, UserDTO::setSecondName);
    }

    public UserDTO convert(User user) {
        return modelMapper.getTypeMap(User.class, UserDTO.class).map(user);
    }
}


而且总是可以懒惰地执行它。

public UserDTO convert(User user) {
    var typeMap = modelMapper.getTypeMap(User.class, UserDTO.class);
    if (typeMap == null) {
        typeMap = modelMapper.createTypeMap(User.class, UserDTO.class);
        typeMap.addMapping(User::getLastName, UserDTO::setSecondName);
    }
    return typeMap.map(user);
}


为带有转换器的setter添加映射或使用 using

这是一个将 List 转换为 StringtypeMap 示例,在将此值设置给DTO的setter之前。为此,我们使用我们的转换器逻辑调用 using

public UserDTO convertWithUsing(User user) {
    return modelMapper.typeMap(User.class, UserDTO.class)
            .addMappings(mapper -> {
                mapper.map(User::getLastName, UserDTO::setSecondName);
                mapper.using((MappingContext<List<String>, String> ctx) -> ctx.getSource() == null ? null : String.join(",", ctx.getSource()))
                        .map(User::getSubscriptions, UserDTO::setSubscriptions);
            })
            .map(user);
}


同样,对于需要在setter之前进行自定义转换的任何实体都适用。

ModelMapper 不支持在 addMapping 中进行转换

在代码示例中,setter中的值将为空,因此我们应避免使用 addMapping 进行任何转换。

// will throw NPE as o is null
public UserDTO addMappingWithConversion(User user) {
    return modelMapper.typeMap(User.class, UserDTO.class)
            .addMapping(User::getLastName, UserDTO::setSecondName)
            // o is null
            .addMapping(User::getSubscriptions, (UserDTO dest, List<String> o) -> dest.setSubscriptions(String.join(",", o)))
            .map(user);
}


但是ModelMapper支持嵌套setter

countrycity字段映射到嵌套对象LocationDTO的示例。

public UserDTO convertNestedSetter(User user) {
    return modelMapper.typeMap(User.class, UserDTO.class)
            .addMapping(User::getLastName, UserDTO::setSecondName)
            .addMapping(User::getCountry, (UserDTO dest, String v) -> dest.getLocation().setCountry(v))
            .addMapping(User::getCity, (UserDTO dest, String v) -> dest.getLocation().setCity(v))
            .map(user);
}


使用preConverterpostConverter

当左边没有选择余地,或者在需要有条件地更改源或目的地,但使用using很难或不可能时,请使用。

这里是preConverter的一个超级简化示例:

public UserDTO convertWithPreConverter(User user) {
    return modelMapper.typeMap(User.class, UserDTO.class)
            .setPreConverter(context -> {
                context.getSource().setFirstName("Joe");
                return context.getDestination();
            })
            .addMapping(User::getLastName, UserDTO::setSecondName)
            .map(user);
}


相同的逻辑适用于postConverter

public UserDTO convertWithPostConverter(User user) {
    return modelMapper.typeMap(User.class, UserDTO.class)
            .setPostConverter(context -> {
                var location = new LocationDTO(context.getSource().getCountry(), context.getSource().getCity());
                context.getDestination().setLocation(location);
                return context.getDestination();
            })
            .addMapping(User::getLastName, UserDTO::setSecondName)
            .map(user);
}


使用建造者模式

适用于使用建造者模式的不可变实体。

模型

@Data
@Builder
public class UserWithBuilder {
    private final String firstName;
    private final String lastName;
}
@Data
@Builder
public final class UserWithBuilderDTO {
    private final String firstName;
    private final String secondName;
}


转换器

public class BuilderConverter {

    private final ModelMapper modelMapper;

    public BuilderConverter(ModelMapper modelMapper) {
        this.modelMapper = modelMapper;
        var config = modelMapper.getConfiguration().copy()
                .setDestinationNameTransformer(NameTransformers.builder())
                .setDestinationNamingConvention(NamingConventions.builder());
        var typeMap = modelMapper.createTypeMap(UserWithBuilder.class, UserWithBuilderDTO.UserWithBuilderDTOBuilder.class, config);
        typeMap.addMapping(UserWithBuilder::getLastName, UserWithBuilderDTO.UserWithBuilderDTOBuilder::secondName);

    }

    public UserWithBuilderDTO convert(UserWithBuilder user) {
        return modelMapper.getTypeMap(UserWithBuilder.class, UserWithBuilderDTO.UserWithBuilderDTOBuilder.class)
                .map(user).build();
    }
}


但如果所有实体都使用建造者模式,则可以在全局范围内设置配置。

本文的完整源代码可以在GitHub上找到

推荐阅读: 阿里巴巴面经(67)

本文链接: 我的ModelMapper速查表