【SpringBoot】26 实体映射工具(MapStruct)

news2024/11/21 23:06:27

Gitee 仓库

https://gitee.com/Lin_DH/system

介绍

现状

为了让应用程序的代码更易于维护,通常会将项目进行分层。在《阿里巴巴 Java 开发手册》中,推荐分层如下图所示:
在这里插入图片描述
每层都有对应的领域模型,即不同类型的 Bean。

  • DO(Data Object):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
  • DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
  • BO(Business Object):业务对象,由 Service 层输出的封装业务逻辑的对象。
  • AO(Application Object):应用对象,在 Web 层与 Service 层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
  • VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
  • Query:数据查询对象,各层接收上层的查询请求。超过两个参数的查询封装,禁止使用 Map 类进行传输。

痛点

由于代码分层的原因,就会导致代码中有多种 Bean,如 UserVO,UserDTO,UserDO 等,并且经常发生各种 VO / DTO / DO 之间的转换。从而产生很多 vo.setUsername(dto.getUsername()) 的代码。当字段多了不仅容易出错,而且很浪费开发时间。也有使用 BeanUtils.copyProperties() 进行转换,这样虽然减少了开发时间和代码,但依然存在问题。如:1)利用反射导致性能不好;2)不同名称的属性无法直接进行映射。

解决方案

本次使用的 Java 实体对象映射框架是 MapStruct 。MapStruct基于 JSR 269 的 Java 注解处理器,用于生成类型安全,高性能,无依赖的 Bean 映射代码,自动生成对象的代码,使用便捷,性能优越。

特点

  • 1)通过 getter / setter 进行字段拷贝,而不是利用反射机制。
  • 2)字段名称相同直接转换,名称不同使用 @Mapping 注解标识。

区别

与动态映射框架相比,MapStruct 的优势:

  • 1)使用普通的 getter / setter 方法,而不是反射机制,执行更快,性能更好。
  • 2)编译时类型安全。
  • 3)清晰的错误提示信息。

依赖

pom.xml

需要引入 mapstruct 和 mapstruct-processor,同时 scope 设置为 provided ,即它只影响到编译,测试阶段。

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.0.Final</version>
    <scope>provided</scope>
</dependency>
 
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.0.Final</version>
    <scope>provided</scope>
</dependency>

代码实现

第一步:编写 Student 实体类

Student.java

package com.lm.system.common;

import lombok.*;

import java.io.Serializable;
import java.util.Date;

/**
 * @author DUHAOLIN
 * @date 2024/11/12
 */
@Data
@Builder
public class Student implements Serializable {

    private static final long serialVersionUID = 1L;
    private Integer id;
    private String name;
    private Integer age;
    private String gender;
    private Date createTime;

}

第二步:编写 StudentVO 实体类

StudentVO.java

package com.lm.system.common.dto;

import lombok.Data;

/**
 * @author DUHAOLIN
 * @date 2024/11/12
 */
@Data
public class StudentVO {

    private Integer userId;
    private String username;
    private Integer age;
    private String gender;

}

第三步:编写实体类转换接口

StudentConvert.java

package com.lm.system.convert;

import com.lm.system.common.Student;
import com.lm.system.common.dto.StudentVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

/**
 * @author DUHAOLIN
 * @date 2024/11/12
 */
@Mapper
public interface StudentConvert {

    /**
     * 获取该类自动生成的实体类实例
     */
    StudentConvert INSTANCES = Mappers.getMapper(StudentConvert.class);

    @Mappings({
            @Mapping(source = "id", target = "userId"),
            @Mapping(source = "name", target = "username")
    })
    StudentVO toStudentVO(Student student);

}

第四步:编写测试类

MapStructTest.java

package com.lm.system.test;

import com.lm.system.common.Student;
import com.lm.system.common.dto.StudentVO;
import com.lm.system.convert.StudentConvert;
import org.junit.Test;

import java.util.Date;

/**
 * @author DUHAOLIN
 * @date 2024/11/12
 */
public class MapStructTest {

    @Test
    public void testStudent() {
        Student student = getStudent();
        System.out.println(student);
        StudentVO studentVO = StudentConvert.INSTANCES.toStudentVO(student);
        System.out.println(studentVO);
    }

    private Student getStudent() {
        return Student.builder()
                .id(1)
                .name("Tom")
                .age(18)
                .gender("男")
                .createTime(new Date())
                .build();
    }

}

效果图

在这里插入图片描述

属性处理

简单属性

当 gender 传入的是男或女,需要转换成对应的0或1,再传入数据库时,则需要进行处理。

StudentConvert.java

@Mappings({
        @Mapping(source = "id", target = "userId"),
        @Mapping(source = "name", target = "username"),
        @Mapping(target = "gender", expression = "java(student.getGender() == \"男\" ? \"0\" : \"1\")")
})
StudentVO toStudentVO(Student student);

在这里插入图片描述

复杂属性

限制输入年龄的数值。

StudentConvert.java

package com.lm.system.convert;

import com.lm.system.common.Student;
import com.lm.system.common.dto.StudentVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;

/**
 * @author DUHAOLIN
 * @date 2024/11/12
 */
