쟈미로그

[Spring Boot][JPA] nativeQuery 사용 시 NullPointerException 문제 본문

JPA

[Spring Boot][JPA] nativeQuery 사용 시 NullPointerException 문제

쟈미 2023. 3. 19. 22:31

@Query 어노테이션의 nativeQuery = true 옵션을 사용해서 네이티브 쿼리를 사용하려던 도중 문제가 발생했다.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainController' defined in file [/Users/kakaobank/Desktop/practice/build/classes/java/main/com/blog/practice/controller/MainController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'searchService' defined in file [/Users/kakaobank/Desktop/practice/build/classes/java/main/com/blog/practice/service/SearchService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'keywordRepository' defined in com.blog.practice.repository.KeywordRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract int com.blog.practice.repository.KeywordRepository.saveOrUpdate(java.lang.String); Reason: Cannot invoke "String.contains(java.lang.CharSequence)" because "variable" is null; nested exception is java.lang.NullPointerException: Cannot invoke "String.contains(java.lang.CharSequence)" because "variable" is null
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1372) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1222) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.25.jar:5.3.25]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.25.jar:5.3.25]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.25.jar:5.3.25]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.9.jar:2.7.9]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.9.jar:2.7.9]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.9.jar:2.7.9]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.9.jar:2.7.9]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.9.jar:2.7.9]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.9.jar:2.7.9]
	at com.blog.practice.PracticeApplication.main(PracticeApplication.java:10) ~[main/:na]
...
Caused by: java.lang.NullPointerException: Cannot invoke "String.contains(java.lang.CharSequence)" because "variable" is null
	at org.springframework.data.jpa.repository.query.QueryUtils.createCountQueryFor(QueryUtils.java:607) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.query.DefaultQueryEnhancer.createCountQueryFor(DefaultQueryEnhancer.java:49) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.query.StringQuery.deriveCountQuery(StringQuery.java:119) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery.<init>(AbstractStringBasedJpaQuery.java:72) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.query.NativeJpaQuery.<init>(NativeJpaQuery.java:53) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromMethodWithQueryString(JpaQueryFactory.java:51) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:169) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:253) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:93) ~[spring-data-jpa-2.7.8.jar:2.7.8]
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:103) ~[spring-data-commons-2.7.8.jar:2.7.8]
	... 66 common frames omitted

(에러 메세지 시작과 끝 부분)

 

슥 보니 스프링 부트가 네이티브 쿼리를 처리하는 부분에서 NullPointerException이 나서.. 쿼리가 생성이 안되고 문제가 이어져서 빈 생성도 안되고 뭐 그런 것 같다.. 

 

 

원인 찾기

에러 메세지 끝 부분을 보면 QueryUtils의 variable이라는 변수가 null이라는 것 같으니 디버깅을 해서 찾아가보았다.

static String createCountQueryFor(String originalQuery, @Nullable String countProjection, boolean nativeQuery) {

    Assert.hasText(originalQuery, "OriginalQuery must not be null or empty!");

    Matcher matcher = COUNT_MATCH.matcher(originalQuery);
    String countQuery;

    if (countProjection == null) {

        String variable = matcher.matches() ? matcher.group(VARIABLE_NAME_GROUP_INDEX) : null;
        boolean useVariable = StringUtils.hasText(variable) //
                && !variable.startsWith("new") // select [new com.example.User...
                && !variable.startsWith(" new") // select distinct[ new com.example.User...
                && !variable.startsWith("count(") // select [count(...
                && !variable.contains(",");

        String complexCountValue = matcher.matches() && StringUtils.hasText(matcher.group(COMPLEX_COUNT_FIRST_INDEX))
                ? COMPLEX_COUNT_VALUE
                : COMPLEX_COUNT_LAST_VALUE;

        String replacement = useVariable ? SIMPLE_COUNT_VALUE : complexCountValue;

        if (nativeQuery && (variable.contains(",") || "*".equals(variable))) {
            replacement = "1";
        } else {

            String alias = QueryUtils.detectAlias(originalQuery);
            if (("*".equals(variable) && alias != null)) {
                replacement = alias;
            }
        }

        countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, replacement));
    } else {
        countQuery = matcher.replaceFirst(String.format(COUNT_REPLACEMENT_TEMPLATE, countProjection));
    }

    return countQuery.replaceFirst(ORDER_BY_PART, "");
}

 

?.? variable이 null값이 될 수 있는데 null에 대한 처리가 없었다.. 

그래서

if (nativeQuery && (variable.contains(",") || "*".equals(variable)))

이 부분에서 NPE가 났던 것이다..!

 

 

문제 해결

사실 이 문제가 처음엔 네이티브 쿼리 문법이 잘못돼서 나는건가 했다ㅜ 테이블 필드 중에 count라는 필드가 있는데 이 count가 예약어였다던지.. 등등

그런데 그냥 단순 select 네이티브 쿼리도 안되길래 찾아보니 특정 Spring Boot 버전에서 이 예외가 발생했다는 글들을 몇개 볼 수 있었다. 그래서 일단 스프링부트 버전을 2.7.9 -> 2.7.7로 내리니 문제를 해결할 수 있었다..

디버깅이 서툴러서 문제를 찾는데 시간이 오래 걸렸다ㅜㅜ.. 디버깅 익숙해지기!!!

 

 

'JPA' 카테고리의 다른 글

[JPA] 영속성 관리 - 2  (0) 2023.05.30
[JPA] 영속성 관리 - 1  (0) 2023.05.03
[JPA] @Modifying과 영속성 컨텍스트  (0) 2023.03.02
Comments