SpringBoot项目中ModelMapper配置以及使用

这里总结一下ModelMapper的使用方式,供大家参考

前言

        项目中对象与对象赋值转换使用的频率非常的高,比如数据库表实体对象(Entity)与业务类对象(Model)之间的赋值传递,或者模型对象(Model)与视图对象(ViewModel)之间的赋值传递。

        如果我们一个一个字段的赋值,将是非常繁琐并且毫无价值的重复工作,此时虽然我们可以自己通过反射提取个公共的方法来处理,但是更高效的方式是查看是否有第三方已经提供了比较成熟稳定的工具包,避免重复造轮子的工作。

        在C#中我们一般使用AutoMapper作为对象转换工具(AutoMapper配置使用参考:https://blog.csdn.net/fly_duck/article/details/102605046)。

        Java中也有类似的转换工具,比如:Dozer,Orika,MapStruct,JMapper,ModelMapper以及BeanUtils等,都可以实现对象之间的转换。 本问主要介绍SpringBoot项目中ModelMapper的配置以及使用,使用ModelMapper我们可以快速的解决对象与对象之间的映射转换。官方的介绍如下:

The goal of ModelMapper is to make object mapping easy, by automatically determining how one object model maps to another, based on conventions, in the same way that a human would - while providing a simple, refactoring-safe API for handling specific use cases.

        ModelMapper的Github地址: https://github.com/modelmapper/modelmapper,官方地址:http://modelmapper.org/,官方使用文档:http://modelmapper.org/user-manual/。

pom.xml配置

首先在pom.xml引入ModelMapper依赖:

        <!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>2.3.9</version>
        </dependency>

 ModelMapper配置

        在SpringBoot项目中将ModelMapper配置好后注册为JavaBean交给Spring来管理,使用的时候直接通过注解获取ModelMapper实例来对对象进行转换。

        项目中强烈建议将setFullTypeMatchingRequired设置为true使用完全匹配模式,将setMatchingStrategy设置为MatchingStrategies.STRICT使用严格匹配模式,避免字段名缺失被相似字段转换错误的情况。

package com.flyduck.mybatis.config;
 
import com.flyduck.mybatis.entity.User;
import com.flyduck.mybatis.model.UserModel;
import org.modelmapper.AbstractConverter;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import java.text.SimpleDateFormat;
import java.util.Date;
 
@Configuration
public class ModelMapperConfig {
 
    private Converter<Date, String> dateToStringConverter = new AbstractConverter<Date, String>() {
        @Override
        protected String convert(Date date) {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
            return date == null ? null : simpleDateFormat.format(date);
        }
    };
 
    /**
     * 将ModelMapper注册到Spring中
     * 可以添加一些定制化的配置,官方文档:http://modelmapper.org/user-manual/property-mapping/
     *
     * @author flyduck
     * @date 2020/11/25 21:35
     * @param []
     * @return org.modelmapper.ModelMapper
    */
    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();
 
        // 官方配置说明: http://modelmapper.org/user-manual/configuration/
        // 完全匹配
        modelMapper.getConfiguration().setFullTypeMatchingRequired(true);
 
        // 匹配策略使用严格模式
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
 
        modelMapper.addConverter(dateToStringConverter);
 
        configureUser(modelMapper);

 
        return modelMapper;
    }
 
    private void configureUser(ModelMapper modelMapper) {
        modelMapper.typeMap(UserModel.class, User.class)
                .addMappings(mapper -> mapper.skip(User::setPassword))
                .addMappings(mapper -> mapper.skip(User::setCreateTime))
                .addMappings(mapper -> mapper.skip(User::setUpdateTime))
                .addMappings(mapper -> mapper.map(UserModel::getName, User::setRealName));
 
        modelMapper.typeMap(User.class, UserModel.class)
                .addMappings(mapper -> mapper.skip(UserModel::setPassword))
                .addMappings(mapper -> mapper.map(User::getRealName, UserModel::setName));

//                .addMappings(mapper -> mapper.using(dateToStringConverter).map(User::getCreateTime, UserModel::setCreateTime))
//                .addMappings(mapper -> mapper.using(dateToStringConverter).map(User::getUpdateTime, UserModel::setUpdateTime));
    }
}

        默认情况下,ModelMapper会通过反射按照字段名自动进行对象转换。

        但是一些特殊情况,需要我们自定义配置,示例配置中我们单独为User和UserModel类之间的转换做了一些常用的配置。 当某个字段不需要转换可以通过skip来设置,当字段名不一致可以通过map来设置,当字段值需要处理后再转换可以通过Converter结合map来设置

配置2

