我还没有找到开箱即用的解决方案。
根据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
新版本有更好的实现。如果source 是null,则返回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;
}
替代解决方案:
为Create 和Update 创建两个单独的方法。
@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);
}