Eliminate Smelly Boilerplate Mapping Code with MapStruct + Lombok in Spring Boot
The article shows how Spring Boot projects can replace repetitive, error‑prone Entity‑DTO‑VO conversion code with a concise MapStruct and Lombok combo, detailing setup, simple and advanced mappings, and the measurable productivity gains it brings.
Problem with manual object mapping in Spring Boot
In layered Spring Boot applications (Entity → DTO → VO) developers write repetitive assignments such as dto.setId(entity.getId());. Adding or renaming fields requires changes across many places and missing assignments cause bugs.
MapStruct overview
MapStruct generates mapper implementations at compile time based on annotations. Advantages:
Annotation‑driven, no manual implementation classes.
Generated code runs with the same performance as hand‑written code.
Mismatched fields cause compilation errors.
Integrates with Lombok and Spring.
Maven configuration (3 minutes)
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>Adding the annotation processor once enables generated mappers for the whole project.
Basic mapping with identical fields
Entity and DTO definitions (Lombok @Data):
@Data
public class CatEntity {
private Long id;
private String name;
private String color;
}
@Data
public class CatDto {
private Long id;
private String name;
private String color;
}Mapper interface:
@Mapper(componentModel = "spring")
public interface CatMapper {
CatDto toDto(CatEntity entity);
CatEntity toEntity(CatDto dto);
}Calling catMapper.toDto(entity) replaces dozens of manual setXXX calls.
Mapping fields with different names or ignored fields
When source and target property names differ, use @Mapping:
@Mapper
public interface CatMapper {
@Mapping(source = "nameInDatabase", target = "name")
@Mapping(target = "weight", ignore = true)
CatDto toDto(CatEntity entity);
}Custom value derived from business logic
Business service providing a computed value:
@Service
public class CatWeightProvider {
public Integer getWeight(String name) {
return 5; // example logic
}
}Abstract mapper that injects the service and uses a custom method annotated with @Named:
@Mapper(componentModel = "spring")
public abstract class CatMapper {
@Autowired
protected CatWeightProvider provider;
@Mapping(target = "weight", source = "name", qualifiedByName = "calcWeight")
public abstract CatDto toDto(CatEntity entity);
@Named("calcWeight")
protected Integer calcWeight(String name) {
return provider.getWeight(name);
}
}Observed benefits
Mapping code size reduced by more than 70 %.
Compilation errors catch missing or mismatched fields, decreasing runtime bugs.
Mapper definitions serve as explicit documentation.
Refactoring field names no longer requires project‑wide search.
Gotchas
Different field names require an explicit @Mapping annotation.
Custom conversion methods must be annotated with @Named.
To enable Spring injection, set componentModel = "spring" on the mapper.
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.
LuTiao Programming
LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.
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.
