I am using ModelMapper to map DTOs to Entities and vice versa. I have a problem when a I am mapping a ChildDTO
to a Child
where both the Child
class and the Parent
class have a property with the same name.
These are the objects:
public class Child {
@Id
@EqualsAndHashCode.Include
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String name; // <---- property called 'name'
// other properties
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Parent parent;
}
public class Parent {
@Id
@EqualsAndHashCode.Include
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String name; // <---- property called 'name'
// other properties
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> items = new ArrayList<>();
}
public class ChildDTO {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
private String name;
// other properties
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long parentId;
}
And this is the code that does the mapping:
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setPropertyCondition(Conditions.isNotNull());
modelMapper.getConfiguration().setAmbiguityIgnored(true);
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
Child childItem = repository.findById(id);
ChildDTO childItemDTO = new ChildDTO();
childItemDTO.setName("new name");
modelMapper.map(childItemDTO, childItem);
This results in both Child::name
and Parent::name
to change to "new name". Notice I'm already using the STRICT
mapping strategy.
I could also not find way of skipping calling Parent::setName()
when mapping from ChildDTO
to Child
ModelMapper version: 2.3.2
I encountered a challenge where Model Mapper attempted to map nested fields with the same abstract class as the parent, resulting in the engine mapping values to the child properties. To address this issue, I devised a solution by extending the matching strategy to avoid mapping unexpected nested fields. example :
abstract class AbstractClass{
String ref;
}
class Parent extends AbstractClass{
...
}
class Child extends AbstractClass{
Parent parent;
...
}
when i try to map a Child object whith this data
{ ref: "CHILD-REF"; parent: null } to another Child object { ref: null, parent: { ref: "PARENT-REF" } }
the beahvior by default will be a Child object : { ref: "CHILD-REF", parent: { ref: "CHILD-REF" } }
and using my new condition the result will be { ref: "CHILD-REF", parent: { ref: "PARENT-REF" } }
here the details
...
final ModelMapper mapper = new ModelMapper();
...
mapper.getConfiguration().setMatchingStrategy(new CustomModelMapperMapping());
extending the MatchingStrategies.STANDARD
public class CustomModelMapperMapping implements MatchingStrategy {
static final MatchingStrategy standardMatchingStrategy = MatchingStrategies.STANDARD;
NuagikeModelMapperMapping() {
}
public boolean matches(PropertyNameInfo propertyNameInfo) {
//this is the strandard behavior
final boolean standard_matching_result = standardMatchingStrategy.matches(propertyNameInfo);
//and then i added new condition
if (standard_matching_result) {
final String sourceSubTokens = propertyNameInfo.getSourcePropertyTokens()
.stream()
.flatMap(s -> StreamSupport.stream(s.spliterator(), false))
.collect(Collectors.joining("-")).toLowerCase();
final String destinationSubTokens = propertyNameInfo.getDestinationPropertyTokens()
.stream()
.flatMap(s -> StreamSupport.stream(s.spliterator(), false))
.collect(Collectors.joining("-")).toLowerCase();
return sourceSubTokens.equals(destinationSubTokens);
}
return false;
}
public boolean isExact() {
return false;
}
public String toString() {
return "my custom strategy";
}
}