@Mapper
public interface StudentConvert {

    /**
     * 获取该类自动生成的实体类实例
     */
    StudentConvert INSTANCES = Mappers.getMapper(StudentConvert.class);

    @Mappings({
            @Mapping(source = "id", target = "userId"),
            @Mapping(source = "name", target = "username"),
            @Mapping(target = "gender", expression = "java(student.getGender() == \"男\" ? \"0\" : \"1\")"),
            @Mapping(source = "age", target = "age", qualifiedByName = "transferAge")
    })
    StudentVO toStudentVO(Student student);

    @Named("transferAge")
    default Integer transferAge(Integer age) {
        if (age < 0) {
            return 0;
        }
        else if (age > 120){
            return 120;
        }
        else {
            return age;
        }
    }

}

在这里插入图片描述

Spring中使用

如果在 Spring 中使用,需要修改组件模型为 spring,可以通过 pom.xml 参数修改,也可以通过注解修改。修改后会在实现类上添加 @Component 注解,从而成为一个 Bean,加入 Spring 容器中。

StudentConvert.java

@Mapper(componentModel = "spring")
public interface StudentConvert {}

报错

如果遇到报错:java.lang.NoSuchMethodError,则在 IDEA 右侧的 Maven 选项中,运行 clean 和 compile,再进行重试。
在这里插入图片描述

项目结构图

在这里插入图片描述

参考链接

推荐一款Java实体映射工具—mapstruct:【https://www.cnblogs.com/lvmengtian/p/14594185.html】
【springboot进阶】优雅使用 MapStruct 进行类复制:【https://blog.csdn.net/lrb0677/article/details/127838138】
芋道 Spring Boot 对象转换 MapStruct 入门:【https://www.iocoder.cn/Spring-Boot/MapStruct/?self】

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

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

相关文章

理解和选择Vue的组件风格:组合式API与选项式API详解

目录 前言1. Vue 的两种组件风格概述1.1 选项式 API&#xff1a;直观且分块清晰1.2 组合式 API&#xff1a;灵活且逻辑集中 2. 深入理解组合式 API 的特点2.1 响应式变量与函数式编程2.2 逻辑组织更清晰2.3 更好的代码复用 3. 应用场景分析&#xff1a;如何选择 API 风格3.1 适…

Windows和mac OS共用VMware虚拟机

在Windows下使用VMware Workstation Pro创建的虚拟机&#xff0c;是以文件夹形式存储在硬盘中的&#xff0c;在mac OS中对应的虚拟机产品是VMware Fusion&#xff0c;那么在Windows下创建的虚拟机怎么在mac OS中使用呢&#xff1f; 在下图中我们可以看到&#xff0c;Windows 1…

【K8S系列】Kubernetes Pod节点ImagePullBackOff 状态及解决方案详解【已解决】

在 Kubernetes 中,当某个 Pod 的容器无法从指定的镜像仓库拉取镜像时,Pod 的状态会变为 ImagePullBackOff。这通常是因为指定的镜像不存在、镜像标签错误、认证失败或网络问题等原因。 以下是关于 ImagePullBackOff 的详细分析及解决方案。 1. ImagePullBackOff 状态分析 1.…

04 —— Webpack打包CSS代码

加载器css-loader &#xff1a;解析css代码 webpack 中文文档 | webpack中文文档 | webpack中文网 加载器style-loader&#xff1a;把解析后的css代码插入到DOM style-loader | webpack 中文文档 | webpack中文文档 | webpack中文网 准备css代码&#xff0c;放到src/login目…

单元测试框架gtest学习(二)—— 认识断言

前言 我们在上一篇文章中&#xff0c;简要介绍了gtest是如何使用的 单元测试框架gtest学习&#xff08;一&#xff09;——初始gtest-CSDN博客 这篇文章我们主要总结gtest中的所有断言相关的宏。首先需要明确的是&#xff0c;gtest是通过各种断言宏进行测试的&#xff0c;而这…

网安瞭望台第2期:零日漏洞密集爆发、2024年常见网络安全漏洞类型及分析

国内外要闻 Ubuntu 服务器 Needrestart 软件包惊现严重安全漏洞 近日&#xff0c;Ubuntu 服务器&#xff08;自 21.04 版本起默认安装&#xff09;的 Needrestart 软件包被曝存在多个可追溯至数十年前的安全漏洞。这些漏洞允许本地攻击者在无需用户交互的情况下获取根…

PDF内容提取,MinerU使用

准备环境 # python 3.10 python3 -m pip install huggingface_hub python3 -m pip install modelscope python3 -m pip install -U magic-pdf[full] --extra-index-url https://wheels.myhloli.com下载需要的模型 import json import osimport requests from huggingface_hub…

【TDOA最小二乘解算】两步最小二乘迭代的TDOA解算方法,适用于二维平面、自适应锚点(附MATLAB代码)

本文所述的MATLAB代码实现了一个基于两步加权最小二乘法的二维目标定位算法&#xff0c;利用多个锚点&#xff08;基站&#xff09;和时间差到达&#xff08;TDOA&#xff09;数据来估计未知目标的位置。 文章目录 运行结果代码代码功能概述代码结构和详细说明初始化部分参数和…

