JPA를 이용해서 구현하는 프로젝트에서 두 개의 프로퍼티를 묶어서 식별자로 사용해야 하는 경우가 생겼다. 그래서, @EmbeddedId를 이용해서 ID 필드를 지정하였다. 이 프로젝트는 Spring Data JPA를 이용해서 Repository를 자동생성하고 있는데, @EmbeddedId 부분에서 문제가 발생했다. 아래 코드는 문제를 발생시킨 리포지토리의 인터페이스이다.
public interface FollowRepository extends Repository<Follow, FollowId> {
Page<Follow> findAll(Specification<Follow> spec, Pageable pageable);
...
}
Follow의 식별자의 타입은 FollowId 클래스이고, FollowId 클래스는 두 개의 프로퍼티를 갖고 있다. 그런데, 위 코드에서 findAll()을 실행하는 순간에 다음과 같은 쿼리가 실행되면서 문제가 발생했다.
select count((follow0_.FOLLOWING_ID, follow0_.USER_ID)) as col_0_0_
from FOLLOW follow0_
where ....
DBMS로 오라클을 사용하고 있는데, 위 count() 부분에서 쿼리 오류가 발생한 것이다. 처음엔 Spring Data JPA 문제일까 해서 커스텀 구현을 넣어 보았다.
public class FollowRepositoryImpl implements FollowRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public Page<Follow> findAll(Specification<Follow> spec, Pageable pageable) {
List<Follow> result = getResultList(spec, pageable);
long total = count(spec);
return new PageImpl<Follow>(result, pageable, total);
}
private List<Follow> getResultList(Specification<Follow> spec,
Pageable pageable) {
...
return query.getResultList();
}
private long count(Specification<Follow> spec) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> c = cb.createQuery(Long.class);
Root<Follow> root = c.from(Follow.class);
c.select(cb.count(root));
Predicate predicate = spec.toPredicate(root, c, cb);
c.where(predicate);
TypedQuery<Long> query = entityManager.createQuery(c);
return query.getSingleResult();
}
}
하지만, 결과는 동일했다. JPA API를 직접 사용해도 변화가 없는 걸 봐서는 Spring Data JPA의 문제는 아니였다. Spring Data JPA 1.1 버전을 사용하고 있었는데, 이 버전은 하이버네이트 3.6.9를 기준으로 하고 있어서 해당 버전의 하이버네이트를 사용했었다.
하이버네이트의 버전 문제가 아닐까해서 범위를 좀 더 좁혀서 구글링을 해 봤더니 하이버네이트에서 이런 문제가 발생하고 있는 듯 했다. (관련 문의가 포럼에 있었다.) 좀 더 파 볼까 하다가, 하이버네이트 버전을 올려보기로 결심했다. 하이버네이트 버전을 3.6.9에서 4.1.4로 올렸다.
결과는? 야호! 테스트케이스의 녹색바! 에러가 없어졌다. 처음부터 하이버네이트 버전 업부터 할 걸, 괜히 코드 작성했다. 오픈소스에서 뭔가 이상한 문제가 있으면 먼저 버전을 올려보라는 걸 다시 한 번 확인했다.