Spring/Spring Boot

JPA DTO PROJECTION - 클래스 기반, 인터페이스 기반

민돌v 2022. 9. 15. 19:54

 

 

jpa 로 데이터를 불러올때, dto 혹은 엔티티말고 다른 무엇인가로 데이터를 담는방법 = 프로젝션

 


 

하고싶은 것

  1. queryDsl 을 사용하지 않고 간단하게 jpa 만을 사용해서 dto를 담고싶었습니다.
  2. interface 기반의 dto 프로젝션이 가능하다는 것을 알고 있지만 재사용성을위해 dto class 그대로 사용하고싶었습니다.

 

 

문제

  1. 하지만 컬럼명이 맞지않아서 오류가 났고 이를 해결...해야함
  2. 인터페이스 기반이 아닌 클래스 기반 프로젝션을 할 때 에러가 남
  • no converter found capable of converting from type [org.springframework.data.jpa.repository.query.abstractjpaquery$tupleconverter$tuplebackedmap] to type[dto] with root cause

 

@Value
public class dtoResponse {
    Integer idx;
    String code;
    String typeName;
}



 

1.  컬럼명이 맞지않는 오류 해결

➡️ jpa 에서 테이블명이 언더바일 때, 카멜케이스 변수명으로 자동 치환되는줄 알았는데 이것도 yaml 에서 설정을 해줘야했다...ㅠ

➡️ 귀찮아서, 깔금하게 @Column 어노테이션으로 테이블 명시, 해결

 

@Column(name = "type_name")
private String typeName;

 


 

2. 인터페이스 기반이 아닌 클래스 기반 프로젝션을 할 때 에러나는 문제

 

첫 시도

  • 해결방법 : https://tte-yeong.tistory.com/93
  • 간단한 방식이었지만 긴 시간 소비했다. 참고로 쿼리문을 수정하더라도 Dto를 사용하면 에러가 발생한다.
  • 즉 기존 코드에서 -> projection으로 인터페이스로 변환 -> AS로 이름 매핑 이 방식으로 해결했다.

 

📌  위 블로그 글을보고 인터페이스 프로젝션은 성공했지만 뭔가,, 의구심이 들고 마음에안들구 해서 몆가지 테스트를 해봄

 

해결방법 요약

  • 네이티브 쿼리로 인터페이스로 별칭사용
  • 인터페이스로 받음

 

 

public interface dtoResponse {

    Integer getIdx();

    String getCode();

    String getTypeName();
}

 

@Query(value = "select nt.idx as idx, nt.code as code, nt.type_name as typeName "
    + "from notice_type nt", nativeQuery = true)
List<dtoResponse> findAllNoticeType();

 

📌 근데 놀랍게도 별칭 안써도 결과가 나옴 ㄷ ㄷ

@Query(value = "select nt.idx, nt.code, nt.type_name from notice_type nt", nativeQuery = true)
List<NoticeTypeResponse> findAllNoticeType();

 

 

📌 신기해서 네이티브 쿼리 빼고 해봤더니 매칭이 안됩니다

 

 

📌 근데 별칭을 넣으니까 또 잘되네..?

@Query(value = "select nt.idx as idx, nt.code as code, nt.typeName as typeName from NoticeType nt")
List<NoticeTypeResponse> findAllNoticeType();

 

첫 시도 결론

  • 인터페이스 프로젝션은 네이티브 쿼리를 굳이 사용하지 않아도 됩니다
  • 단 네이티브 쿼리를 사용할때는 별칭을 사용하지 않아도 되지만, jpql을 사용할 때는 별칭을 사용해 주어야합니다
  • ❓ 네이티브 쿼리 안써도 되구 코드가 더 깔금해졌네여,, ㅎㅎ 구글만 너무 믿지말구 내가 직접 해보자구

 

 


 

두 번째 시도

인터페이스 프로젝션 말고, 초기 계획되로 재사용성을 위한 클래스 기반 프로젝션이 하고싶었다

jpa 버전이 올라가면서 바뀐건지, 공식문서내용을 참고해도 되지않음..

 

공식문서 기반 Class base projection 방법

  1. 생성자가 있어야함
  2. getter() 와 hashcode() equals() 가 구현되어있어얌
  3. 클래스 기반 프로젝션은 entity의 정보를 생성자의 파라미터 이름으로 매칭

 

public class PersonDto {
    private String firstName;
    private String lastName;

    public PersonDto(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    // getters, equals and hashCode
}

 

❓❓ 하지만 안된다고~~

 

해결 방법

jpql을 사용해서 클래스기반 프로젝션을 해주기 위해서, select 문에 dto 경로까지 지정해주니 해결이 되었씁니다..!!

@Query(value = "select new com.pood.server.facade.notice.response.NoticeTypeResponse (nt.idx, nt.code , nt.typeName) from NoticeType nt")
List<NoticeTypeResponse> findAllNoticeType();

 

-> new 로 직접 객체에 할당해주고 class 의 경로까지 지정해주어야 root 경로를 찾아가는 것 같습니다...

 

암튼 어찌저찌 해결 ㅠㅜ