Comfy UI 工作流(三)高清修复 - 低显存放大

前面提到的几种高清修复、高清放大都会占用&#xff0c;较多的现存。 和 Sd Web UI 中的 SD Upscale 放大一样&#xff0c;Comfy UI 中也提供有类似的自定义节点&#xff0c;通过分块的方式进行图片放大&#xff0c;从而减少现、显存的占用&#xff0c;该方式也经常用于 Com…

形态学图像处理(Morphological Image Processing)

形态学图像处理(Morphological Image Processing) 前言 ‍ 本博客为个人总结数字图像处理一课所写&#xff0c;并给出适当的扩展和相应的demo。 写博客跟做 checkpoint​ 很像&#xff0c;毕竟个人还不能达到那种信手拈来的境界&#xff0c;忘了就是从零开始训练&#xff0…

数据库迁移--laravel进阶篇

本地开发中的数据库和线上发布的数据库是不一样的,每进行一个线上版本的更新,很可能也涉及大量数据库的改动,那么这些数据库的改动在laravel中可以使用数据库迁移来处理。 比如我想创建一张flights数据表 执行php artisan make:migration create_flights_table命令就能自动生…

正则表达式完全指南,总结全面通俗易懂

目录 元字符 连接符 限定符 定位符 修饰符&#xff08;标记&#xff09; 运算符优先级 普通字符集及其替换 零宽断言 正向先行断言 负向先行断言 正向后发断言 负向后发断言 捕获组 普通捕获组 命名捕获组 PS:非捕获组 正则表达式在线测试: 正则在线测试工具 …

vulfocus在线靶场:CVE-2018-7600 速通手册

目录 一、启动环境&#xff0c;访问页面&#xff0c;语言选择中文&#xff0c;打开phpmyadmin 二、phpmyadmin中打开小房子 三、选择显示php信息 四、ctrlF&#xff0c;搜索flag&#xff0c;复制粘贴到任务中&#xff0c;通关 一、启动环境&#xff0c;访问页面&#xff0c;…

springboot实战(13)(@PatchMapping、@RequestParam、@URL、ThreadLocal线程局部变量)

目录 一、PATCH请求方式。 二、实现用户更新头像功能。 三、注解RequestParam。 四、注解URL。&#xff08;对传来的参数是否是合法地址进行校验&#xff09; 一、PATCH请求方式。 patch中文翻译&#xff1a;局部、小块。PATCH 请求主要用于对已存在的资源进行局部修改&#xf…

记录下,用油猴Tampermonkey监听所有请求,绕过seesion

油猴Tampermonkey监听所有请求&#xff0c;绕过seesion 前因后果脚本编写 前因后果 原因是要白嫖一个网站的接口&#xff0c;这个接口的页面入口被隐藏掉了&#xff0c;不能通过页面调用&#xff0c;幸好之前有想过逆向破解通过账号密码模拟登录后拿到token&#xff0c;请求该…

Spring Cloud Hystrix 豪猪哥 服务容错与保护组件

Spring Cloud Hystrix 豪猪哥 服务容错与保护组件 一、Spring Cloud Hystrix概述1.简介2.核心功能3.雪崩效应 二、Hystrix服务降级1.服务降级简介2.样例准备3.服务提供者降级4.服务消费者降级5.全局降级方法6.对API接口降级方法 三、Hystrix服务熔断1.服务熔断机制简介2.熔断工…

Facebook商城号封号的原因是什么?

Facebook商城作为一个重要的销售平台&#xff0c;不仅为商家提供了巨大的市场机会&#xff0c;也带来了一系列需要警惕的风险&#xff0c;其中包括账号被封的风险。本文将从环境异常、频繁操作和违规行为三个主要方面深入探讨&#xff0c;解析导致Facebook商城账号被封禁的具体…

Android okhttp 网络链接各阶段监控

步骤 1: 添加依赖 在项目的 build.gradle 文件中&#xff0c;添加 OkHttp 依赖&#xff1a; implementation com.squareup.okhttp3:okhttp:4.11.0 步骤 2: 创建自定义的 EventListener 创建一个自定义的 EventListener 类&#xff1a; import android.util.Log import okht…

MyBatis——#{} 和 ${} 的区别和动态 SQL

1. #{} 和 ${} 的区别 为了方便&#xff0c;接下来使用注解方式来演示&#xff1a; #{} 的 SQL 语句中的参数是用过 ? 来起到类似于占位符的作用&#xff0c;而 ${} 是直接进行参数替换&#xff0c;这种直接替换的即时 SQL 就可能会出现一个问题 当传入一个字符串时&#xff…

【Python】30个Python爬虫的实战项目!!!(附源码)

Python爬虫是数据采集自动化的利器。本文精选了30个实用的Python爬虫项目&#xff0c;从基础到进阶&#xff0c;每个项目都配有完整源码和详细讲解。通过这些项目的实战&#xff0c;可以全面掌握网页数据抓取、反爬处理、并发下载等核心技能。 一、环境准备 在开始爬虫项目前…