[Error] JPQL에서 엔티티조인 후 내부의 엔티티 조회
JPQL은 일반적인 SQL문이랑 미묘하게 다른점들이 있다. 물론 쿼리를 잘 짜는 사람이라면 JPQL도 금방 익숙해질 수 있겠지만 문제는 익숙해지는 과정에 있을 수 있다. 기본문법을 숙지하고 사용한다고 한들 응용영역에서 에러를 만나면 삽질을 할 수 밖에 없기 때문에... 바로 나처럼
3개의 엔티티가 사용되었고 2개의 엔티티를 조인한 후 조인된 엔티티에 속해있는 다른 엔티티를 조건으로 조회하는 방법이다.
먼저 엔티티 코드이다. 예제를 위한 내용만 입력하겠다.
@Entity
public class Share extends BaseTimeEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "share_id")
private Long id;
@OneToMany(mappedBy = "share", cascade = CascadeType.PERSIST)
private List<ShareMember> shareMember = new ArrayList<>();
}
@Entity
public class ShareMember {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne
@JoinColumn(name = "share_id")
private Share share;
}
@Entity
public class Member extends BaseTimeEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
}
이렇게 3개의 엔티티를 이용했다.
ShareRepository에서 Share와 ShareMember를 조인한 후 ShareMember에 있는 member를 조건문으로 출력하는 쿼리를 만들어야 했다.
아무래도 이전 회사에서 MyBatis와 MSSQL을 이용해서 프로젝트를 했다보니 나의 머리와 본능은 일반적인 쿼리문에 너무나도 익숙해져 있었다. 그렇다보니 JPQL의 기본문법을 공부했음에도 그것과 조금만 벗어나면 일반 쿼리문의 습관처럼 작성해버리는 것이다. 하지만 JQPL은 철저하게 객체를 중심으로 작성해야한다. from 뒤에 나오는 테이블명도 DB안에 생성된 테이블명이 아닌 직접 만든 엔티티의 이름을 넣어야 되는 것 처럼 말이다. 이런게 너무 헷갈렸다.
처음에 짠 쿼리는 아래와 같다.
@Query("select s from Share s join s.shareMember sm on sm.member_id = :member_id")
List<Share> findList(@Param("member_id") Long member_id);
음... 스스로 느끼기에 너무나도 자연스러웠다. 하지만 실행시켰을 때 member_id를 찾을 수 없다는 에러가 나타나기 시작했다..!
분명 member엔티티에 id컬럼명을 member_id로 변경했고 생성되는 테이블을 살펴봐도 share_member테이블에는 분명히 member_id가 들어있었다. 그런데도 계속 member_id라는 컬럼을 찾을 수 없다고 해서 이리저리 찾아해매다가 문득 드는 생각이 엔티티 중심으로 동작한다면 member_id가 아니라 member객체를 넘겨야 하는게 아닐까... 라는 생각이 들었고 바로 쿼리를 변경하고 테스트 해봤다.
@Query("select s from Share s join s.shareMember sm on sm.member = :member")
List<Share> findList(@Param("member") Member member);
service에서 미리 member객체를 조회하고 그 객체를 파라미터로 넘겨서 조건문으로 쿼리를 만들어봤다.
결과는 성공적이었다.
일반적인 쿼리를 작성할때는 상상도 못하던 일이지만 여기서는 이렇게 사용해야 하는 것이었다.
비슷한 문제가 하나 더 있었다.
위에서 만든 member엔티티에서 id값으로 조회하는 쿼리를 JPQL로 작성할려면 어떻게 해야할까?
보통은 메소드 이름으로 쿼리를 생성해버리기때문에 단일 엔티티에서 id값으로 조회하는 JPQL을 만들일은 거의 없을 것이다. 하지만 여러 테이블이 조인되면서 만들일이 생겼는데 이때 member엔티티에 id의 이름을 member_id로 지정해서 조건문에도 member_id를 넣었다. 그랬더니 또 member_id를 찾을 수 없다는 것이다.
설마설마 싶어서 그냥 id를 넣었는데 제대로 동작하였다...
select m from Member m join m.board where m.id = :member_id
예제로 작성한 쿼리이지만 저런식으로 작성해야한다. 내가 컬럼명을 member_id로 지정했다면 이것은 db에 저장될 때 적용이 되는 것이고 JPQL에서는 철저하게 엔티티에 선언한 내용을 토대로 작성해야하는 것이다.
쿼리를 많이 다뤄보지 않은 사람들은 아직 일반적인 쿼리에 적응이 안되있기 때문에 실수하지 않을 수도 있겠지만 이미 적응해버린 나같은 경우는 이런점이 너무나도 헷갈렸다. 처음 JPA를 배우고 처음 JPA를 이용해서 만드는 프로젝트다보니 시행착오가 계속 생기는 것 같지만 오히려 좋다. 진짜 배움은 성공이 아닌 실패속에서 나타날 것이다.
'에러모음' 카테고리의 다른 글
JPA필수코스 N+1문제 해결하기 (0) | 2022.06.25 |
---|---|
스프링부트 gradle build 실패 (0) | 2022.06.15 |
[Error] Mockito를 사용한 단위테스트(form데이터 전송) (0) | 2022.05.17 |
[Error] TransientPropertyValueException에러.. (0) | 2022.05.02 |
[Error] Form태그 안에 버튼이 있다면 버튼의 역할은? (0) | 2022.04.21 |
댓글
이 글 공유하기
다른 글
-
스프링부트 gradle build 실패
스프링부트 gradle build 실패
2022.06.15 -
[Error] Mockito를 사용한 단위테스트(form데이터 전송)
[Error] Mockito를 사용한 단위테스트(form데이터 전송)
2022.05.17 -
[Error] TransientPropertyValueException에러..
[Error] TransientPropertyValueException에러..
2022.05.02 -
[Error] Form태그 안에 버튼이 있다면 버튼의 역할은?
[Error] Form태그 안에 버튼이 있다면 버튼의 역할은?
2022.04.21