티스토리 뷰
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
728x90
반응형
'Java' 카테고리의 다른 글
[Java] 단위테스트 Spock Framework (0) | 2024.05.09 |
---|---|
[Java] Mapstruct 사용 시 @Builder 패턴 객체 주의 사항 (0) | 2024.04.15 |
[Java] Mustache Template Rendering (0) | 2024.04.03 |
[Java] Object Mapping Frameworks 성능 비교 (0) | 2024.04.02 |
[Java] Mockito Framework Spy, ArgumentCaptor (0) | 2024.03.31 |
반응형
300x250