티스토리 뷰

728x90
반응형

Mapstruct를 통해 객체 매핑 작업을 할 때 필드명은 같지만 타입이 다를 때 타입변환이 자동으로 되는데

String -> int 필드로 변환할 때는 주의해야한다.

@NoArgsConstructor
@AllArgsConstructor
@Data
@SuperBuilder
public class WishListDto {
    private Integer index;
    private String title;
    private String category;
    private String address;
    private String roadAddress;
    private String homePageLink;
    private String imageLink;
    private boolean isVisit;
    private int visitCount;
    private LocalDateTime lastVisitDate;
    private String sameFieldName;	//타입은 다르고 변수명은 동일한 필드
}

@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
@Table(name = "WishList")
@SequenceGenerator(name="RES_SEQ_GEN", sequenceName = "RES_SEQ", initialValue = 1, allocationSize = 1)
public class WishListEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "RES_SEQ_GEN")
    private Integer index;

    private String title;
    private String category;
    private String address;
    private String roadAddress;
    private String homePageLink;
    private String imageLink;
    private boolean isVisit;
    private int visitCount;
    @UpdateTimestamp
    private LocalDateTime lastVisitDate;
    private int sameFieldName;	//타입은 다르고 변수명은 동일한 필드
}

Mapper Interface

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface WishListMapper extends EntityMapper<WishListDto, WishListEntity> {
    WishListMapper INSTANCE = Mappers.getMapper(WishListMapper.class);

    WishListDto toDto(WishListEntity wishListEntity);

    WishListEntity toEntity(WishListDto wishListDto);
}

Compile된 Mapper 구현체

public class WishListMapperImpl implements WishListMapper {

    @Override
    public WishListDto toDto(WishListEntity wishListEntity) {
        if ( wishListEntity == null ) {
            return null;
        }

        WishListDto.WishListDtoBuilder<?, ?> wishListDto = WishListDto.builder();

        wishListDto.index( wishListEntity.getIndex() );
        wishListDto.title( wishListEntity.getTitle() );
        wishListDto.category( wishListEntity.getCategory() );
        wishListDto.address( wishListEntity.getAddress() );
        wishListDto.roadAddress( wishListEntity.getRoadAddress() );
        wishListDto.homePageLink( wishListEntity.getHomePageLink() );
        wishListDto.imageLink( wishListEntity.getImageLink() );
        wishListDto.visitCount( wishListEntity.getVisitCount() );
        wishListDto.lastVisitDate( wishListEntity.getLastVisitDate() );
        wishListDto.sameFieldName( String.valueOf( wishListEntity.getSameFieldName() ) );		//필드 타입이 다를 때 형변환

        return wishListDto.build();
    }

    @Override
    public WishListEntity toEntity(WishListDto wishListDto) {
        if ( wishListDto == null ) {
            return null;
        }

        WishListEntity wishListEntity = new WishListEntity();

        wishListEntity.setIndex( wishListDto.getIndex() );
        wishListEntity.setTitle( wishListDto.getTitle() );
        wishListEntity.setCategory( wishListDto.getCategory() );
        wishListEntity.setAddress( wishListDto.getAddress() );
        wishListEntity.setRoadAddress( wishListDto.getRoadAddress() );
        wishListEntity.setHomePageLink( wishListDto.getHomePageLink() );
        wishListEntity.setImageLink( wishListDto.getImageLink() );
        wishListEntity.setVisit( wishListDto.isVisit() );
        wishListEntity.setVisitCount( wishListDto.getVisitCount() );
        wishListEntity.setLastVisitDate( wishListDto.getLastVisitDate() );
        if ( wishListDto.getSameFieldName() != null ) {
            wishListEntity.setSameFieldName( Integer.parseInt( wishListDto.getSameFieldName() ) );		//필드 타입이 다를 때 형변환
        }

        return wishListEntity;
    }
}

String 변수를 null 체크는 하지만 "" 공란일 때는 체크하지 않고 Integer.parseInt()를 타게 되면 NumberFormatException 발생된다.

class WishListDtoTest {

    @Test
    void testMapToDto_whenStringFieldIsNotEmpty() {
        WishListDto dto = WishListDto.builder()
                .index(1)
                .title("title")
                .address("address")
                .sameFieldName("0") // String 필드에 "0"
                .build();
        WishListEntity entity = WishListMapper.INSTANCE.toEntity(dto);

        assertThat(entity.getSameFieldName()).isEqualTo(0);
    }

