Как рекурсивно обновлять вложенные объекты из одного объекта другим, используя mapstruct или modelmapper?
Лучше я объясню свою задачу на примере того, что я хочу получить. Возможно ли решить эту проблему с помощью mapstruct / modelmapper / etc?
class Person{
String name;
Address address;
}
class Address{
String street;
Integer home;
}
Обновления:
{
name: "Bob"
address: {
street: "Abbey Road"
}
}
Цель:
{
name: "Michael"
address: {
street: "Kitano"
home: 5
}
}
И в результате я хочу получить:
{
name: "Bob"
address: {
street: "Abbey Road"
home: 5
}
}
Он не должен переписывать объект Address. Он рекурсивно устанавливает новые значения в нем.
2 ответа
Да, вы можете использовать Обновление существующих экземпляров bean-компонентов из MapStruct для выполнения необходимых обновлений.
Картограф будет выглядеть так:
@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public interface PersonMapper {
void update(@MappingTarget Person toUpdate, Person person);
void update(@MappingTarget Address toUpdate, Address address);
}
Сгенерированный код для этого будет выглядеть так:
public class PersonMapperImpl implements PersonMapper {
@Override
public void update(Person toUpdate, Person person) {
if ( person == null ) {
return;
}
if ( person.getName() != null ) {
toUpdate.setName( person.getName() );
}
if ( person.getAddress() != null ) {
if ( toUpdate.getAddress() == null ) {
toUpdate.setAddress( new Address() );
}
update( toUpdate.getAddress(), person.getAddress() );
}
}
@Override
public void update(Address toUpdate, Address address) {
if ( address == null ) {
return;
}
if ( address.getStreet() != null ) {
toUpdate.setStreet( address.getStreet() );
}
if ( address.getHome() != null ) {
toUpdate.setHome( address.getHome() );
}
}
}
nullValuePropertyMappingStrategy
- Стратегия, которая должна применяться, когда свойство исходного компонентаnull
или нет По умолчанию устанавливается значение целевого значенияnull
nullValueCheckStrategy
- Определяет, когда включатьnull
проверка значения свойства источника отображения bean-компонента
NB nullValuePropertyMappingStrategy
это из MapStruct 1.3.0.Beta2
Хотя существующий ответ верен, есть другое решение, которое не требует определения методов для всех внутренних объектов. Я лично столкнулся с аналогичной проблемой при определении преобразователей для всех внутренних классов при попытке сопоставить множество встраиваемых объектов с требованием не заменять, а оставлять их исходные экземпляры.
Согласно официальной документации, поведением картографа можно управлять с помощьюmappingControl
свойство. ОбъединениеmappingControl = DeepClone.class
с@BeanMapping
мы можем написать очень простой картограф:
@BeanMapping(mappingControl = DeepClone.class)
public abstract User updateFields(@MappingTarget User oldUser, User newUser);
и сгенерированная реализация картографа будет проходить через внутренние объекты и обновлять их новыми значениями:
@Override
public User updateFields(User oldUser, User newUser) {
if ( newUser == null ) {
return oldUser;
}
oldUser.setName( newUser.getName() );
oldUser.setEmail( newUser.getEmail() );
if ( newUser.getDepartment() != null ) {
if ( oldUser.getDepartment() == null ) {
oldUser.setDepartment( new Department() );
}
departmentToDepartment( newUser.getDepartment(), oldUser.getDepartment() );
}
else {
oldUser.setDepartment( null );
}
return oldUser;
}
protected void departmentToDepartment(Department department, Department mappingTarget) {
if ( department == null ) {
return;
}
mappingTarget.setName( department.getName() );
mappingTarget.setAddress( department.getAddress() );
}
Обратите внимание, что созданныйdepartmentToDepartment
метод устанавливает все поляDepartment
класс один за другим вместо того, чтобы просто устанавливать ссылку наdepartment
от нового объекта к цели сопоставления.
ПС
ИсточникUser.class
:
public class User {
private String name;
private String email;
private Department department;
}
ИсточникDepartment.class
:
public class Department {
private String name;
private String address;
}