인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

yeon _leaf님의 프로필 이미지
yeon _leaf

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

상속관계 매핑

Dtype의 종류에 따라 쿼리 조건을 다르게 넣고 싶습니다.

해결된 질문

작성

·

295

0

안녕하세요 영한님. 강의 정말 잘 듣고 있습니다.
강의 내용을 토대로 Plan과 Todo 엔티티를 사용하는 일정 관리 웹사이트를 만들고 있습니다.
상속 관계와 관련된 문제가 발생하여 도움을 얻을 수 있을까 해서 질문 남겨봅니다.
 
[엔티티 구조]
Plan 엔티티는 startDate 변수를 가지고 있습니다.
PlanRegular와 PlanTerm 엔티티는 Plan의 자식으로 Plan을 상속받습니다.
PlanRegular는 Plan을 단순히 상속받기만 하고 PlanTerm은 Plan을 상속받은 뒤 endDate라는 필드를 따로 가집니다.
 
이렇게 만든 이유는 기간이 있는 일정(term)과 없는 일정(regular)을 표현하기 위해서입니다.
 
상속 전략은
@Inheritance(strategy = InheritanceType.JOINED)
를 사용하고 있습니다.
Dtype을 객체에서 사용하기 위해서 dtype 변수를 읽기 전용으로 추가했습니다.
 
Plan 엔티티와 Todo 엔티티는 일대 다 연관관계를 형성하고 있습니다. planId를 외래 키로 사용합니다.
 
[질문 내용]
저는 Todo를 날짜별로 조회할 때 미리 Plan의 Dtype으로 regular인지 term인지를 판단하고 그에 따라 쿼리 조건을 달리 하고 싶습니다.
예를 들어 Todo를 2021년 12월 9일로 조회할 경우
매핑된 Plan이 regular인 경우 endDate가 없기 때문에 startDate가 2021년 12월 9일 이전인지 확인하고
매핑된 Plan이 term인 경우 startDate와 endDate 사이에 2021년 12월 9일이 있는지 확인합니다.
public List<Todo> getTodoByPlanIdAndDate_Regular(Plan plan, LocalDate date) {
if (plan.getDtype() == "regular") {
/*regular일 경우 startDate 조건만 걸기*/
return em.createQuery("select o from Todo o where o.plan.dtype = :dtype and o.plan.id =:planId and o.plan.startDate <= :date")
.setParameter("date", date)
.setParameter("planId", plan.getId())
.setParameter("dtype", plan.getDtype())
.getResultList();
}
else {
/*term일 경우 startDate, endDate 조건 둘 다 걸기*/
return em.createQuery("select o from Todo o where o.plan.dtype = :dtype and o.plan.id =:planId and o.plan.startDate <= :date and o.plan.endDate >= :date")
.setParameter("date", date)
.setParameter("planId", plan.getId())
.setParameter("dtype", plan.getDtype())
.getResultList();
}
}

코드로 표현하면 이런 식이 될 것 같습니다.

그런데 문제는 regular일 때 조건은 잘 걸리는데 term일 때 조건이 안 걸린다는 것입니다.

endDate를 resolve할 수 없다고 뜹니다. 저 코드에서는 o.plan.endDate로 조건을 걸었는데 부모인 Plan에는 endDate가 없어서 그런 것 같습니다.

Todo의 연관관계는 부모인 Plan으로 걸려 있기 때문에 자식인 PlanRegular나 PlanTerm으로 조회할 수도 없습니다.

이 문제를 해결하기 위해 Todo도 regular와 term으로 분리하고 모든 Todo 관련 메서드를 regular와 term을 따로 처리하도록 하는...그런 방법을 생각해 봤습니다.

하지만 중복되는 코드가 너무 많아지기도 하고

Plan과 다르게 Todo는 regular와 term이 다르게 가지고 있는 변수가 없어서(Plan의 endDate처럼) 상속을 적용하기는 적합하지 않을 것 같습니다.

이 문제를 해결할 수 있는 방법이 있을까요?

답변 1

0

yeon _leaf님의 프로필 이미지
yeon _leaf
질문자

자문자답입니다. 계속 구글링한 결과 찾았습니다^^... treat를 써서 엔티티를 다운캐스팅하면 되는 거였습니다. 더 좋은 방법이 있을지도 모르겠습니다만...

return em.createQuery("select o from Todo o join o.plan p where p.id =:planId and treat(p as PlanTerm).startDate <= :date and treat(p as PlanTerm).endDate >= :date")
.setParameter("date", date)
.setParameter("planId", plan.getId())
.getResultList();

아직 이 jpa 기본편을 끝까지 듣지 못해서 몰랐는데 이 강의 마지막쯤 객체지향 쿼리 언어 파트에도 나오네요.

역시 김영한님 강의입니다. 프로젝트를 하면서 들을 게 아니라 다 듣고 나서 프로젝트를 했어야 하나 봅니다. 혹시 참고할 사람이 있을지도 몰라서 글은 남겨놓겠습니다.

김영한님의 프로필 이미지
김영한
지식공유자

yeon _leaf님 스스로 잘 해결하셨습니다^^

yeon _leaf님의 프로필 이미지
yeon _leaf

작성한 질문수

질문하기