MapStruct:使用 updateMethod() 初始化,即使源为空也创建

MapStruct: initialize using updateMethod(), create even though source is null

有人能告诉我如何(或者如果可能的话)强制更新方法在目标为空时创建目标吗?

我想实现这样的目标:

    @BeforeMapping
    @Mapping(source = "inputBy", target = "tactical.inputBy")
    default BCSecuritiesTrade updateOrCreateInputBy(String inputBy, @MappingTarget BCSecuritiesTrade bcSecuritiesTrade){
        return bcSecuritiesTrade != null? bcSecuritiesTrade:new BCSecuritiesTrade();
    }

但是这个 return 始终是新实例并且不进行映射。

我想要一种方法,当它存在时更新“目标”并在输入 null 时创建新的“目标”对象。

此外,当“源”对象为空时,它不应 return 为空,而应为“目标”对象或创建新对象(如果“目标”对象为空)。当前方法在“源”为空时 returning 为空。

非常感谢!

我还没有找到开箱即用的解决方案。 根据 documentation,MapStruct 似乎设计为仅对现有实例使用 updateMethod
我通过其他自定义方法解决了问题。

MapStruct 版本 1.4.2.Final

@Mapper
public interface BCSecuritiesTradeMapper {
    BCSecuritiesTradeMapper INSTANCE = Mappers.getMapper(BCSecuritiesTradeMapper.class);

    @Mapping(source = "inputBy", target = "tactical.inputBy")
    BCSecuritiesTrade updateInputBy(String inputBy, @MappingTarget BCSecuritiesTrade bcSecuritiesTrade);

    default BCSecuritiesTrade updateOrCreateInputBy(String inputBy, BCSecuritiesTrade bcSecuritiesTrade) {
        if (bcSecuritiesTrade == null) {
            bcSecuritiesTrade = new BCSecuritiesTrade();
        }
        if (inputBy == null) {
            return bcSecuritiesTrade;
        }
        return updateInputBy(inputBy, bcSecuritiesTrade);
    }
}

生成的代码:

public class BCSecuritiesTradeMapperImpl implements BCSecuritiesTradeMapper {
    @Override
    public BCSecuritiesTrade updateInputBy(String inputBy, BCSecuritiesTrade bcSecuritiesTrade) {
        if ( inputBy == null ) {
            return null;
        }
        if ( bcSecuritiesTrade.tactical == null ) {
            bcSecuritiesTrade.tactical = new Tactical();
        }
        stringToTactical( inputBy, bcSecuritiesTrade.tactical );

        return bcSecuritiesTrade;
    }

    protected void stringToTactical(String string, Tactical mappingTarget) {
        if ( string == null ) {
            return;
        }
        mappingTarget.inputBy = string;
    }
}

MapStruct 版本 1.5.0.RC1
新版本有更好的实现。如果 sourcenull,将返回 target,但如果传递 null,它仍然无法创建 target 对象的实例。
自定义方法简化但未消除。

@Mapper
public interface BCSecuritiesTradeMapper {
    BCSecuritiesTradeMapper INSTANCE = Mappers.getMapper(BCSecuritiesTradeMapper.class);

    @Mapping(source = "inputBy", target = "tactical.inputBy")
    BCSecuritiesTrade updateInputBy(String inputBy, @MappingTarget BCSecuritiesTrade bcSecuritiesTrade);

    default BCSecuritiesTrade updateOrCreateInputBy(String inputBy, BCSecuritiesTrade bcSecuritiesTrade) {
        if (bcSecuritiesTrade == null) {
            bcSecuritiesTrade = new BCSecuritiesTrade();
        }
        return updateInputBy(inputBy, bcSecuritiesTrade);
    }
}

生成的代码:

public class BCSecuritiesTradeMapperImpl implements BCSecuritiesTradeMapper {

    @Override
    public BCSecuritiesTrade updateInputBy(String inputBy, BCSecuritiesTrade bcSecuritiesTrade) {
        if ( inputBy == null ) {
            return bcSecuritiesTrade;
        }
        if ( bcSecuritiesTrade.tactical == null ) {
            bcSecuritiesTrade.tactical = new Tactical();
        }
        stringToTactical( inputBy, bcSecuritiesTrade.tactical );
        return bcSecuritiesTrade;
    }

    protected void stringToTactical(String string, Tactical mappingTarget) {
        if ( string == null ) {
            return;
        }
        mappingTarget.inputBy = string;
    }
}

单元测试:

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class MapperTest {
    @Test
    public void test() {
        BCSecuritiesTrade bcSecuritiesTrade = BCSecuritiesTradeMapper.INSTANCE.updateOrCreateInputBy(null, null);
        Assert.assertNotNull(bcSecuritiesTrade);
        Assert.assertNull(bcSecuritiesTrade.tactical);

        BCSecuritiesTrade bcSecuritiesTradeUpdate = BCSecuritiesTradeMapper.INSTANCE.updateOrCreateInputBy(null, bcSecuritiesTrade);
        Assert.assertNotNull(bcSecuritiesTradeUpdate);
        Assert.assertNull(bcSecuritiesTradeUpdate.tactical);
        Assert.assertEquals(bcSecuritiesTrade, bcSecuritiesTradeUpdate);

        bcSecuritiesTrade = BCSecuritiesTradeMapper.INSTANCE.updateOrCreateInputBy("123", null);
        Assert.assertNotNull(bcSecuritiesTrade);
        Assert.assertEquals(bcSecuritiesTrade.tactical.inputBy, "123");

        bcSecuritiesTradeUpdate = BCSecuritiesTradeMapper.INSTANCE.updateOrCreateInputBy("124", bcSecuritiesTrade);
        Assert.assertNotNull(bcSecuritiesTradeUpdate);
        Assert.assertEquals(bcSecuritiesTradeUpdate.tactical.inputBy, "124");
        Assert.assertEquals(bcSecuritiesTrade, bcSecuritiesTradeUpdate);

    }
}

使用的实体:

public class BCSecuritiesTrade {
    public Tactical tactical;
}

public class Tactical {
    public String inputBy;
}

备选方案:
CreateUpdate 创建两个单独的方法。

@Mapper(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
public interface BCSecuritiesTradeMapper {
    BCSecuritiesTradeMapper INSTANCE = Mappers.getMapper(BCSecuritiesTradeMapper.class);

    @Mapping(source = "inputBy", target = "tactical.inputBy")
    BCSecuritiesTrade updateInputBy(String inputBy, @MappingTarget BCSecuritiesTrade bcSecuritiesTrade);

    @Mapping(source = "inputBy", target = "tactical.inputBy")
    BCSecuritiesTrade createInputBy(String inputBy);
}