본문 바로가기
DataBase/Hibernate

Hibernate - setResultTransformer 주의 (조인 테이블 row count 구하기)

by Kingbbode 2016. 10. 5.

 주어진 상황은 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 쿼리로그를 찍어보면 확실히 알 수 있습니다.



 대략 이런 쿼리가 나옵니다. 실행된 쿼리에는 중복을 제거하는 DISTINCT가 없습니다. 중복이 제거되기 전 위 조건의 count 쿼리가 실행되어 count(*) 컬럼만이 결과로 나온 result set을 ResultTransformer로 등록해놓은  DISTINCT_ROOT_ENTITY 조건으로 중복을 제거하는 handling이 이루어진 것 입니다.

 setResultTransformer의 이름그대로 Result를 변환하는 이 기능을 잘 알고서 활용해야겠습니다!

 이제 ResultTransformer을 빼고 Projecton을 추가해보겠습니다.
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
 setProjection(Projections.countDistinct("id"))을 사용하면


딱 원하는 쿼리가 실행됩니다!(결과도 1로 잘 나오네요!)



 이 글이 작성된 배경은 조인된 테이블에서 특정 조건들의 검색 결과를 pagination하기 위해 count를 작성하면서 겪었던 삽질을 공유하기 위함입니다. generic한 환경에서 공용 criteria 생성부를 사용하여 List와 Count를 모두 구하려하다 벌어진 삽질입니다

 

댓글