Spring/Spring Boot
JPA DTO PROJECTION - 클래스 기반, 인터페이스 기반
민돌v
2022. 9. 15. 19:54
jpa 로 데이터를 불러올때, dto 혹은 엔티티말고 다른 무엇인가로 데이터를 담는방법 = 프로젝션
하고싶은 것
- queryDsl 을 사용하지 않고 간단하게 jpa 만을 사용해서 dto를 담고싶었습니다.
- interface 기반의 dto 프로젝션이 가능하다는 것을 알고 있지만 재사용성을위해 dto class 그대로 사용하고싶었습니다.
문제
- 하지만 컬럼명이 맞지않아서 오류가 났고 이를 해결...해야함
- 인터페이스 기반이 아닌 클래스 기반 프로젝션을 할 때 에러가 남
- 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 방법
- 생성자가 있어야함
- getter() 와 hashcode() equals() 가 구현되어있어얌
- 클래스 기반 프로젝션은 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 경로를 찾아가는 것 같습니다...
암튼 어찌저찌 해결 ㅠㅜ