주어진 상황은 1:N(nullable) 관계의 A와 B란 두 테이블을 조인하여 특정 조건을 만족하는 A의 row의 수를 구하는 것 입니다. 특정 조건은 A의 조건일 수도 있고 B의 조건일 수도 있습니다.
이런 경우 A 주체의 왼쪽 외부 조인을 사용하게 됩니다. B 테이블과 관계를 맺지 않은 A의 레코드에서도 조건을 만족할 수 있기 때문입니다. 이 쿼리는 중복된 A의 레코드를 생기게 합니다.
그래서 중복을 제거하여야 합니다!
중복을 제거하는 방법으로 많이 사용되는 Hiberante 코드는 setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)입니다.
Projection으로 인한 요소버림없이 전체 데이터를 쉽게 가지고 올 수 있기 때문입니다.
그러나 결론부터 말하자면 조인 테이블에서 row Count를 구하는데에는 setProjection(Projections.countDistinct("id"))을 사용하시기 바랍니다.
setResultTransformer는 쿼리의 결과를 변환하는 옵션이기때문입니다.
쉽게 알기위한 예제를 만들어보았습니다.
<category>
<post>
블로그의 category와 post 테이블이 있습니다. 상태가 CLOSE인 포스트를 가지고있는 카테고리를 구하려고 합니다.
1 2 3 4 5 6 | sessionFactory.getCurrentSession() .createCriteria(Category.class) .createAlias("post", "p") .add(Restrictions.eq("p.state", "CLOSE")) .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) .list(); | cs |
위 Hibernate 코드를 실행하면
으로 결과가 잘 나옵니다.
그러나 row count를 구하기위한 Projection을 추가하면
1 2 3 4 5 6 7 | (Long)sessionFactory.getCurrentSession() .createCriteria(Category.class) .createAlias("post", "p") .add(Restrictions.eq("p.state", "OPEN")) .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) .setProjection(Projections.rowCount()) .uniqueResult(); | cs |
입니다.
위와 같은 결과가 나오는 이유는 setResultTransformer는 쿼리의 결과를 변환하는 옵션이기때문입니다.
hibernate 옵션으로 sql 쿼리로그를 찍어보면 확실히 알 수 있습니다.
1 2 3 4 5 6 | (Long)sessionFactory.getCurrentSession() .createCriteria(Category.class) .createAlias("post", "p") .add(Restrictions.eq("p.state", "OPEN")) .setProjection(Projections.countDistinct("id")) .uniqueResult(); | cs |
딱 원하는 쿼리가 실행됩니다!(결과도 1로 잘 나오네요!)
이 글이 작성된 배경은 조인된 테이블에서 특정 조건들의 검색 결과를 pagination하기 위해 count를 작성하면서 겪었던 삽질을 공유하기 위함입니다. generic한 환경에서 공용 criteria 생성부를 사용하여 List와 Count를 모두 구하려하다 벌어진 삽질입니다
댓글