【推荐】使用 mapstruct 对 PO VO DTO 进行转换

本文介绍了Mapstruct这一开源项目,用于简化实体之间的数据转换,特别是当字段众多时,对比了它与BeanUtils的使用,并展示了如何通过Lombok和Mapper接口实现自动化的字段映射和格式化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文转载,原文地址: 别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?      

老铁们是不是经常为写一些实体转换的原始代码感到头疼,尤其是实体字段特别多的时候。介绍一个开源项目 mapstruct ,可以轻松优雅的进行转换,简化你的代码。当然有的人喜欢写get set,或者用BeanUtils 进行复制,代码只是工具,本文只是提供一种思路。

先贴下官网地址吧:https://mapstruct.org/

废话不多说,上代码:

pom 配置:

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<maven.compiler.source>1.8</maven.compiler.source>
	<maven.compiler.target>1.8</maven.compiler.target>
	<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
	<org.projectlombok.version>1.18.12</org.projectlombok.version>
</properties>


<dependencies>
	<dependency>
		<groupId>org.mapstruct</groupId>
		<artifactId>mapstruct</artifactId>
		<version>${org.mapstruct.version}</version>
	</dependency>

	<!-- lombok dependencies should not end up on classpath -->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<version>${org.projectlombok.version}</version>
		<scope>provided</scope>
	</dependency>
	
	<!-- idea 2018.1.1 之前的版本需要添加下面的配置,后期的版本就不需要了,可以注释掉,我自己用的2019.3 -->
	<dependency>
		<groupId>org.mapstruct</groupId>
		<artifactId>mapstruct-processor</artifactId>
		<version>${org.mapstruct.version}</version>
		<scope>provided</scope>
	</dependency>

</dependencies>


<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<version>3.8.1</version>
			<configuration>
				<source>1.8</source>
				<target>1.8</target>
				<annotationProcessorPaths>
					<path>
						<groupId>org.projectlombok</groupId>
						<artifactId>lombok</artifactId>
						<version>${org.projectlombok.version}</version>
					</path>
					<path>
						<groupId>org.mapstruct</groupId>
						<artifactId>mapstruct-processor</artifactId>
						<version>${org.mapstruct.version}</version>
					</path>
				</annotationProcessorPaths>
			</configuration>
		</plugin>
	</plugins>
</build>

关于lombok和mapstruct的版本兼容问题多说几句,maven插件要使用3.6.0版本以上、lombok使用1.16.16版本以上,另外编译的lombok mapstruct的插件不要忘了加上。否则会出现下面的错误:No property named "aaa" exists in source parameter(s). Did you mean "null"?

这种异常就是lombok编译异常导致缺少get setter方法造成的。还有就是缺少构造函数也会抛异常。

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private String name;
    private int age;
    private GenderEnum gender;
    private Double height;
    private Date birthday;

}


public enum GenderEnum {
    Male("1", "男"),
    Female("0", "女");

    private String code;
    private String name;

    public String getCode() {
        return this.code;
    }

    public String getName() {
        return this.name;
    }

    GenderEnum(String code, String name) {
        this.code = code;
        this.name = name;
    }
}


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
    private String name;
    private int age;
    private String gender;
    private Double height;
    private String birthday;
}


@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "gender.name", target = "gender")
    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    StudentVO student2StudentVO(Student student);

}

实体类是开发过程少不了的,就算是用工具生成肯定也是要有的,需要手写的部分就是这个Mapper的接口,编译完成后会自动生成相应的实现类。

然后就可以直接用mapper进行实体的转换了

public class Test {

    public static void main(String[] args) {

        Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();
        System.out.println(student);
        //这行代码便是实际要用的代码
        StudentVO studentVO = StudentMapper.INSTANCE.student2StudentVO(student);
        System.out.println(studentVO);

    }

}

mapper可以进行字段映射,改变字段类型,指定格式化的方式,包括一些日期的默认处理。

可以手动指定格式化的方法:

@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "gender", target = "gender")
    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    StudentVO student2StudentVO(Student student);

    default String getGenderName(GenderEnum gender) {
        return gender.getName();
    }

}

