SpringBoot项目中ModelMapper配置以及使用

news2024/11/24 17:32:54

这里总结一下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_脚本之家

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

报错:Parsing error: No Babel config file detected...的解决方案

报错&#xff1a;Parsing error: No Babel config file detected for E:\前端学习资料\9.vue基础\class\day03\02-源代码\01-component\vue.config.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the confi…

EasyPOI实现excel文件导出

EasyPOI真的是一款非常好用的文件导出工具&#xff0c;相较于传统的一行一列的数据导出&#xff0c;这种以实体类绑定生成的方式真的非常方便&#xff0c;也希望大家能够了解、掌握其使用方法&#xff0c;下面就用一个实例来简单介绍一下EasyPOI的使用。 1.导入依赖 <!-- e…

Autosar模块介绍:Memory_3(MemIf-内存接口抽象)

上一篇 | 返回主目录 | 下一篇 Autosar模块介绍&#xff1a;Memory_3(MemIf-内存接口抽象 1 基本术语解释2 MemIf组成结构图 1 基本术语解释 编号缩写原文解释1(Logical) Block——可单独寻址的连续内存区域&#xff08;即&#xff0c;用于读、写、擦除、比较等操作&#xff…

深度学习 python opencv 实现人脸年龄性别识别 计算机竞赛

文章目录 0 前言1 项目课题介绍2 关键技术2.1 卷积神经网络2.2 卷积层2.3 池化层2.4 激活函数&#xff1a;2.5 全连接层 3 使用tensorflow中keras模块实现卷积神经网络4 Keras介绍4.1 Keras深度学习模型4.2 Keras中重要的预定义对象4.3 Keras的网络层构造 5 数据集处理训练5.1 …

如何用python生成动态随机验证码图片

相信大部分小伙伴在登录注册页面都看到过这样的验证码图片&#xff1a; 今天就带大家用实现一波这种验证码图片的生成&#xff0c;这在Django开发中可以拿来即用~ 1. 首先导入必要的库&#xff1a; import random from PIL import Image, ImageDraw, ImageFont, ImageFilter…

Vatee万腾科技决策力的引领创新:Vatee数字化视野的崭新天地

在数字时代的激烈竞争中&#xff0c;Vatee万腾以其科技决策力的引领&#xff0c;开创了数字化视野的崭新天地。这并不仅仅是一场技术的飞跃&#xff0c;更是一次对未来的深刻洞察和引领创新的勇敢实践。 Vatee万腾的科技决策力不仅仅停留在数据分析和算法的运用&#xff0c;更是…

reticulate | R-python调用 | 安装及配置 | conda文件配置

reticulate | R-python安装及配置 | conda文件配置 1. 基础知识2. 安装reticulate from CRAN3. 包含了用于Python和R之间协同操作的全套工具&#xff0c;在R和Rstudio中均可使用4. 配置python环境4.1 4种环境配置方式4.2 miniconda 环境install_miniconda()报错一install_minic…

设计模式之原型模式(Prototype)

原型模式 如果已经有一个对象了&#xff0c;你想创建一个对象&#xff0c;而且对象里面的属性和已经存在的对象的属性差不多&#xff0c;就可以使用clone方法 克隆一个出来 实现原型模式需要实现标记型接口Cloneable -->标记型接口 : 里面没有需要实现的方法(空接口) 一般…

双十一钜惠!三门不可多得的HarmonyOS学习教程

今年双十一&#xff0c;各大商城优惠不断。这里介绍三门不可多得的HarmonyOS学习教程&#xff0c;都有非常大的折扣优惠。 《鸿蒙HarmonyOS手机应用开发实战》 《鸿蒙HarmonyOS手机应用开发实战》是由清华大学出版社出版的。 目前当当是“7.56折”&#xff1a;http://produc…

照片放大软件 Topaz Gigapixel AI mac中文版简介

Topaz Gigapixel AI mac是一款使用人工智能功能扩展图像的桌面应用程序&#xff0c;同时添加自然细节以获得惊人的效果。使用深度学习技术&#xff0c;A.I.Gigapixel™可以放大图像并填写其他调整大小的产品遗漏的细节&#xff0c;使用A.I.Gigapixel™&#xff0c;您可以裁剪照…

Vatee万腾外汇数字化策略:Vatee科技决策力的未来引领

