Используйте двунаправленные методы сущностей с Mapstruct

У меня есть двунаправленное сопоставление (@OneToMany Hibernate) с дополнительными методами, чтобы гарантировать, что оба объекта связаны. Простой пример:

@Setter
class ParentDto {
    List<ChildDto> childList;
}

@Setter
class ChildDto {
    String text;
}

@Setter
class Parent {

    List<Child> childList;

    public void addChild(Child child) {
        childList.add(child);
        child.setParent(this);
    }
}

@Setter
class Child {
    Parent parent;
    String text;
}

Картограф:

@Mapper(componentModel = "spring")
public interface TestMapper {

Parent toEntity(ParentDto parentDto);
}

Сгенерировано:

public class TestMapperImpl implements TestMapper {

@Override
public Parent toEntity(ParentDto parentDto) {
    if ( parentDto == null ) {
        return null;
    }

    Parent parent = new Parent();
    parent.setChildList( childDtoListToChildList( parentDto.getChildList() ) );

    return parent;
}

protected Child childDtoToChild(ChildDto childDto) {
    if ( childDto == null ) {
        return null;
    }

    Child child = new Child();
    child.setText( childDto.getText() );

    return child;
}

protected List<Child> childDtoListToChildList(List<ChildDto> list) {
    if ( list == null ) {
        return null;
    }

    List<Child> list1 = new ArrayList<Child>( list.size() );
    for ( ChildDto childDto : list ) {
        list1.add( childDtoToChild( childDto ) );
    }
    return list1;
}

Основной вопрос: как заставить Mapstruct использовать parent.addChild (...), чтобы сохранить двунаправленное сопоставление между родителем и списком дочерних элементов.

У меня более сложная структура с несколькими вложенными дочерними элементами, поэтому следует учитывать расширяемость.


person Dima Garbar    schedule 27.02.2021    source источник


Ответы (2)


В MapStruct используется концепция стратегии сопоставления коллекций. Это позволяет вам использовать сумматоры при их отображении.

e.g.

@Mapper(componentModel = "spring", collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
public interface TestMapper {

    Parent toEntity(ParentDto parentDto);
}
person Filip    schedule 27.02.2021

После долгих поисков я нашел лучшее решение на данный момент. Он не использует специальный метод, но позволяет поддерживать двунаправленное соединение.

@AfterMapping
default void mapBidirectional(@MappingTarget Parent parent){
    List<Child> childList = parent.getChildList();
    if (childList != null) {
        childList.forEach(child -> child.setParent(parent));
    }
}

Будет

@Override
public Parent toEntity(ParentDto parentDto) {
    if ( parentDto == null ) {
        return null;
    }

    Parent parent = new Parent();

    parent.setChildList( childDtoListToChildList( parentDto.getChildList() ) );

    mapBidirectional( parent );

    return parent;
}

Но, скорее всего, есть другое решение этой проблемы, так как двусторонняя связь довольно распространена, и это решение плохо масштабируется

И его нельзя разделить на несколько классов *Mapping, потому что вы не можете использовать сгенерированную переменную в методе @AfterMapping

person Dima Garbar    schedule 27.02.2021