10

Is it possible to propagate value form a parent object to collection of nested objects? For example

Source DTO classes

class CarDTO {
  private String name;
  private long userId;
  private Set<WheelDto> wheels; 
};

class WheelDto {
  private String name;
}

Target entity classes

class Car {
  private String name;
  private long userId;
  private Set<Wheel> wheels; 
};

class Wheel {
  private String name;
  private long lastUserId;
}

As you could see I do not have lastUserId on WheelDto, hence I would like to map userId from CarDto to lastUserId on WheelDto on each object in a wheels collection I tried

@Mapping(target = "wheels.lastUserId", source = "userId") 

but no luck

antohoho
  • 950
  • 2
  • 17
  • 37

1 Answers1

17

Currently it is not possible to pass a property. However, you could solve this via @AfterMapping and / or @Context.

Update Wheel after Car mapping

This would though mean that you would need to iterate twice over the Wheel. It can look like

@Mapper
public interface CarMapper {

    Car map(CarDto carDto);

    @AfterMapping
    default void afterCarMapping(@MappingTarget Car car, CarDto carDto) {
        car.getWheels().forEach(wheel -> wheel.setLastUserId(carDto.getUserId()));
    }
}

Pass @Context while mapping wheel to have state during the mapping

If you want to iterate only once through the Wheel then you can pass a @Context object that would get the userId from the CarDto before mapping the car and then set it on the Wheel in an after mapping. This mapper can look like:

@Mapper
public interface CarMapper {

    Car map(CarDto carDto, @Context CarContext context);

    Wheel map(WheelDto wheelDto, @Context CarContext context);

}

public class CarContext {

    private String lastUserId;

    @BeforeMapping
    public void beforeCarMapping(CarDto carDto) {
        this.lastUserId = carDto.getUserId();
    }

    @AfterMapping
    public void afterWheelMapping(@MappingTarget Wheel wheel) {
        wheel.setLastUserId(lastUserId);
    }

}

The CarContext would be passed to the wheel mapping method.

Filip
  • 19,269
  • 7
  • 51
  • 60
  • I used second approach. Good solution, works for me. – antohoho Dec 28 '18 at 00:22
  • @Filip Can you add an example of calling resulting mappers in "@Context" variant from mapper's client code? If I understand correctly, it would be something like `map(carDto, new CarContext())`, right? So you must always create a dummy context object explicitly from **client code**? – Ruslan Stelmachenko Oct 02 '20 at 16:45
  • And what if `CarMapper` will be used in another mapper using `@Mapper(uses = CarMapper.class)`? How that another mapper will call `Car map(CarDto carDto, @Context CarContext context);`? Where it will get the `CarContext` object? Or do I need to add this parameter in all other mappers, which will implicitly use `CarMapper`? – Ruslan Stelmachenko Oct 02 '20 at 17:24
  • I tried to use @AfterMapping like yesterday but I did something wrong, based on your example it started working. Thanks! – MrFisherman Feb 03 '23 at 17:10