Как использовать MapStruct для обновления неизменяемого поля inline? - PullRequest
1 голос
/ 10 марта 2020

Я использую MapStruct для передачи данных из одного компонента в другой, и я пытаюсь скопировать данные в неизменяемое внутреннее поле целевого объекта. В частности, внутренним неизменным полем является мультикарта Guava:

// Source.java

import com.google.common.collect.LinkedHashMultimap;

public class Source
{
    private final LinkedHashMultimap<String, String> info = LinkedHashMultimap.create();

    public LinkedHashMultimap<String, String> getInfo()
    {
        return info;
    }

}
// Target.java

import com.google.common.collect.LinkedHashMultimap;

public class Target
{
    private final LinkedHashMultimap<String, String> info = LinkedHashMultimap.create();

    public LinkedHashMultimap<String, String> getInfo()
    {
        return info;
    }

}

Основатель c из источника в цель не видит поле info:

// BasicTargetMapper.java

import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;

import com.google.common.collect.LinkedHashMultimap;

@Mapper
public interface BasicTargetMapper
{

    default void copyMultimap(final LinkedHashMultimap<String, String> sourceMap,
            @MappingTarget final LinkedHashMultimap<String, String> targetMap)
    {
        if (sourceMap != null) {
            targetMap.putAll(sourceMap);
        }
    }

    Target toTarget(Source source);
}

// Generated BasicTargetMapperImpl.java

import javax.annotation.Generated;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-03-10T11:43:53+0100",
    comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_221 (Oracle Corporation)"
)
public class BasicTargetMapperImpl implements BasicTargetMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        return target;
    }
}

Принудительное копирование поля приводит к ошибке компиляции:

// ForcedTargetMapper.java

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Named;

import com.google.common.collect.LinkedHashMultimap;

@Mapper
public interface ForcedTargetMapper
{

    @Named("copyMultimap")
    default void copyMultimap(final LinkedHashMultimap<String, String> sourceMap,
            @MappingTarget final LinkedHashMultimap<String, String> targetMap)
    {
        if (sourceMap != null) {
            targetMap.putAll(sourceMap);
        }
    }

    @Mapping(target = "info", qualifiedByName = "copyMultimap")
    Target toTarget(Source source);
}

Компиляция приводит к: Property "info" has no write accessor in Target.

Решение, которое я нашел до сих пор, заключается в использовании @AfterMapping, как это :

// TargetMapper.java

import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

import com.google.common.collect.LinkedHashMultimap;

@Mapper
public interface TargetMapper
{

    default void copyMultimap(final LinkedHashMultimap<String, String> sourceMap,
            final LinkedHashMultimap<String, String> targetMap)
    {
        if (sourceMap != null) {
            targetMap.putAll(sourceMap);
        }
    }

    @AfterMapping
    default void finishCopy(final Source source, @MappingTarget final Target target)
    {
        copyMultimap(source.getInfo(), target.getInfo());
    }

    @Mapping(target = "info", ignore = true)
    Target toTarget(Source source);
}

// Generated TargetMapperImpl.java

import javax.annotation.Generated;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-03-10T11:55:42+0100",
    comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_221 (Oracle Corporation)"
)
public class TargetMapperImpl implements TargetMapper {

    @Override
    public Target toTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        finishCopy( source, target );

        return target;
    }
}

Есть ли более практичное решение этой проблемы? Тот же вопрос возникает, если целевым полем является постоянная коллекция Hibernate, и вы хотите изменить ее на месте (например: удалить уже сохраненные объекты, обновить существующие и / или добавить новые).

...