Master Java Entity Mapping with MapStruct: A Complete Guide
This article introduces the open‑source MapStruct library for Java, shows how to configure Maven, defines entity and DTO classes, demonstrates basic and advanced mapping techniques—including list conversion, multi‑object merging, and default values—while providing complete code examples and explanations.
Many developers struggle with writing boilerplate code for converting entities, especially when the entities have many fields. This article introduces the open‑source project MapStruct , which enables elegant and concise entity mapping, simplifying the code.
Official site: https://mapstruct.org/
Below is the Maven configuration needed to use MapStruct:
<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>
<!-- for older IDEA versions, add the processor 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>Version compatibility notes: Maven compiler plugin should be 3.6.0 or newer, Lombok 1.16.16 or newer, and the Lombok‑MapStruct annotation processor must be added; otherwise you may encounter errors such as "No property named \"aaa\" exists in source parameter(s)." which are caused by missing getters/setters generated by Lombok.
Below are the core entity, DTO, and mapper definitions:
@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);
}The mapper interface is the only part you need to write manually; the implementation is generated at compile time.
Using the generated mapper is straightforward:
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);
}
}MapStruct can map fields, change field types, and apply custom formatting, such as date conversion.
You can also define custom methods for formatting, for example:
@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 Conversion
Mapping a list of entities follows the same configuration:
@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. Mapping Multiple Source Objects to One Target
@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 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. Default Values
@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);
}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.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