在外汇市场&#xff0c;Vatee万腾通过其前瞻性的外汇数字化策略&#xff0c;正引领着科技决策的未来。这一数字化策略的崭新愿景为投资者提供了更智慧、更高效的外汇投资体验&#xff0c;成为科技决策领域的翘楚。 Vatee万腾的外汇数字化策略是科技决策力未来引领的典范。通过运…

14.求n!和1!+2!+...+20!和2^1+2^2+……++2^20和2^1+2^3+……++2^19

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 四、举一反三一、题目描述&#xff1a;求1&#xff01;2&#xff01;...20! 二、题目分析 三、解题 程序运行代码1程序运行代码2 一、题目描述&#xff1a;求求2^1^2^2^……2^20^二、解题 程序运行代码 一、题目…

将ChatGPT集成在AR中,Snap玩出了新花样!

著名社交媒体平台Snap在官网宣布&#xff0c;在最新的AR开发平台Lens Studio 5.0版本中&#xff0c;集成ChatGPT功能。 在ChatGPT的帮助下&#xff0c;开发人员可以创建更多有趣、科普、对话、创意的Snapchat镜头&#xff0c;例如&#xff0c;通过ChatGPT创建无限测验和随机生…

燃气管网监测系统|全面保障燃气安全

根据新华日报的报道&#xff0c;2023年上半年&#xff0c;我国共发生了294起燃气事故&#xff0c;造成了57人死亡和190人受伤&#xff0c;燃气事故的发生原因有很多&#xff0c;其中涉及到燃气泄漏、设备故障等因素。因此&#xff0c;加强燃气安全管理&#xff0c;提高城市的安…

祝贺莱佛士学生获得SDC国际设计大赛新加坡赛区冠军

染色师和调色师协会国际设计大赛&#xff08;SDC International Design Competition&#xff0c;简称SDC国际设计大赛&#xff09;由英国染色家协会&#xff08;Society of Dyers and Colourists&#xff0c;简称SDC&#xff09;举办。 SDC成立于1884年&#xff0c;是国际上最…

基于 PostgreSQL 构建 AI 电商产品图片相似度搜索方案

在这篇文章中&#xff0c;将介绍如何基于向量数据库&#xff0c;构建一个电商产品图片目录的向量相似度查询解决方案。我们将通过 Amazon SageMaker、pgvector 向量数据库扩展插件、小型语言模型助力 AI 图片搜索能力&#xff0c;从而在产品目录中查找到最符合条件的产品&#…

证明串口是好的

前提&#xff1a;客户返回来一个pad&#xff0c;说串口不好用&#xff0c;售后让研发确定一下串口好不好用。 pad的串口是usb口&#xff08;不知道这样说对不对&#xff09;&#xff0c;然后就一个“usb--9针”的线。 要确定串口好不好&#xff0c;首先要从电脑发数据给pad&a…

【PyTorch教程】如何使用PyTorch分布式并行模块DistributedDataParallel(DDP)进行多卡训练

本期目录 1. 导入核心库2. 初始化分布式进程组3. 包装模型4. 分发输入数据5. 保存模型参数6. 运行分布式训练7. DDP完整训练代码 本章的重点是学习如何使用 PyTorch 中的 Distributed Data Parallel (DDP) 库进行高效的分布式并行训练。以提高模型的训练速度。 1. 导入核心库 D…

双十一电视盒子哪个牌子好?测评工作室整理口碑电视盒子排名

在挑选电视盒子的时候&#xff0c;新手朋友们不知道从何下手&#xff0c;最近很多粉丝评论想要我们分享双11电视盒子推荐&#xff0c;于是我们根据用户的评价整理了目前口碑最好的电视盒子排名&#xff0c;给不懂电视盒子哪个牌子好的朋友们做个参考。 TOP 1、泰捷WEBOX WE40S电…

【python自动化】Playwright基础教程(五)事件操作②悬停输入清除精讲

【python自动化】Playwright基础教程(五)事件操作②悬停&输入&清除精讲 本章目录 文章目录 【python自动化】Playwright基础教程(五)事件操作②悬停&输入&清除精讲鼠标悬停 - hover鼠标悬停实战 输入内容 - fill输入内容实战清空内容实战 输入内容 - type模拟…