간단하게 JPA를 공부해보고 바로 적용해보는데 에러가 발생했다.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'animationRepository' defined in hello.repository.AnimationRepository defined in @EnableJpaRepositories declared on JpaConfig: Could not create query for public abstract java.util.List hello.repository.AnimationRepository.findAnimations(); Reason: Validation failed for query for method public abstract java.util.List hello.repository.AnimationRepository.findAnimations()
AnimationRepository 인터페이스의 findAnimations() 메서드에 대한 쿼리 생성이 실패했다는데
나는 아무리봐도 모르겠다 맞게 쓴 거 같은데..
import hello.dto.animation.GetAniListDTO;
import hello.entity.animation.Animation;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface AnimationRepository extends JpaRepository<Animation, Long> {
// 애니 전체 리스트 조회
@Query("SELECT new hello.dto.animation.GetAniListDTO(a.id, a.imagePath, " +
"COALESCE(SUM(r.likeCount), 0), COALESCE(AVG(r.score), 0)) " +
"FROM Animation a LEFT JOIN a.reviews r " +
"WHERE a.imagePath IS NOT NULL AND a.existReview = TRUE " +
"GROUP BY a.id, a.imagePath")
List<GetAniListDTO> findAnimations();
간단하게 모든 정보를 다불러오게해도 에러가 발생.
밑으로 좀 더 내려보니 또 다른 에러있었는데 이게 문제인거 같았다.
Caused by: org.hibernate.query.SemanticException: Missing constructor for type 'GetAniListDTO' [SELECT new hello.dto.animation.GetAniListDTO(a.id, a.imagePath, COALESCE(SUM(r.likeCount), 0), COALESCE(AVG(r.score), 0)) FROM Animation a LEFT JOIN a.reviews r WHERE a.imagePath IS NOT NULL AND a.existReview = TRUE GROUP BY a.id]
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitInstantiation(SemanticQueryBuilder.java:1506)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitInstantiation(SemanticQueryBuilder.java:275)
at org.hibernate.grammars.hql.HqlParser$InstantiationContext.accept(HqlParser.java:4029)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSelectableNode(SemanticQueryBuilder.java:1453)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSelection(SemanticQueryBuilder.java:1407)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSelectClause(SemanticQueryBuilder.java:1400)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitQuery(SemanticQueryBuilder.java:1249)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitQuerySpecExpression(SemanticQueryBuilder.java:1035)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitQuerySpecExpression(SemanticQueryBuilder.java:275)
at org.hibernate.grammars.hql.HqlParser$QuerySpecExpressionContext.accept(HqlParser.java:2132)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSimpleQueryGroup(SemanticQueryBuilder.java:1020)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSimpleQueryGroup(SemanticQueryBuilder.java:275)
at org.hibernate.grammars.hql.HqlParser$SimpleQueryGroupContext.accept(HqlParser.java:2003)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitSelectStatement(SemanticQueryBuilder.java:490)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.visitStatement(SemanticQueryBuilder.java:449)
at org.hibernate.query.hql.internal.SemanticQueryBuilder.buildSemanticModel(SemanticQueryBuilder.java:322)
at org.hibernate.query.hql.internal.StandardHqlTranslator.translate(StandardHqlTranslator.java:71)
at org.hibernate.query.internal.QueryInterpretationCacheStandardImpl.createHqlInterpretation(QueryInterpretationCacheStandardImpl.java:145)
at org.hibernate.query.internal.QueryInterpretationCacheStandardImpl.resolveHqlInterpretation(QueryInterpretationCacheStandardImpl.java:132)
at org.hibernate.internal.AbstractSharedSessionContract.interpretHql(AbstractSharedSessionContract.java:802)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:852)
... 85 common frames omitted
원인은 JPQL 쿼리를 실행할 때 DTO에 정의된 생성자와 쿼리에서 호출된 생성자의 매개변수 타입이 일치하지 않는 것이 문제였다.
package hello.dto.animation;
import lombok.Data;
@Data
public class GetAniListDTO {
private long id;
private String imagePath;
private int likeCount;
private double score;
public GetAniListDTO(long id, String imagePath, int likeCount, double score) {
this.id = id;
this.imagePath = imagePath;
this.likeCount = likeCount;
this.score = score;
}
}
likeCount와 score 필드는 int와 double 타입으로 선언하였는데,
쿼리에서 SUM(r.likeCount)와 AVG(r.score)는 Long과 Double 타입만을 반환할 수 있다.
하지만 DTO 생성자는 int와 double을 받으려해서 타입 불일치로 인해 생성자를 찾지 못하는 에러가 발생한 것이였다.
[ 해결 방법 ]
타입 일치
- DTO의 필드 타입을 Long과 Double로 변경하여 쿼리에서 반환되는 타입과 DTO 생성자의 파라미터 타입을 맞춰주었다.
package hello.dto.animation;
import lombok.Data;
@Data
public class GetAniListDTO {
private Long id;
private String imagePath;
private Long likeCount;
private Double averageScore;
public GetAniListDTO(Long id, String imagePath, Long likeCount, Double averageScore) {
this.id = id;
this.imagePath = imagePath;
this.likeCount = likeCount;
this.averageScore = averageScore;
}
}
해결!