ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [MapStruct] 기본 매핑
    자바/mapstruct 2020. 11. 26. 00:42

    간단하게 Mapper를 생성하는 방법은 필요한 매핑 함수를 가진 자바 인터페이스를 정의하고, org.mapstruct.Mapper 어노테이션을 붙이면 됩니다.

     

    @Mapper
    public interface CarMapper {
    
        @Mapping(source = "make", target = "manufacturer")
        @Mapping(source = "numberOfSeats", target = "seatCount")
        CarDto carToCarDto(Car car);
    
        @Mapping(source = "name", target = "fullName")
        PersonDto personToPersonDto(Person person);
    }

    @Mapper 어노테이션은 MapStruct의 code generator를 통해서 빌드 시점에 CarMapper 인터페이스에 정의된 함수에 대한 구현체 파일을 생성합니다.

    생성된 함수 구현체 파일에서, 소스 타입(e.g. Car)에 있는 모든 읽기 가능한 프로퍼티들은 타겟 타입(e.g. CarDto)에 있는 연관된 프로퍼티로 복사되어집니다. 

    • 프로퍼티 이름이 대상 엔티티와 동일한 경우, 암묵적으로 매핑되어 집니다.
    • 프로퍼티 이름이 대상 엔티티와 다를 경우, @Mapping 어노테이션을 통해서 명시해주어야 합니다.

    JavaBeans 스펙에 정의되어 있는 프로퍼티 이름은 반드시 @Mapping 어노테이션에 명시되어져야 합니다.

    e.g. seatCount 프로퍼티에 대해 accessor 함수인 getSeatCount()와 setSeatCount()이 있어야 합니다.

     

    @BeanMapping(ignoreByDefault = true)에 의해서 기본적인 동작은 명시적으로 매핑을 선언해야 합니다. 즉, 모든 매핑은 @Mapping 어노테이션에 의해서 명시되어야하고 빠진 타겟 프로퍼티에 대해서는 어떤 경고도 표시되지 않습니다.

     

    Fluent Setter 또한 제공되어집니다. Fluent Setter는 타입이 변경되어 질 때 같은 타입을 리턴하도록 하는 Setter 함수 입니다.

    해당 기능은 Lombok의 @Builder 어노테이션과 같습니다.

    E.g.

    public Builder seatCount(int seatCount) {
        this.seatCount = seatCount;
        return this;
    }

     

    MapStruct의 기능에 대한 더 나은 이해를 돕기 위해서 MapStruct에 의해 생성되어진  아래 carToCarDto()  함수의 구현부분을 보도록 하겠습니다.

    // GENERATED CODE
    public class CarMapperImpl implements CarMapper {
    
        @Override
        public CarDto carToCarDto(Car car) {
            if ( car == null ) {
                return null;
            }
    
            CarDto carDto = new CarDto();
    
            if ( car.getFeatures() != null ) {
                carDto.setFeatures( new ArrayList<String>( car.getFeatures() ) );
            }
            carDto.setManufacturer( car.getMake() );
            carDto.setSeatCount( car.getNumberOfSeats() );
            carDto.setDriver( personToPersonDto( car.getDriver() ) );
            carDto.setPrice( String.valueOf( car.getPrice() ) );
            if ( car.getCategory() != null ) {
                carDto.setCategory( car.getCategory().toString() );
            }
            carDto.setEngine( engineToEngineDto( car.getEngine() ) );
    
            return carDto;
        }
    
        @Override
        public PersonDto personToPersonDto(Person person) {
            //...
        }
    
        private EngineDto engineToEngineDto(Engine engine) {
            if ( engine == null ) {
                return null;
            }
    
            EngineDto engineDto = new EngineDto();
    
            engineDto.setHorsePower(engine.getHorsePower());
            engineDto.setFuel(engine.getFuel());
    
            return engineDto;
        }
    }
    

    MapStruct의 일반적인 철학은 가능한 매우 프로그래머가 직접 작성한 것 처럼 보이는 코드를 만들어내는 것입니다. 

    특별히 MapStruct는 리플렉션 대신 getter/setter를 통해서 소스에서 타겟으로 값들을 복사합니다.

     

    예제가 보여주는 것처럼 생성된 코드는 @Mapping에 의해 명시된 모든 이름 매핑을 고려합니다.

    만약 소스와 타겟 엔티티 사이에 매핑 된 속성의 타입이 다르면, MapStruct는 자동 변환(e.g. price 속성) 을 적용하거나 선택적으로 다른 매핑 함수를 호출 또는 생성(e.g. driver / engine 속성)합니다.

    MapStruct는 만약 소스와 타겟의 프로퍼티가 Bean의 프로퍼티이고, 그 자체가 Bean 또는 간단한 프로퍼티인 경우에만 새로운 매핑 함수를 생성합니다. 즉 Collection이나 Map 타입의 프로퍼티에서는 새로운 매핑 함수를 생성하지 않습니다.

     

    같은 element 타입을 가진 Collection 타입의 속성들은 소스 프로퍼티의 element를 포함하고 있는 타겟 Collection 타입의 새로운 인스턴스를 생성함으로써 복사되어진다.

    서로 다른 element 타입을 가진 Collection 타입의 속성들에 경우, 각각의 element들은 개별적으로 매핑되어지고 타겟 collection에 추가되어진다.

     

    MapStruct는 소스와 타겟 타입의 모든 public 프로퍼티들을 고려합니다. 이것은 super 타입에 선언된 프로퍼티들도 포함합니다.

     

     

     

    take ~ into account  v. ~를 고려하다, ~참작하다, ~를 안중에 두다
    invoke  v. 호출하다

     

     

    댓글

Designed by Tistory.