통계에서 사용하는 쿼리를 만들다보니, (약간은) 부득이 하게 JPA Query Method를 사용하지 않고 @Query annotation을 이용해서 아래처럼 구현하였다.
1 2 3 4 5 |
@Query(value = "SELECT COUNT(id) " + "FROM FooObject fooObject " + "WHERE {some_condition}") List<Integer> findCountByIdAndDateRangeGroupBySomeCondition( @Param("id") String id, @Param("localDate") LocalDate localDate); |
호출하는 쪽의 로직은 아래와 같다.
1 2 3 4 5 6 7 8 9 |
private void checkAllTypeIsAvailable(String id, LocalDate localDate) { List<Integer> counts = fooRepository.findCountByIdAndDateRangeGroupBySomeCondition(id, localDate); for (int count : counts) { // TODO by freeism: long -> int class cast exception if (count % 2 != 0) { throw new CustomException(ErrorCode.INVALID_PARAMETER); } } } |
자세한 로직은 회사 업무상 대외비이기 때문에 약간 이상한 네이밍인 점은 신경쓰지 않기로 하자.
어쨌거나 위의 로직을 돌리면 오류가 발생한다.
1 2 3 4 5 |
java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer at kr.co.freeism.validator.ParamChecker.checkAllTypeIsAvailable(SettlementAdjustParamChecker.java:60) at kr.co.freeism.validator.ParamChecker.checkTargetPaymentMethodType(SettlementAdjustParamChecker.java:43) at kr.co.freeism.validator.ParamChecker$$FastClassBySpringCGLIB$$e7af0b5d.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) |
for (int count : counts) 에서 ClassCastException이 발생한다.
심지어 List<Integer> counts 는 정상적으로 받아왔는데, for문에서 오류가 나는 것이 좀 당황스러웠다.
그래서 JPA 스펙을 찾아보니 아래처럼 기술되어 있다.
JSR338 (Java Persistence API v2.1)에 따르면 Count() 를 이용한 쿼리의 return value는 Long으로 정의되어 있다. 그리고 Count에 대한 결과가 존재하지 않을 경우에는 0을 반환하도록 되어 있다. ( sum() 의 경우는 null이 return된다)
결국 이 모든 것은 지정된 type(여기서는 Integer)으로 return 해주는 mybatis에 익숙했던 것이 오류였다.
다만, 위에 적은 것처럼 List<Integer> counts 로 return 값을 assign 할 때 오류가 나는 것이 아니라, 해당 값을 사용하려 할 때 오류가 나는 점은 여전히 설계에 문제가 있지 않나 생각해본다.
추가로 위 counts를 getClass() 해보면 class java.lang.Long 으로 표시된다. 분명 generic은 Integer인데도 말이다.
제네릭은 컴파일 시점에서 처리되기 때문에 그런 것 같습니다 ㅎㅎ
네 그런 것 같습니다. 댓글 감사합니다^^