Advanced MapStruct Usage: Expressions, qualifiedByName, nullValueMappingStrategy, and Decorators
MapStruct is a compile‑time Java annotation processor that automates bean mapping, offering concise, high‑performance, type‑safe conversions, and advanced features such as expression mapping, qualifiedByName methods, null‑value strategies, and decorators, with full IDE support and illustrative code examples.
MapStruct is a Java compile‑time annotation‑processing framework that automatically maps one Java bean to another, aiming to minimize code and runtime overhead.
Key features include:
Concise: Simplifies conversion code between Java beans by generating straightforward assignment statements.
Excellent Performance: Generates code at compile time without reflection, resulting in superior execution speed.
Safety: Provides type‑safe mappings and catches potential errors during compilation.
Flexibility: Supports custom conversion methods, type conversions, and mapping strategies for complex scenarios.
IDE Support: Offers strong integration with IDEs for code completion, error highlighting, and navigation.
expression
During mapping you can execute a Java expression directly. Example:
@Mapper(componentModel = "spring")
public interface MyMapper {
@Mapping(target = "createTime", expression = "java(System.currentTimeMillis())")
Target toTarget(Source source);
}The generated implementation sets createTime to System.currentTimeMillis() :
@Component
public class MyMapperImpl implements MyMapper {
@Override
public Target toTarget(Source source) {
Target target = new Target();
target.setCreateTime(System.currentTimeMillis());
return target;
}
}qualifiedByName
When the default getter/setter mapping is insufficient, you can specify a custom method using qualifiedByName :
@Mapper(componentModel = "spring")
public interface MyMapper {
@Mapping(target = "name", source = "name", qualifiedByName = "toUpperCase")
Target toTarget(Source source);
@Named("toUpperCase")
default String toUpperCase(String value) {
return value == null ? null : value.toUpperCase();
}
}The generated code calls the custom method:
@Component
public class MyMapperImpl implements MyMapper {
@Override
public Target toTarget(Source source) {
if (source == null) {
return null;
}
Target target = new Target();
target.setName(toUpperCase(source.getName()));
return target;
}
}nullValueMappingStrategy
By default, a null source results in a null target field ( NullValueMappingStrategy.RETURN_NULL ). To return a default value instead, use NullValueMappingStrategy.RETURN_DEFAULT :
@Mapper(componentModel = "spring", nullValueMappingStrategy = org.mapstruct.NullValueMappingStrategy.RETURN_DEFAULT)
public interface MyMapper {
Target toTarget(Source source);
}The implementation creates empty collections when source values are null:
@Component
public class MyMapperImpl implements MyMapper {
@Override
public Target toTarget(Source source) {
Target target = new Target();
if (source != null) {
target.setName(toUpperCase(source.getName()));
List
list = source.getIds();
if (list != null) {
target.setIds(new ArrayList
(list));
} else {
target.setIds(new ArrayList
());
}
}
return target;
}
}Decorator
You can create a decorator class to add global post‑processing logic to mapper methods:
public abstract class YourMapperDecorator implements YourMapper {
private final YourMapper delegate;
public YourMapperDecorator(YourMapper delegate) {
this.delegate = delegate;
}
@Override
public Target toTarget(Source source) {
Target result = delegate.toTarget(source);
if (result != null) {
if (result.getField() == null) {
result.setField("");
}
// additional field handling
}
return result;
}
}Apply it with @DecoratedWith on the mapper interface:
@Mapper
@DecoratedWith(YourMapperDecorator.class)
public interface YourMapper {
Target toTarget(Source source);
}This setup ensures that every mapping call passes through the decorator, allowing you to enforce default values or other cross‑cutting concerns.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.