반응형
1. Mapstuct??
Java에서 데이터 매핑 작업을 쉽고, 빠르게 할 수 있는 라이브러리이다.
2. 설정 방법
build.gradle에 dependency를 추가한다.
- 주의! gradle version 4.6 미만은 설정 방식이 상이함. (참고 : https://mapstruct.org/documentation/installation/)
// mapstruct
implementation("org.mapstruct:mapstruct:1.5.3.Final")
annotationProcessor("org.mapstruct:mapstruct-processor:1.5.3.Final")
3. Mapper Interface 만들기
Model 생성 시 유의사항이 있다. 모델은 getter가 있어야 하고 setter 혹은 builder가 있어야한다.
만약 setter와 builder가 같이 있는 경우 builder가 사용된다.
Mapstruct에 사용된 Entity와 Dto 객체는 다음과 같다.
@Setter
@Getter
public class CarEntity {
private int carSeq;
private String carNum;
private String carName;
private String owner;
private CarTypeDto type;
private String insId;
private LocalDateTime insDtm;
private String modId;
private LocalDateTime modDtm;
private LocalDateTime dispStartDt;
private LocalDateTime dispEndDt;
private YnPolicy useYn;
private String local;
}
@Setter
@Getter
public class CarSaveRequestDto {
private String carNum;
private String carName;
private String owner;
private String color;
private String company;
private String userId;
private String dispStartDt;
private String dispEndDt;
}
@Setter
@Getter
public class CarSearchResponseDto {
private int carSeq;
private String carNum;
private String carName;
private String owner;
private String color;
private String company;
private String dispStartDt;
private String dispEndDt;
private String dispDt;
private String useYn;
private String useYnNm;
private String local;
}
맵핑을 하기위한 Mapstruct interface는 손쉽게 사용 가능하다.
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = MappingConstants.ComponentModel.SPRING)
public interface CarMapstruct {
// CarSaveRequestDto -> CarEntity로 매핑
CarEntity toCarDto(CarSaveRequestDto dto);
//CarEntity -> CarSearchResponseDto로 매핑
CarSearchResponseDto toCarSearchResponseDto(CarEntity dto);
List<CarSearchResponseDto> toCarSearchResponseDto(List<CarEntity> dto);
}
@Mapper 어노테이션을 붙이면 자동으로 Mapstruct가 CarMapstruct의 구현체를 생성해 주고 기본적으로 필드명이 동일하면 자동으로 변환하여 매핑됩니다.
이렇게 생성된 구현체는 다음과 같습니다.
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-12-13T10:57:35+0900",
comments = "version: 1.5.3.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-7.4.1.jar, environment: Java 17.0.6 (Amazon.com Inc.)"
)
@Component
public class CarMapstructImpl implements CarMapstruct {
@Override
public CarEntity toCarDto(CarSaveReqDto dto) {
if ( dto == null ) {
return null;
}
CarEntity carEntity = new CarEntity();
carEntity.setCarNum( dto.getCarNum() );
carEntity.setCarName( dto.getCarName() );
carEntity.setOwner( dto.getOwner() );
if ( dto.getDispStartDt() != null ) {
carEntity.setDispStartDt( LocalDateTime.parse( dto.getDispStartDt() ) );
}
if ( dto.getDispEndDt() != null ) {
carEntity.setDispEndDt( LocalDateTime.parse( dto.getDispEndDt() ) );
}
return carEntity;
}
@Override
public CarSearchResDto toCarSearchResponseDto(CarEntity dto) {
if ( dto == null ) {
return null;
}
CarSearchResDto carSearchResDto = new CarSearchResDto();
carSearchResDto.setCarSeq( dto.getCarSeq() );
carSearchResDto.setCarNum( dto.getCarNum() );
carSearchResDto.setCarName( dto.getCarName() );
carSearchResDto.setOwner( dto.getOwner() );
if ( dto.getDispStartDt() != null ) {
carSearchResDto.setDispStartDt( DateTimeFormatter.ISO_LOCAL_DATE_TIME.format( dto.getDispStartDt() ) );
}
if ( dto.getDispEndDt() != null ) {
carSearchResDto.setDispEndDt( DateTimeFormatter.ISO_LOCAL_DATE_TIME.format( dto.getDispEndDt() ) );
}
if ( dto.getUseYn() != null ) {
carSearchResDto.setUseYn( dto.getUseYn().name() );
}
carSearchResDto.setLocal( dto.getLocal() );
return carSearchResDto;
}
@Override
public List<CarSearchResDto> toCarSearchResponseDto(List<CarEntity> dto) {
if ( dto == null ) {
return null;
}
List<CarSearchResDto> list = new ArrayList<CarSearchResDto>( dto.size() );
for ( CarEntity carEntity : dto ) {
list.add( toCarSearchResponseDto( carEntity ) );
}
return list;
}
}
@Mapper안에 사용할 수 있는 속성들은 아래와 같다.
- unmappedTargetPolicy는 매핑 실패시 정책
- componentModel : 종속성 주입 시 사용.
4. Mapstruct의 다양한 활용법
Mapstructsms 필드명이 다르거나 맵핑을 제외하거나 하는 다양한 편의기능을 제공하고 있다.
여기서 전체기능에 대해 이야기하기 보단 간단하게 몇가지만 다뤄보고자 한다.
- source : 소스 필드
- targer : 타켓 필드
- ignore : 매핑시키지 않기
- expression : 직접 구현, 간단한 java 코드 사용 가능
- constant : 상수 값
- defaultValue : 디폴트값
- qualifiedByName : 별도 메소드 분리하여 복잡하게 매핑
- beforeMapping / afterMapping : 매핑 전후로 수행할 로직
- condition : 맵핑 시 모든 필드들에 대해 공통 함수를 적용할 때 사용 (null 이나 빈 값 체크 시 ) → 전체 필드에 적용되므로, 공통일 경우에만 쓸 것.
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = MappingConstants.ComponentModel.SPRING)
public interface CarMapstruct {
@Mapping(target = "carSeq", ignore = true)
@Mapping(target = "type.color", source = "color")
@Mapping(target = "type.company", source = "company")
@Mapping(target = "insId", source = "userId", defaultValue = "SYSTEM")
@Mapping(target = "modId", source = "userId")
@Mapping(target = "local", constant = "KOR")
@Mapping(target = "dispStartDt", source = "dispStartDt", qualifiedByName = "setStartTime")
@Mapping(target = "dispEndDt", source = "dispEndDt", qualifiedByName = "setEndTime")
CarEntity toCarDto(CarSaveRequestDto dto);
@Mapping(target = "owner", expression = "java(dto.getOwner() + \"님\")")
@Mapping(target = "color", source = "type.color")
@Mapping(target = "company", source = "type.company")
@Mapping(target = "dispStartDt", source = "dispStartDt", dateFormat = "yyyy-MM-dd")
@Mapping(target = "dispEndDt", source = "dispEndDt", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(target = "dispDt", source = ".", qualifiedByName = "setDisplayDate")
@Mapping(target = "useYnNm", source = "useYn.desc")
CarSearchResponseDto toCarSearchResponseDto(CarEntity dto);
List<CarSearchResponseDto> toCarSearchResponseDto(List<CarEntity> dto);
@Named("setStartTime")
default LocalDateTime setStartTime(String date) {
if (date == null) {
return null;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate ld = LocalDate.parse(date, formatter);
return ld.atStartOfDay(); //00:00:00
}
@Named("setEndTime")
default LocalDateTime setEndTime(String date) {
if (date == null) {
return null;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate ld = LocalDate.parse(date, formatter);
return ld.atTime(23, 59, 59); //23:59:59
}
@Named("setDisplayDate")
default String setDisplayDate(CarEntity dto) {
if (dto.getDispStartDt() == null || dto.getDispEndDt() == null) {
return null;
}
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
return dto.getDispStartDt().format(dtf) + " ~ " + dto.getDispEndDt().format(dtf);
}
@Mapping(target = "useYn", constant = "Y")
@Mapping(target = "user.venCd", source = "venCd")
ItemCateEntity toItemCateEntity(ItemCateSearchDto dto);
ItemCateGetDto toItemCateGetDto(ItemCateEntity dto);
List<ItemCateGetDto> toItemCateGetDto(List<ItemCateEntity> dto);
@AfterMapping
default void setSearchValue(@MappingTarget ItemCateEntity target, ItemCateSearchDto source) {
if (StringUtils.isAnyBlank(source.getSearchKeyword(), source.getSearchType())) {
return;
}
ItemCateSearchType searchType = ItemCateSearchType.valueOf(source.getSearchType().toUpperCase());
switch (searchType) {
case CATECD:
target.setItemCateCd(source.getSearchKeyword());
break;
case CATENM:
target.setItemCateNm(source.getSearchKeyword());
break;
}
}
@Condition
public static boolean isNotEmpty(String value) {
return value != null && !value.isEmpty();
}
}
반응형
'Backend > Java' 카테고리의 다른 글
Java - Generic(제네릭) 2편 - 제네릭제약(와일드카드, extends, super) (0) | 2022.05.13 |
---|---|
Java - Generic(제네릭) 1편 (0) | 2022.04.22 |
JAVA 버전별 특징(Java7~10) (0) | 2022.03.10 |
래퍼 클래스(wrapper class)? (0) | 2022.03.03 |
인터페이스를 사용하는 이유 (0) | 2016.12.13 |