• 作者:老汪软件技巧
  • 发表时间:2024-09-29 17:01
  • 浏览量:

前言

在项目中,常常会遇到从数据库读取数据后不能直接返回给前端展示的情况,因为还需要对字段进行加工,比如去除时间戳记录、隐藏敏感数据等。传统的处理方式是创建一个新类,然后编写大量的 get/set 方法进行赋值,若字段很多,这无疑是一场噩梦,而且还容易出现遗漏的情况。

我们都清楚,随着工程日益成熟,模块划分会越发细致。实体类通常存放在 domain 中,但最好不要让 domain 工程被其他工程依赖。所以,当其他工程需要获取实体类数据时,就需要在各自工程中编写 model。自定义 model 能够根据自身业务需求映射相应的实体属性。如此一来,这个映射工作似乎并不简单。

这个时候,我们可以使用MapStruct,在企业级应用中,经常需要在不同类型的对象(如 DTO 和 DO、VO 和 PO 等)之间进行转换。MapStruct 通过在编译时基于接口定义生成转换代码,大大简化了这个过程。例如,从一个包含用户注册信息的 DTO 转换为一个用于业务逻辑处理的 DO 时,只需要定义一个 MapStruct 接口。

引入依赖

<properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    <java.version>17java.version>
    <maven.compiler.source>17maven.compiler.source>
    <maven.compiler.target>17maven.compiler.target>
    <lombok.version>1.18.34lombok.version>
    <mapstruct.version>1.6.2mapstruct.version>
properties>
<dependencies>
    <dependency>
      <groupId>junitgroupId>
      <artifactId>junitartifactId>
      <version>4.13.2version>
      <scope>testscope>
    dependency>
    
    <dependency>
      <groupId>org.projectlombokgroupId>
      <artifactId>lombokartifactId>
      <version>${lombok.version}version>
    dependency>
    
    <dependency>
      <groupId>org.mapstructgroupId>
      <artifactId>mapstructartifactId>
      <version>${mapstruct.version}version>
    dependency>
    <dependency>
      <groupId>org.mapstructgroupId>
      <artifactId>mapstruct-processorartifactId>
      <version>${mapstruct.version}version>
      <scope>providerscope>
    dependency>
dependencies>

org.mapstruct:mapstructorg.mapstruct:mapstruct - processor定义实体

定义两个实体,字段上略微有些差别

import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private long id;
    private String name;
    private int age;
    private String password;
    private Date createTime;
}


import lombok.Data;
@Data
public class UserVO {
    private Long id;
    private String name;
    private Integer age;
    private String code;
    private String hello;
    private String createTime;
}

优雅使用 MapStruct 进行类复制__优雅使用 MapStruct 进行类复制

定义转换的mapper

如果是spring项目,用@Mapper(componentModel = "spring"),生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入

package com.zxy.demo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
    public static final UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    static String hello(User user) {
        return "hello " + user.getName();
    }
    @Mapping(target = "createTime", source = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(target = "code", expression="java(\"xx-\" + user.getId())")
    @Mapping(target = "hello", expression = "java(UserMapper.hello(user))")
    UserVO toUserVO(User user);
}

如果所有的字段都一样,用@Mappings({})。不一样的用target+source,需要特殊处理的可以用expression。

在 MapStruct 中,expression是一个强大的功能,用于在对象映射过程中执行自定义的表达式。它允许开发人员在映射规则中使用 Java 表达式来处理复杂的映射逻辑,而不仅仅是简单的属性到属性的映射。

这在源对象和目标对象的属性之间存在复杂关系,或者需要进行额外的计算、逻辑判断等情况时非常有用。

来个单测运行一下

package  com.zxy.demo;
import java.util.Date;
import org.junit.Assert;
import org.junit.Test;
public class UserTest {
    @Test
    public void ok() {
        User u = new User();
        u.setId(1);
        u.setAge(10);
        u.setName("zxy");
        u.setPassword("123456");
        u.setCreateTime(new Date());
        Assert.assertEquals(10, u.getAge());
        Assert.assertEquals("zxy", u.getName());
        System.out.println(new User(1, "zxy", 12, "123456", new Date()));
        UserVO vo = UserMapper.INSTANCE.toUserVO(u);
        System.out.println("vo: "+vo);
        Assert.assertEquals("zxy", vo.getName());
        Assert.assertEquals("xx-1", vo.getCode());
        Assert.assertEquals("hello zxy", vo.getHello());
    }
}

简单看一下生成的代码

 // Source code is unavailable, and was generated by the Fernflower decompiler.
package com.zxy.demo;
import java.text.SimpleDateFormat;
public class UserMapperImpl implements UserMapper {
   public UserVO toUserVO(User user) {
      if (user == null) {
         return null;
      } else {
         UserVO userVO = new UserVO();
         if (user.getCreateTime() != null) {
            userVO.setCreateTime((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(user.getCreateTime()));
         }
         userVO.setId(user.getId());
         userVO.setName(user.getName());
         userVO.setAge(user.getAge());
         userVO.setCode("xx-" + user.getId());
         userVO.setHello(UserMapper.hello(user));
         return userVO;
      }
   }
}