Default values at object instantiation
To assure default values for objects at initialization we can define them either in the constructor or at field declaration.
Example:
class CarEntity {
String type = "SUV"; // default set at declaration
String color; // default set in constructor
String name; // no default, may be null!
public CarEntity() {
this.color = "black"; // default color set in constructor
}
}
Here we have preset defaults for two fields (type and color).
If fields are required (as non-null) they should be declared final and either initialized directly or passed as constructor argument: e.g. the required field final UUID id can not have a default, it must be unique like new CarEntity( UUID.randomUUID() ).
Only optional and nullable field is name. It has no default and can be freely assigned a value from a mapping.
Let's see how we can use MapStruct to set a default value when mapped.
Mapping to default values
When default values are set in the class already, the mapping can be simplified.
- we don't need to set fields at target to assure non-null values, they have preset defaults already
- all non-null values are mapped from source to target
- we only need to prevent a
null value in respective source properties to overwrite the preset defaults in target
MapStruct may support this:
@Mapper
public interface CarMapper {
@Mapping(target = "type", ignore = true)
@Mapping(target = "name", source = "name", defaultValue = "Sample")
@Mapping(target = "color", condition = "isNotEmpty")
CarEntity dtoToCarEntity(CarDTO car);
@Condition
default boolean isNotEmpty(String value) {
return value != null && !value.isEmpty();
}
}
How it works:
- the predefined default field
type is completely ignored in mapping. It will not be mapped at all. So the default at target is preserved, not modified by mapping.
- the
name field is set to default "Sample" if null in source. The default value can also be an empty String like defaultValue = "" or a defaultExpression like for an ID field: defaultExpression = "java( UUID.randomUUID().toString() ).
- If the field
color exists in source as null or empty String it will be ignored by the specified condition = "isNotEmpty" referencing the @Conditional predicate-method. See the MapStruct feature issue-205.
Conditional mapping of properties
However in 10.10. Conditional Mapping is explained that we can decide on property-mapping with a condition:
A custom condition method is a method that is annotated with org.mapstruct.Condition and returns boolean.
e.g. if you only want to map a String property when it is not `null, and it is not empty then you can do something like:
@Condition
default boolean isNotEmpty(String value) {
return value != null && !value.isEmpty();
}
This will have the conditional effect we want, here for Strings:
- map only non-null) source-properties and ignore null/empty source-properties
- all target properties will be set to defaults during initialization
Ignore strategies
Closely related to default values in mapping are the ignore-strategies. However they seem not applicable to creational mappings, but only on update (as of current version 1.5.0.Beta2).
Ignore null properties (currently on update only)
See MapStruct Reference Guide, 10.6. Controlling mapping result for 'null' arguments.
Any annotation parameter nullValuePropertyMappingStrategy like NullValuePropertyMappingStrategy.IGNORE or NullValuePropertyMappingStrategy.SET_TO_DEFAULT has no effect for creational mappings.
Ignore unmapped properties (and keep the preset defaults at target)
There are also strategies to completely ignore unmapped properties. See the tutorial on Baeldung: Ignoring Unmapped Properties with MapStruct.
However this would turn our defaults into something like final fields, similar to constants. Defaults should are designed to be overwritten when more specific values are given.