ModelMapperConfig.java
@Configuration
public class ModelMapperConfig {
    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.typeMap(Order.class, OrderDTO.class).addMappings(mapper -> {
            mapper.map(src -> src.getBillingAddress().getStreet(),
                    Destination::setBillingStreet);
            mapper.map(src -> src.getBillingAddress().getCity(),
                    Destination::setBillingCity);
        });
        return modelMapper;
    }
}
 

MatchResult匹配模式

        MatchResult有三种结果:FULL、PARTIAL和NONE(即全部匹配,部分匹配和不匹配)。

        注意,这里有一个部分匹配,也就是容易踩到的坑。在对like进行匹配时,likeNum就被定义为部分匹配。因此,当likeNum大于2时,就不能被转换成boolean类型。

        这里解决方法有两种,一种是在设置中,规定必须字段名完全匹配;另一种就是将匹配策略定义为严格。

设置方法如下:

modelMapper.getConfiguration().setFullTypeMatchingRequired(true); modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);

例子1

User类的定义

public class User implements Serializable {
    private Long id;
    private String code;
    private Date createTime;
    private Date updateTime;
    private Boolean isDelete;
    private String userName;
    private String email;
    private String phoneNumber;
    private String password;
    private String realName;
    private String nickName;
    private String avatar;
 
    // 省略getter, setter方法
}
UserModel的定义:

public class UserModel {
    private Long id;
    private String createTime;
    private String updateTime;
    private String userName;
    private String password;
    private String email;
    private String phoneNumber;
    private String name;
    private String nickName;
    private String avatar;
 
    // 省略 getter,setter方法
}

对象转换

        在Controller控制器中,通过@Autowired注解得到ModelMapper实例,然后直接通过modelMapper.map(user, UserModel.class)转换得到VO层的UserModel对象,然后直接传给前端接口。

@RestController
@RequestMapping("user")
public class UserController {
 
    @Autowired
    private UserMapper userMapper;
 
    @Autowired
    private ModelMapper modelMapper;
 
    @GetMapping("{id}")
    public UserModel get(@PathVariable Long id) {
        User user  = userMapper.selectByPrimaryKey(id);
 
        if (user == null || user.getIsDelete()) {
            return null;
        }
 
        UserModel userModel = modelMapper.map(user, UserModel.class);
 
        return userModel;
    }
}

例子2

假设JPA Entity:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "posts", uniqueConstraints = {@UniqueConstraint(columnNames = {"title"})})
public class Post {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "title")private String title;@Column(name = "description")private String description;@Column(name = "content")private String content;
}


定义DTO对象:PostDto.java

@Data
public class PostDto {private long id;private String title;private String description;private String content;
}

不使用ModelMapper库的代码,需要手工一个个字段转:

    Post post = postRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Post", "id", id));post.setTitle(postRequest.getTitle());post.setDescription(postRequest.getDescription());post.setContent(postRequest.getContent());return postRepository.save(post);

使用ModelMapper库的代码:

    Post post = postService.getPostById(id);// convert entity to DTOPostDto postResponse = modelMapper.map(post, PostDto.class);return ResponseEntity.ok().body(postResponse);

注意的是:modelMapper需要在配置为一个Bean才能被注入使用:

@SpringBootApplication
public class SpringbootBlogApiApplication {@Beanpublic ModelMapper modelMapper() {return new ModelMapper();}public static void main(String args) {SpringApplication.run(SpringbootBlogApiApplication.class, args);}
}

实体和DTO双向转换:

// convert DTO to entityPost postRequest = modelMapper.map(postDto, Post.class);Post post = postService.createPost(postRequest);// convert entity to DTOPostDto postResponse = modelMapper.map(post, PostDto.class);

总结

        以上就是SpringBoot项目中ModelMapper的常规使用,示例源码:https://gitee.com/flyduck128/springboot-demo/tree/master/flyduck-mybatis。

        使用ModelMapper可以大大提高开发人员对于对象转换的开发效率,特别适用于微服务中对象之间的转换。对于ModelMapper的详细配置以及高级使用可以参考官方文档。

        就如开篇所说的Java中对象转换的工具很多,ModelMapper常规使用配置简单,上手容易,同时也提供一些高级用法,同时由于使用的反射进行转换所以效率不是很高,对于效率要求不高的项目推荐使用ModelMapper。

拓展

        国外朋友专门对Dozer,Orika,MapStruct,JMapper和ModelMapper进行了性能测试,测试地址:https://www.baeldung.com/java-performance-mapping-frameworks。

测试结果为JMapper和MapStruct性能表现最好。

参考

SpringBoot项目中ModelMapper配置以及使用_springboot modelmapper-CSDN博客

Java中ModelMapper 的高级使用_java_脚本之家

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/174245.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaEE初阶(18)(JVM简介:发展史,运行流程、类加载:类加载的基本流程,双亲委派模型、垃圾回收相关:死亡对象的判断算法,垃圾回收算法,垃圾收集器)

