MapStruct 적용기

Hannah·2022년 7월 8일
0

kotlin-project

목록 보기
2/3
post-thumbnail

Kotlin에 MapStruct를 적용해보겠습니다~!

1. 의존성 주입 (gradle)

plugins {
    ...

    // mapStruct
    kotlin("kapt") version "1.6.21"
}

dependencies {
    ...

    // MapStruct
    implementation("org.mapstruct:mapstruct:1.5.1.Final")
    kapt("org.mapstruct:mapstruct-processor:1.5.1.Final")
    kaptTest("org.mapstruct:mapstruct-processor:1.5.1.Final")
}

2. Entity & DTO

@Entity
class Member(

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,

    var userNickName: String,
    var age: Int,

    @Enumerated(EnumType.STRING)
    var gender: Gender

) : BaseEntity() 
@Entity
class Score(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null,

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
    val member: Member,

    @Enumerated(EnumType.STRING)
    val subject: Subject,

    val score: Int,

) : BaseEntity()
data class ScoreDTO(
    val id: Long?,
    val memberId: Long?,
    val memberName: String,
    val subject: Subject,
    val score: Int,
    val creationDate: Date
)

3. MapStruct

@Mapper
interface ScoreMapper {
    @Mappings(
        Mapping(target = "id", ignore = true),
        Mapping(target = "memberId", source = "member.id"),
        Mapping(target = "memberName", source = "member.userNickName"),
        Mapping(target = "subject", source = "score.subject"),
        Mapping(target = "score", source = "score.score"),
        Mapping(target = "creationDate", expression = "java(new java.util.Date())")
    )
    fun toScoreDto(score: Score, member: Member): ScoreDTO
}

build(실행) 결과

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2022-07-08T15:31:12+0900",
    comments = "version: 1.5.1.Final, compiler: IncrementalProcessingEnvironment from kotlin-annotation-processing-gradle-1.6.21.jar, environment: Java 17.0.3 (Amazon.com Inc.)"
)
public class ScoreMapperImpl implements ScoreMapper {

    @Override
    public ScoreDTO toScoreDto(Score score, Member member) {
        if ( score == null && member == null ) {
            return null;
        }

        Subject subject = null;
        int score1 = 0;
        if ( score != null ) {
            subject = score.getSubject();
            score1 = score.getScore();
        }
        Long memberId = null;
        String memberName = null;
        if ( member != null ) {
            memberId = member.getId();
            memberName = member.getUserNickName();
        }

        Long id = null;
        Date creationDate = new java.util.Date();

        ScoreDTO scoreDTO = new ScoreDTO( id, memberId, memberName, subject, score1, creationDate );

        return scoreDTO;
    }
}

테스트까지 성공!

📛 public? private?

순탄하게 테스트 코드를 짜면서 돌리던중... 에러가 발생했습니다

C:\{filePath}\domain\author\AuthorMapper.java:10: warning: Unmapped target properties: "books, id, name, gender, age".
    public abstract com.example.kotlinserver.dto.author.AuthorDTO toAuthorDto(@org.jetbrains.annotations.NotNull

읭?? 뭐지 하다가 값을 분명히 넘겼는데 에러가 뜨니 당황스러웠습니다
어떤게 문제일까 코드를 분석하던 도중

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2022-07-08T16:00:02+0900",
    comments = "version: 1.5.1.Final, compiler: IncrementalProcessingEnvironment from kotlin-annotation-processing-gradle-1.6.21.jar, environment: Java 17.0.3 (Amazon.com Inc.)"
)
public class AuthorMapperImpl implements AuthorMapper {

    @Override
    public AuthorDTO toAuthorDto(Author author) {
        if ( author == null ) {
            return null;
        }

        Long id = null;
        String name = null;
        Gender gender = null;
        int age = 0;
        List<BookDTO> books = null;

        AuthorDTO authorDTO = new AuthorDTO( id, name, gender, age, books );

        return authorDTO;
    }
}

뭔가 잘못됐다는걸 느꼈습니다 보통의 mapper라면 분명 값을 세팅하는 로직이 있을텐데 그 로직이 없던것이었습니다 그렇게 분석 도중... 원인을 찾았습니다!!

@Entity
@TypeDef(name = "json", typeClass = JsonType::class)
class Author(
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private val id: Long? = null,
    private val name: String,
    private val gender: Gender,
    private val age: Int,

    @Type(type = "json")
    @Column(columnDefinition = "json")
    private var books: List<Book>

) : BaseEntity()

entity에 접근제어가 필요하다고 생각하는 저는 private을 붙인 상황이었고 MapStruct는 만들어지는 대상은 Getter, 만드는 대상은 Setter가 필요했기때문에 에러가 났던 것 입니다
아무래도 java 기반이다 보니 그런것 같아 private을 빼고 테스트 코드를 돌려보니 성공했습니다 :-)

converter하는 작업은 극히 개인적인 성향, 취향이라고 생각하기때문에 팀원들과 코드 스타일을 맞출 때 협의하는게 좋을것 같습니다

추가

저번에 포스트했던 json type으로 저장한게 생각나서 해봤습니다
많은 삽질을 하겠다고 예상했지만... 예상외로 간단하게 되서 놀랐습니다...

@Mapper
interface AuthorMapper {
    fun toAuthorDto(author: Author): AuthorDTO
}

레퍼런스 주소
(++ 참고 사이트)
https://mapstruct.org/documentation/stable/reference/html/
https://mangchhe.github.io/spring/2021/01/25/ModelMapperAndMapStruct/


읽어주셔서 감사합니다🥰🥰🥰

좌충우돌 kotlin spring boot project 생성기 https://github.com/HongChaeMin/kotlin/tree/main/kotlinServer

profile
backend developer

0개의 댓글