Simplify Java Bean Mapping with MapStruct: A Practical Guide
This article introduces the open‑source MapStruct library for Java, shows how to configure Maven and Lombok, provides step‑by‑step code examples for basic and advanced entity‑to‑DTO mappings—including list conversions, multi‑object mappings, and default values—so developers can replace boilerplate conversion code with clean, type‑safe mappings.
Many developers find writing boilerplate code for converting entities, especially when the entity has many fields, tedious. This guide introduces the open‑source project MapStruct , which enables elegant and type‑safe bean mapping.
Maven Configuration
<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 should not end up on the classpath -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
<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>Make sure the Maven compiler plugin version is at least 3.6.0 and Lombok version is 1.16.16 or newer; otherwise you may encounter errors such as
No property named "aaa" exists in source parameter(s). Did you mean "null"?.
Entity Classes
@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 Interface
@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);
}Usage Example
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);
// Actual conversion code
StudentVO studentVO = StudentMapper.INSTANCE.student2StudentVO(student);
System.out.println(studentVO);
}
}The mapper can map fields, change field types, and apply custom date formatting.
Advanced Features
1. List Conversion
@Mapper
public interface StudentMapper {
// existing method omitted for brevity
List<StudentVO> students2StudentVOs(List<Student> studentList);
}
public class Test {
public static void main(String[] args) {
Student student = Student.builder()...build();
List<Student> list = new ArrayList<>();
list.add(student);
List<StudentVO> result = StudentMapper.INSTANCE.students2StudentVOs(list);
System.out.println(result);
}
}2. Multiple Objects to One Object
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Course {
private String courseName;
private int sortNo;
private long id;
}
@Mapper
public interface StudentMapper {
@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()...build();
Course course = Course.builder().id(1L).courseName("语文").build();
StudentVO studentVO = StudentMapper.INSTANCE.studentAndCourse2StudentVO(student, course);
System.out.println(studentVO);
}
}3. Default Values
@Mapper
public interface StudentMapper {
@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);
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