接上次博客&#xff1a;初阶JavaEE&#xff08;17&#xff09;Linux 基本使用和 web 程序部署-CSDN博客 目录 JVM 简介 JVM 发展史 JVM 运行流程 JVM的内存区域划分 JVM 执行流程 堆 堆的作用 JVM参数设置 堆的组成 垃圾回收 堆内存管理 类加载 类加载的基本流…

【开源】基于Vue和SpringBoot的校园失物招领管理系统

项目编号&#xff1a; S 006 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S006&#xff0c;文末获取源码。} 项目编号&#xff1a;S006&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系…

pcl opencv关于flann的冲突:flann_algorithm_t等

问题如下&#xff1a; 引起问题的点&#xff1a; 解决方法&#xff1a;先include pcl后include opencv; 其他解决方式是在环境变量中将pcl置于opencv前面&#xff0c;但是这里如果是先include opencv&#xff0c;后include pcl问题得不到解决&#xff1b;

临床决策分析(DCA)演示APP:理解DCA分析

临床决策分析&#xff08;DCA&#xff09;演示APP&#xff1a;理解DCA分析 之前讨论了DCA分析的分析过程和作用&#xff0c;认为其最主要的作用是确定预测模型的决策阈值&#xff0c;从而促进预测模型与临床的结合。DCA的影响不止于此&#xff0c;在DCA分析中&#xff0c;预测…

关于Android Studio 同步Gradle失败的解决方案

&#xff08;1&#xff09;打开Android Studio的Settings找到Gradle的目录 &#xff08;2&#xff09;打开本地文件目录&#xff0c;找到对应的gradle版本&#xff0c;可以通过Index of /gradle/ 下载gradle压缩包。把目录中gradle-7.0.2-bin\一堆字符\ 下 的.lck 和.part文…

uniapp+uviewPlus+vue3+ts+pinia+vite+echarts 开发基础模板,开箱即用,非常顺手

github仓库地址&#xff1a;https://github.com/Sjj1024/uniapp-vue3 使用 uniapp vue3 ts pinia vite echarts 开发基础模板&#xff0c;拿来即可使用&#xff0c;不要删除 yarn.lock 文件&#xff0c;否则会启动报错&#xff0c;这个可能和 pinia 的版本有关&#xff0…

Docker安装Octoprint 3D打印控制软件

Octoprint简介 Octoprint是一个运行在Linux系统上的开源套件&#xff0c;可以为普通的3D打印机添加强大的外围管理功能。 web管理界面远程操控摄像头实时监控视频录制、延时摄影在线切片图形化的温度曲线显示手机监控操作免SD卡和U盘通过插件和USB/GPIO接口实现更多功能 Oct…

mp4视频批量截取!!!

mp4视频批量截取&#xff01;&#xff01;&#xff01; 问题&#xff1a;如果我们想截取一个mp4视频中的多个片段&#xff0c;一个一个截会很麻烦&#xff01; 可以将想要截取的开始时间和结束时间保存到 excel表 中&#xff0c;进行批量截取。 1、对一个视频&#xff0c;记…

搬家快递服务预约小程序的作用是什么

无论家庭还是企业办公&#xff0c;不少人都有搬家快递服务需求&#xff0c;尤其是近些年类似服务市场需求规模增长迅速。而在实际经营中&#xff0c;行业商家从业者也面临一些经营难题&#xff1a; 搬家公司的服务一般主要针对同省用户&#xff0c;同城需求较高&#xff0c;然…

Synchronized实现原理

Synchronized 三种锁类型&#xff08;本质上都是依赖对象来锁&#xff09; this锁&#xff1a;当前实例锁,比如在方法里面通过锁住this class锁&#xff1a;类对象锁 Object锁&#xff1a;对象实例锁前面我们提到synchronized是依赖于对象的对象头中的Monitor来实现的锁功能&a…

matlab 多自由度的车辆垂向振动模型 车辆平稳性研究

1、内容简介 略 17-可以交流、咨询、答疑 多自由度的车辆垂向振动模型 多自由度的车辆垂向振动模型&#xff0c;包含四分之一车体模型、半车模型和整车模型 垂向振动模型、四分之一车体模型、半车模型和整车模型 2、内容说明 略 3、仿真分析 略 4、参考论文 略 链接&…

【Linux网络】ssh服务与配置,实现安全的密钥对免密登录

目录 一、SSH基础 1、什么是ssh服务器 2、对比一下ssh协议与telnet协议 3、常见的底层为ssh协议的软件&#xff1a; 4、拓展 二、SSH软件学习 1、ssh服务软件学习 2、sshd公钥传输的原理&#xff1a; 3、ssh命令学习&#xff1a; 4、学习解读sshd服务配置文件&#x…