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 DTO
        PostDto postResponse = modelMapper.map(post, PostDto.class);

        return ResponseEntity.ok().body(postResponse);

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

@SpringBootApplication
public class SpringbootBlogApiApplication {

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

实体和DTO双向转换:

// convert DTO to entity
        Post postRequest = modelMapper.map(postDto, Post.class);

        Post post = postService.createPost(postRequest);

        // convert entity to DTO
        PostDto 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_脚本之家

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023数字科技生态大会-数字安全论坛 学习笔记

监管合规->价值创造的方向&#xff0c;在安全领域的发展方向-安全运营服务型 ICT->数字->数据 数字安全&#xff1a;网络安全数据安全 传统信息化以计算为核心&#xff0c;数字化以数据为核心 数字安全技术发展十大趋势&#xff1a; 一、 数字安全技术政策环境将不…

问题 C: Oil Deposits(DFS+类并查集)

核心部分&#xff1a; &#xff08;发现时继续深度搜索&#xff0c;同时将该->改为 *&#xff08;即田改为空&#xff09;&#xff09; &#xff08;使所有相连的的改为空&#xff09; 相当于统计了一个集合 统计进行DFS的次数&#xff08;即为集合个数&#xff09;&…

一站式解决文件上传与管理难题,JVS低代码平台助力企业数字化升级

在数字化时代&#xff0c;文件上传与管理功能已成为各类应用程序的标配。为了满足用户在不同场景下的多样化需求&#xff0c;JVS低代码表单引擎中配置了灵活的文件上传组件。通过该组件&#xff0c;用户可以轻松实现文件的上传、管理和查看&#xff0c;同时还能够根据具体需求进…

常用网络命令ping、arp、tracert、route的详细用法,弄懂立马成大神

你们好&#xff0c;我的网工朋友。 做网工&#xff0c;你遇到最多的问题是啥&#xff1f;大部分人的回答都是网络故障吧。 所以有很多朋友多次问到&#xff0c;如何检测网络故障和对网络进行基础的操作。比如&#xff1a;网络经常掉线、网络时好时坏&#xff0c;ip地址冲突、…

芯片设计工程师必备基本功——《设计与验证:Verilog HDL》

Verilog HDL 作为两大硬件描述语言之一&#xff0c;拥有很大的用户群。据调查&#xff0c;目前美国有 90%左右的 IC 设计人员使用 Verilog. 在中国&#xff0c;大概再 50% 左右的人在使用 Verilog 。 大量高校毕业生和部分软件设计人员正在不断涌入这个领域。要想尽快在 IC设计…

VsCode 安装 GitHub Copilot插件 (最新)

##在线安装&#xff1a; 打开Vscode扩展商店&#xff0c;输入 "GitHub Copilot " ,选择下载人数最多的那个。&#xff08;这个是你写一部分代码或者注释&#xff0c;Ai自动帮你提示/补全代码&#xff09;,建议选择这个 注意下面有个和他类似的 "GitHub Copilo…

工业网关贴牌厂家有哪些?工业网关OEM厂家怎么选?

随着物联网技术的不断发展&#xff0c;市面上不断涌现出各种工业物联网厂商。中小型物联网企业苦于有技术&#xff0c;但是生产能力受限&#xff0c;需要寻找OEM代工厂家。但是在如何选择OEM代工厂家时又犯了难。工业网关类产品属于技术密集型产品&#xff0c;对厂家的生产能力…

微信开发者工具如何使用

首先是下载微信开发者工具 链接: https://pan.baidu.com/s/1ri-fRCUQsz9vLxbTqtdPUQ 提取码: 8rhn 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 安装完打开就是以下界面 接下来进入正题 第一步新建或导入 新建 获取AppID 第一步&#xff1a;通过微信公众平…

线性回归,核技巧和线性核

在这篇文章中&#xff0c;我想展示一个有趣的结果&#xff1a;线性回归与无正则化的线性核ridge回归是等 价的。 这里实际上涉及到很多概念和技术&#xff0c;所以我们将逐一介绍&#xff0c;最后用它们来解释这个说法。 首先我们回顾经典的线性回归。然后我将解释什么是核函…

聊聊leetcode可包含重复数字的序列的《47. 全排列 II》中的vis标记函数

1 题目描述&#xff08;字节二面题目&#xff09; 2 代码 class Solution {List<List<Integer>>res;List<Integer>list;boolean[]used;public List<List<Integer>> permuteUnique(int[] nums) {resnew ArrayList<>();listnew ArrayList&l…

安装google浏览器报错

安装google浏览器报错 dpkg: error processing package google-chrome-stable (–install): dependency problems - leaving unconfigured Processing triggers for gnome-menus (3.36.0-1ubuntu1) … Processing triggers for desktop-file-utils (0.24-1ubuntu3) … Processi…

仓库管理系统(WMS)升级解决方案—条码引入

在企业的整个供应链中&#xff0c;仓储起着至关重要的作用&#xff0c;如果不能保证正确的进货和库存控制及发货&#xff0c;将会导致管理费用的增加&#xff0c;服务质量难以得到保证&#xff0c;从而影响企业的竞争力。 传统简单、静态的仓库管理通常以结果为导向&#xff0…

几百封钓鱼邮件如何分析?一个简单的方法告诉你!

前几天的时候收到一批钓鱼邮件需要分析&#xff0c;打开一看就傻了眼&#xff0c;大概有几百封&#xff0c;而且基本上每一封都是钓鱼邮件&#xff0c;第一反应是很崩溃&#xff0c;这么多如何分析&#xff1f;但是客户那边又着急要&#xff0c;那只能先上了&#xff1a; 一、…

做一个Springboot文章分类模块

目录 文章分类 1、新增文章分类 前言 代码编写 测试 2、 文章分类列表 前言 代码编写 测试 3、获取文章列表详情 前言 代码实现 测试 4、更新文章分类 前言 代码实现 测试 5、删除文章分类 前言 代码实现 测试 分页查询 文章列表条件分页 前言 代码编…

USB拦截工具

USB 闪存驱动器对组织的安全和数据构成了独特的威胁。它们的便携性和充足的存储容量使它们成为数据盗窃的便捷媒介。 什么是 USB 拦截器 USB&#xff08;通用串行总线&#xff09;阻止程序用于禁用插入可移动存储设备的端口&#xff0c;便携性和充足的存储容量使 USB 成为可能…

深度学习 机器视觉 人脸识别系统 - opencv python 计算机竞赛

文章目录 0 前言1 机器学习-人脸识别过程人脸检测人脸对其人脸特征向量化人脸识别 2 深度学习-人脸识别过程人脸检测人脸识别Metric Larning 3 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 人脸识别系统 该项目…

C++学习第三十七天----第十章--对象和类

10.2.2 C中的类 类是一种将抽象转换未用户定义类型的C工具&#xff0c;它将数据表示和操作数据的方法合成一个整洁的包。 接口&#xff1a;一个共享框架&#xff0c;供两个系统交互时使用。 1.访问控制 使用类对象的程序可以直接访问类的公有部分&#xff0c;但只能通过公有…

考研的风吹到你了吗?中国人民大学与加拿大女王大学金融硕士为你提供另一读研途径

24考研的风吹到你了吗&#xff1f;随着社会的不断发展&#xff0c;越来越多的人选择继续深造&#xff0c;通过考研来提升自己的学历和能力。然而&#xff0c;考研并不是一件容易的事情&#xff0c;需要付出大量的时间和精力。面对国内竞争激烈的考研环境&#xff0c;许多人会选…

OpenHarmony worker详解

一&#xff0c;定义 worker是与主线程并行的独立线程。创建Worker的线程被称为宿主线程&#xff0c;Worker工作的线程被称为Worker线程。创建Worker时传入的脚本文件在Worker线程中执行&#xff0c;通常在Worker线程中处理耗时的操作&#xff0c;需要注意的是&#xff0c;Work…

Jenkins的介绍与相关配置

Jenkins的介绍与配置 一.CI/CD介绍 &#xff11;.CI/CD概念 ①CI 中文意思是持续集成 (Continuous Integration, CI) 是一种软件开发流程&#xff0c;核心思想是在代码库中的每个提交都通过自动化的构建和测试流程进行验证。这种方法可以帮助团队更加频繁地交付软件&#x…