MapStruct 사용하기

By | 2021년 1월 25일
Table of Contents

MapStruct 사용하기

MapStruct 를 이용하면 DTO – Entity 간 매핑을 간편하게 할 수 있고, 더불어 Entity 업데이트 코드를 매우 간단하게 줄일 수 있습니다.

의존성 추가

dependencies {
    // ......
    implementation 'org.mapstruct:mapstruct:1.4.1.Final'
    compileOnly 'org.projectlombok:lombok'
    compileOnly 'org.projectlombok:lombok-mapstruct-binding:0.2.0'
    annotationProcessor 'org.projectlombok:lombok'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.1.Final'
    // ......
}

DTO – Entity 생성

DTO 와 Entity 의 필드명을 1:1 로 일치시켜야 코딩이 편해집니다.


@Setter, @Getter, @NoArgsConstructor 가 추가되어 있는지 항상 확인한다.
에러의 95% 가 에너테이션 누락입니다.

@Setter
@Getter
@NoArgsConstructor
@Entity
public class Brand extends BaseTimeEntity implements Persistable<String> {

    @Id
    private String brandId;

    private String partnerId;
    private String brandName;
    private String useyn = "Y";

    public Brand(String brandId) {
        this.brandId = brandId;
    }

    @Override
    public String getId() {
        return brandId;
    }

    @Override
    public boolean isNew() {
        return getRegdate() == null;
    }
}
@Getter
@Setter
@NoArgsConstructor
public class BrandResponseDto {
    private String brandId;
    private String partnerId;
    private String brandName;
    private String useyn;
    private LocalDateTime regdate;
    private LocalDateTime lastupdate;
}

RequestDto 에는 insert/update 할 데이타만 남겨놓습니다.

@Getter
@Setter
@NoArgsConstructor
public class BrandCreateRequestDto {
    private String partnerId;
    private String brandName;
}
@Getter
@Setter
@NoArgsConstructor
public class BrandUpdateRequestDto {
    private String brandName;
    private String useyn;
}

GenericMapper 생성

DTO – Entity 간 필드명을 동일하게 생성한 경우 수동 매핑을 아예 안해도 됩니다.

updateFromDto() 는 Entity 업데이트시 값이 null 이 아닌 값만 업데이트 하도록 할 수 있습니다.

public interface GenericMapper<D, E> {
    D toDto(E e);
    List<D> toDto(List<E> e);
    void updateFromDto(D dto, @MappingTarget E entity);
}
@Mapper(
    componentModel = "spring",
    unmappedTargetPolicy = ReportingPolicy.IGNORE,
    nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
)
public interface BrandMapper extends GenericMapper<BrandDto, Brand> {
}

Service 생성

update 가 매우 간단해집니다.

@Service
public class BrandService {

    private final BrandMapper mapper = Mappers.getMapper(BrandMapper.class);

    // ......

    @Transactional
    public ResponseEntity<?> create(BrandCreateRequestDto dto) {
        Brand entity = new Brand();
        converter.updateFromDto(dto, entity);
        return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, converter.toDto(repository.save(entity))));
    }

    @Transactional
    public ResponseEntity<?> update(String brandId, BrandUpdateRequestDto dto) {
        Optional<Brand> brand = repository.findById(brandId);
        assert brand.isPresent();
        converter.updateFromDto(dto, brand.get());
        repository.save(brand.get());
        return ResponseEntity.ok(new ApiResponseMessage(ResponseCode.OK));
    }

    public ResponseEntity<?> get(String brandId) {
        Optional<Brand> brand = repository.findById(brandId);
        assert brand.isPresent();
        return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, converter.toDto(brand.get())));
    }

    public ResponseEntity<?> search(SearchBrandRequestDto dto) {
        Page<BrandResponseDto> dtos = repository.search(dto).map(converter::toDto);
        return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, dtos));
    }
}

뜬금없는 오류발생

이따금 Mapper 관련 오류가 뜬금없이 발생합니다.

이럴땐 grable build clean 후 다시 실행하면 정상적으로 실행됩니다.

답글 남기기