Backend Development 11 min read

Using MapStruct for Efficient Entity Mapping in Java Projects

This article introduces the open‑source MapStruct library, shows how to configure Maven and Lombok dependencies, defines entity and DTO classes with Lombok annotations, creates a mapper interface, and demonstrates simple, list, multi‑source, and default‑value mappings with complete code examples.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Using MapStruct for Efficient Entity Mapping in Java Projects

Developers often struggle with writing repetitive boilerplate code for converting entities, especially when the entities have many fields. The open‑source project MapStruct provides a clean and elegant way to perform these conversions, reducing manual code.

First, add the required Maven configuration. The following snippet shows the necessary properties , dependencies , and build sections, including MapStruct, Lombok, and the Maven compiler plugin with annotation‑processor paths.

<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 IDEA versions before 2018.1.1, add the following configuration; newer versions can comment it out -->
        <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 to use Maven compiler plugin version 3.6.0 or higher and Lombok version 1.16.16 or higher. If the Lombok‑MapStruct processor is missing, you may encounter errors such as No property named "aaa" exists in source parameter(s). Did you mean "null"? , which usually stem from missing getter/setter methods or constructors.

Define your entity classes (e.g., Student ) and corresponding DTO classes (e.g., StudentVO ) using Lombok annotations like @Data , @Builder , @AllArgsConstructor , and @NoArgsConstructor . Then create a mapper interface annotated with @Mapper . MapStruct will generate the implementation at compile time.

@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);
}

After compilation, you can use the generated mapper directly:

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);
        // This line performs the actual conversion
        StudentVO studentVO = StudentMapper.INSTANCE.student2StudentVO(student);
        System.out.println(studentVO);
    }
}

The mapper can handle field name changes, type conversions, and date formatting automatically. You can also define custom mapping methods, for example to convert an enum to its display name:

@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();
    }
}

Advanced usages

1. List conversion – MapStruct can map collections automatically:

@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
students2StudentVOs(List
studentList);
}

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();
        List
list = new ArrayList<>();
        list.add(student);
        List
result = StudentMapper.INSTANCE.students2StudentVOs(list);
        System.out.println(result);
    }
}

2. Mapping multiple source objects to a single target – Define a method that takes several parameters:

@Data
@Builder
@AllArgsConstructor
@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. Providing default values – Use @Mapping with defaultValue to supply a fallback when the source property is null:

@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);
}

These examples demonstrate how MapStruct can dramatically reduce boilerplate code for entity‑to‑DTO conversion, support collection mapping, combine multiple sources, and handle default values, all while keeping the code clean and type‑safe.

JavaDTOMavenMapStructLombokMapperEntity Mapping
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.