    @Test
    void testMapToDto_whenStringFieldIsNull() {
        WishListDto dto = WishListDto.builder()
                .index(1)
                .title("title")
                .address("address")
                .sameFieldName(null) // String 필드에 null
                .build();

        WishListEntity entity = WishListMapper.INSTANCE.toEntity(dto);

        assertThat(entity.getSameFieldName()).isEqualTo(0);
    }

    @Test
    void testMapToDto_whenStringFieldIsEmpty() {
        WishListDto dto = WishListDto.builder()
                .index(1)
                .title("title")
                .address("address")
                .sameFieldName("") // String 필드에 공란
                .build();

        assertThatThrownBy(() -> {
            WishListEntity entity = WishListMapper.INSTANCE.toEntity(dto);      //Mapstruct 객체 매핑 시 NumberFormatException
        }).isInstanceOf(NumberFormatException.class);
    }

}

Mapstruct mapper interface에서 String -> int 형변환을 qualifiedByName을 사용해 명시적으로 선언

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface WishListMapper extends EntityMapper<WishListDto, WishListEntity> {
    WishListMapper INSTANCE = Mappers.getMapper(WishListMapper.class);

    WishListDto toDto(WishListEntity wishListEntity);

    @Mapping(target = "sameFieldName", source = "sameFieldName", qualifiedByName = "mapToInteger")
    WishListEntity toEntity(WishListDto wishListDto);

    @Named("mapToInteger")
    default int mapToInteger(String str) {
        if (str == null || str.isEmpty()) {
            return 0;
        }
        return Integer.parseInt(str);
    }
}

Compile된 Mapper 구현체

public class WishListMapperImpl implements WishListMapper {

    @Override
    public WishListDto toDto(WishListEntity wishListEntity) {
        if ( wishListEntity == null ) {
            return null;
        }

        WishListDto.WishListDtoBuilder<?, ?> wishListDto = WishListDto.builder();

        wishListDto.index( wishListEntity.getIndex() );
        wishListDto.title( wishListEntity.getTitle() );
        wishListDto.category( wishListEntity.getCategory() );
        wishListDto.address( wishListEntity.getAddress() );
        wishListDto.roadAddress( wishListEntity.getRoadAddress() );
        wishListDto.homePageLink( wishListEntity.getHomePageLink() );
        wishListDto.imageLink( wishListEntity.getImageLink() );
        wishListDto.visitCount( wishListEntity.getVisitCount() );
        wishListDto.lastVisitDate( wishListEntity.getLastVisitDate() );
        wishListDto.sameFieldName( String.valueOf( wishListEntity.getSameFieldName() ) );

        return wishListDto.build();
    }

    @Override
    public WishListEntity toEntity(WishListDto wishListDto) {
        if ( wishListDto == null ) {
            return null;
        }

        WishListEntity wishListEntity = new WishListEntity();

        wishListEntity.setSameFieldName( mapToInteger( wishListDto.getSameFieldName() ) );
        wishListEntity.setIndex( wishListDto.getIndex() );
        wishListEntity.setTitle( wishListDto.getTitle() );
        wishListEntity.setCategory( wishListDto.getCategory() );
        wishListEntity.setAddress( wishListDto.getAddress() );
        wishListEntity.setRoadAddress( wishListDto.getRoadAddress() );
        wishListEntity.setHomePageLink( wishListDto.getHomePageLink() );
        wishListEntity.setImageLink( wishListDto.getImageLink() );
        wishListEntity.setVisit( wishListDto.isVisit() );
        wishListEntity.setVisitCount( wishListDto.getVisitCount() );
        wishListEntity.setLastVisitDate( wishListDto.getLastVisitDate() );

        return wishListEntity;
    }
}

 "" 공란일 때도 0으로 정상 변환

@Test
void testMapToDto_whenStringFieldIsEmpty() {
    WishListDto dto = WishListDto.builder()
            .index(1)
            .title("title")
            .address("address")
            .sameFieldName("") // String 필드에 공란
            .build();
    WishListEntity entity = WishListMapper.INSTANCE.toEntity(dto);
    
    assertThat(entity.getSameFieldName()).isEqualTo(0);
}

 

 

https://mapstruct.org/documentation/stable/reference/html/#selection-based-on-qualifiers

 

MapStruct 1.5.5.Final Reference Guide

If set to true, MapStruct in which MapStruct logs its major decisions. Note, at the moment of writing in Maven, also showWarnings needs to be added due to a problem in the maven-compiler-plugin configuration.

mapstruct.org

 

728x90
반응형
반응형
300x250