본문 바로가기
Framework & Lib & API/JPA

쿼리dsl, 코틀린 case when sum 구문에서 사용하기

by 코딩공장공장장 2024. 3. 6.

환경

  • 코틀린 1.9.22
  • springboot:3.2.2
  • querydsl:5.0.0:jakarta

Case when 구문 Sum과 함께 사용시 then 상수값에 타입 필요

case when sum 구문을 통해 집계 자료를 구하기 위해 쿼리를 실행하였더니

 

아래와 같은 에러가 나타났다.

 

java.lang.NullPointerException: Cannot invoke "org.hibernate.query.ReturnableType.getJavaType()" because "argType" is null
at org.hibernate.dialect.function.SumReturnTypeResolver.resolveFunctionReturnType(SumReturnTypeResolver.java:82)
at org.hibernate.query.sqm.function.SelfRenderingSqmFunction.resolveResultType(SelfRenderingSqmFunction.java:212)
at org.hibernate.query.sqm.function.SelfRenderingSqmFunction.resolveResultType(SelfRenderingSqmFunction.java:200)
at org.hibernate.query.sqm.function.SelfRenderingSqmAggregateFunction.convertToSqlAst(SelfRenderingSqmAggregateFunction.java:95)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitFunction(BaseSqmToSqlAstConverter.java:6193)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitFunction(BaseSqmToSqlAstConverter.java:440)
at org.hibernate.query.sqhttp://m.tree.expression.SqmFunction.accept(SqmFunction.java:66)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelection(BaseSqmToSqlAstConverter.java:2271)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectClause(BaseSqmToSqlAstConverter.java:2213)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:2057)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:440)
at org.hibernate.query.sqhttp://m.tree.select.SqmQuerySpec.accept(SqmQuerySpec.java:125)
at org.hibernate.query.sqm.spi.BaseSemanticQueryWalker.visitQueryPart(BaseSemanticQueryWalker.java:218)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQueryPart(BaseSqmToSqlAstConverter.java:1915)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:1600)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:440)
at org.hibernate.query.sqhttp://m.tree.select.SqmSelectStatement.accept(SqmSelectStatement.java:228)
at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.translate(BaseSqmToSqlAstConverter.java:776)
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.buildCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:399)
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:324)
at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:300)
at org.hibernate.query.sqhttp://m.internal.QuerySqmImpl.doList(QuerySqmImpl.java:509)
at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:427)
at org.hibernate.query.Query.getResultList(Query.java:120)
at cohttp://m.querydsl.jpa.impl.AbstractJPAQuery.getResultList(AbstractJPAQuery.java:177)
at cohttp://m.querydsl.jpa.impl.AbstractJPAQuery.fetch(AbstractJPAQuery.java:243)

 

해결방법

해결방법부터 말하자면 

when`(Expressions.TRUE)
.then(Expressions.asNumber(1L).castToNum(Long::class.java))
.otherwise(0L).sum(),

 

then 구문에 위와 같이 Expressions.asNumber(1L).castToNum(Long::class.java) 의 방식으로 타입을 선언해야한다.

Expressions.asNumber(1L)도 NumberExpression을 반환하지만 타입정보를 가지고 있지는 않다.

castToNum(Long::class.java)을 통해 타입정보를 갖는 NumberExpression 상수를 반환할 수 있다.

 

구조파악

디버깅을 통해 왜 NPE가 나타났고 어떻게 돌아가는지 알아보자.(순서는 에러가 발생한 부분부터 호출부로 흐른다.)

 

위에서 보듯 ReturnableType타입의 argType객체가 null이라 getJavaType() 메서드를 호출할 때 NPE 에러가 나타났다.

 

좀더 파고 들어가 보니 위와 같이 argument의 expressibleType이 null로 나왔다.

 

SelfRenderingSqmAggregateFunction 객체의 참조속성 argument는

 

List<? extends SqmTypedNode<?>> 참조속성을 갖는다.

 

SqmTypedNode의 getExpressible 메서드를 통해 SqmExpressible을 전달 받는데 이 값이 null이 가능하다.

 

따라서 SqmTypedNode에 Expressible 타입 정보가 없어 NPE에러가 발생한 것으로 보인다.

 

좀전에 말한 

when`(Expressions.TRUE)
.then(Expressions.asNumber(1L).castToNum(Long::class.java))
.otherwise(0L).sum(),

 

위의 해결방안으로 코드를 실행하니

 

위와 같이 ExpressibleType에 java.lang.Long타입 정보가 전달이 되었고

 

쿼리가 정상적으로 실행되었다.

 

위 에러는 case when과 sum을 함께 사용하는 모든 환경에서 나타나는 것은 아니다.

 

위에서 설명한 구조로 동작되는 방식이 모든 버전에 같은 것은 아닌 것으로 보인다.

 

만약 나와 같은 에러가 나타났고 디버깅을 통해 비슷한 현상이 나타난다면

 

위와 같이 인자값에 타입정보를 줄 수 있는 방식을 고려해보는 것이 좋을 것이다.

 

반응형