上面只是最简单的实体映射处理,下面介绍一些高级用法

1、List 转换

属性映射基于上面的mapping配置

@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "gender.name", target = "gender")
    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    StudentVO student2StudentVO(Student student);


    List<StudentVO> students2StudentVOs(List<Student> studentList);

}


public static void main(String[] args) {

    Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();

    List<Student> list = new ArrayList<>();
    list.add(student);
    List<StudentVO> result = StudentMapper.INSTANCE.students2StudentVOs(list);
    System.out.println(result);
}

2、多对象转换到一个对象

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private String name;
    private int age;
    private GenderEnum gender;
    private Double height;
    private Date birthday;

}


@Data
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class Course {

    private String courseName;
    private int sortNo;
    private long id;

}


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
    private String name;
    private int age;
    private String gender;
    private Double height;
    private String birthday;
    private String course;
}


@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "student.gender.name", target = "gender")
    @Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(source = "course.courseName", target = "course")
    StudentVO studentAndCourse2StudentVO(Student student, Course course);

}


public class Test {

    public static void main(String[] args) {

        Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();
        Course course = Course.builder().id(1L).courseName("语文").build();

        StudentVO studentVO = StudentMapper.INSTANCE.studentAndCourse2StudentVO(student, course);
        System.out.println(studentVO);
    }

}

3、默认值

@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "student.gender.name", target = "gender")
    @Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(source = "course.courseName", target = "course")
    @Mapping(target = "name", source = "student.name", defaultValue = "张三")
    StudentVO studentAndCourse2StudentVO(Student student, Course course);

}

拓展知识点:@Mapper主键,其有不同的属性值,可以看看它不同的属性值的功能用途是什么。  

常用的属性有:builder、 uses、 unmappedTargetPolicy、unmappedSourcePolicy ,还有其他非常多的属性。

                                       

4、参考文章

1、 Mapstruct中使用lombok@Builder的坑          

2、 Mapstruct @Mapper @Mapping 使用介绍以及总结       

3、 MapStruct进阶使用<二>    

                                         

### JavaVO、BO、DTOPO转换方法 在Java开发中,为了保持不同层次间的解耦合以及数据的安全性和一致性,通常会使用不同的对象模型来表示同一逻辑实体的不同视图。这些对象包括VO(View Object)、BO(Business Object)、DTO(Data Transfer Object)和PO(Persistent Object)。下面将介绍如何在这几种对象之间进行转换。 #### 使用 MapStruct 实现自动映射 MapStruct 是一种用于简化类间属性复制工作的框架,可以减少手动编写繁琐的转换代码。通过定义接口并标注特定注解即可实现高效的双向映射功能[^1]。 ```java @Mapper(componentModel = "spring") public interface EntityDtoMapper { UserBo userPoToUserBo(UserPo po); UserDto userBoToUserDto(UserBo bo); } ``` 此段代码展示了从`UserPo`(持久化对象)到`UserBo`(业务对象),再到`UserDto`(传输对象)的过程。借助于MapStruct自动生成具体的实现类完成实际的对象转换工作。 对于VO(视图对象), 如果前端请求或响应需要特殊格式的数据,则可以在控制器层进一步处理: ```java @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public ResponseEntity<UserVo> getUser(@PathVariable Long id){ UserDto dto = userService.getUserById(id); // 假设这里有一个dtoToVo的方法来进行最终转换 return new ResponseEntity<>(dtoToVo(dto), HttpStatus.OK); } } private static UserVo dtoToVo(UserDto dto){ UserVo vo = new UserVo(); BeanUtils.copyProperties(dto,vo); // 可能还需要额外设置一些只读字段或者其他不适合放在DTO中的信息 return vo; } ``` 上述例子说明了当接收到HTTP GET请求时,先调用服务获取DTO实例,之后将其化为适合前端使用VO实例返回给客户端[^2]。 另外值得注意的是,在某些情况下可以直接利用DTO作为VO传递给前端而不需要单独创建VO类,尤其是在两者结构非常相似的情况下这样做能够有效降低系统的复杂度[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值