Master Bean Mapping with MapStruct: From PO to VO Made Easy

This article introduces MapStruct, a Java annotation‑processor that automatically generates bean‑mapping code, compares it with manual setters and Spring/Apache BeanUtils, and demonstrates how to handle property name differences, collections, type conversions, and multi‑source mappings through clear examples and code snippets.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Master Bean Mapping with MapStruct: From PO to VO Made Easy

Background

In a typical Java project, PO (persistent object) represents a database table row, VO (view object) is used for presentation, and BO (business object) encapsulates business logic. Manually copying properties between these objects leads to verbose, error‑prone code.

CarPo carPo = this.carDao.selectById(1L);
CarVo carVo = new CarVo();
carVo.setId(carPo.getId());
carVo.setName(carPo.getName());
// ... many setters
return carVo;

Using BeanUtils.copyProperties simplifies the code but introduces performance concerns, especially with reflection‑based implementations.

MapStruct Tutorial

MapStruct is a compile‑time code generator that creates type‑safe bean‑mapping implementations based on annotated interfaces.

Usage

Add the following Maven dependencies:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.2.0.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.2.0.Final</version>
</dependency>

Define PO and VO classes (using Lombok for brevity) and a mapper interface:

@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class CarPo {
    private Integer id;
    private String brand;
}

@Data @Builder @NoArgsConstructor @AllArgsConstructor
public class CarVo {
    private Integer id;
    private String brand;
}

@Mapper
public interface CarConvertBasic {
    CarConvertBasic INSTANCE = Mappers.getMapper(CarConvertBasic.class);
    CarVo toConvertVo(CarPo source);
}

Test the mapper:

CarPo carPo = CarPo.builder().id(1).brand("BMW").build();
CarVo carVo = CarConvertBasic.INSTANCE.toConvertVo(carPo);
System.out.println(carVo);

Output:

CarVo(id=1, brand=BMW)

Principle

MapStruct generates an implementation class (e.g., CarConvertBasicImpl) at compile time, containing the property‑copy logic, eliminating runtime reflection overhead.

Generated implementation class
Generated implementation class

Advantages

(1) Different property names

MapStruct supports explicit mappings via @Mapping:

@Mapper
public interface CarConvertBasic {
    @Mapping(source = "carName", target = "name")
    CarVo toConvertVo(CarPo source);
}

Result: CarVo(id=1, brand=BMW, name=宝马) (2) Collection conversion

@Mapper
public interface CarConvertBasic {
    List<CarVo> toConvertVos(List<CarPo> source);
}

MapStruct generates looping code that calls the single‑object mapper for each element.

Collection mapping implementation
Collection mapping implementation

(3) Type conversion

When source and target types differ (e.g., Date to String), custom expressions can be used:

@Mappings({
    @Mapping(source = "carName", target = "name"),
    @Mapping(target = "createTime", expression = "java(com.guduyan.util.DateUtil.dateToStr(source.getCreateTime()))")
})
CarVo toConvertVo(CarPo source);

(4) Multiple sources to one target

@Mapper
public interface CarConvertBasic {
    CarBo toConvertBo(CarPo car, AttributePo attr);
}

MapStruct assembles the target object from both sources automatically.

Multi‑source mapping implementation
Multi‑source mapping implementation

(5) Other advanced features

MapStruct offers many additional capabilities such as nested mappings, default values, and expression‑based mappings. Refer to the official documentation for details: https://mapstruct.org/documentation/reference-guide/

Conclusion

MapStruct provides a clean, compile‑time solution for bean conversion, improving readability, performance, and maintainability compared to manual setters or reflection‑based utilities.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javacode-generationmapstructAnnotation Processorbean-mapping
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

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.