"Internal error - Value must not be null!; nested exception is java.lang.IllegalArgumentException: Value must not be null!"

byeol·2023년 5월 25일
0

"Internal error - Value must not be null!; nested exception is java.lang.IllegalArgumentException: Value must not be null!"

위와 같은 오류가 발생하였습니다.

팀원 개발자분께서 디버깅을 통해 발생한 코드를 알려주셨습니다.

JPAQuery<Post> postQuery = queryFactory
                .selectFrom(post)
                .where( (containsTitle(title))
                        .or( containsContents(contents))
                        .or(containsPosition(positionList))
                        .or(containsTechskill(techSkill))
                        .or(containsMeetingWay(meetingWay))
                        .or(afterSth(post.deadline, now))
                )
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize()+1); 

바로 위 코드에서 발생했습니다.
왜 발생했을까요?

where절에 있는 메서드들 중 하나가 null을 반환하면, where() 메서드의 파라미터로 null이 전달됩니다. 이는 JPAQuery의 where() 메서드에서 null 값이 허용되지 않으므로, "Internal error - Value must not be null!" 예외가 발생할 수 있습니다.

라는 Chat GPT의 답변을 듣게 되었고 제가 Query Dsl에 대한 기초가 부족해서
잘못된 조건절을 사용하고 있었습니다.

몇 가지 살펴볼 것이 있습니다.
BooleanExpression, 그리고 ,입니다.

BooleanExpression

BooleanExpression의 몇가지 특성을 나열해보려고 합니다.

  1. BooleanBuilder에 비해 가독성과 재사용성이 좋습니다.

    BooleanBuilder를 사용할 경우
    검색하고자 하는 조건에 대해 if문을 만들어서 주입시켜야 합니다.

    /* Boolean Builder */
    @Override
    public List<Academy> findBynamicQuery(String name, String address, String phoneNumber){
    	BooleanBuilder builder = new BooleanBuilder();
       
       if(!StringUtils.isEmpty(name)){
       	builder.and(academy.name.eq(name));
       }
       if(!StringUtils.isEmpty(address)){
       	builder.and(academy.address.eq(address));
       }
       if(!StringUtils.isEmpty(phoneNumber)){
       	builder.and(academy.phoneNumber.eq(phoneNumber));
       }
       
       return queryFactory
              .selectFrom(academy)
              .where(builder)
              .fetch();
    }

    하지만 BooleanExpression의 경우에는

    /* BooleanExpression */
    @Override
    public List<Academy> findBynamicQuery(String name, String address, String  phoneNumber){
    	return queryFactory
              .selectFrom(academy)
              .where(eqName(name),
                     eqAddress(address),
                     eqPhoneNumber(phoneNumber))
              .fetch();
    }
    
    private BooleanExpression eqName(String name){
    	if(StringUtils.isEmpty(name)){
       	return null;
       }
       return academy.name.eq(name);
    }
    
    private BooleanExpression eqAddress(String address){
    	if(StringUtils.isEmpty(address)){
       	return null;
       }
       return academy.address.eq(address);
    }

    위와 같이 메서드로 조건을 따로 분리함으로써 재사용성과 가독성이 훨씬 좋아졌습니다.

  2. 우리가 검색을 할 때 검색 조건을 여러개 설정할 수 있습니다.

    BooleanBuilder의 경우 null이 오면 조건문에 포함되지 않도록 if문에 isEmpty()와 같은 조건을 걸어야 합니다.
    그러나 BooleanExpression의 경우 ,를 and조건으로 사용합니다.
    여기에 Querydsl의 where는 null이 파라미터로 올 경우 조건문에서 제외해줍니다.

    BooleanExpression은 null 반환 시 자동으로 조건절에서 제거되기 때문에 where절에 모든 파라미터가 null로 들어오는 경우에는 모든 데이터가 검색된다는 것을 기억해야합니다.
    즉 SELECT * FROM TALBE 의 형태로 where절이 사라집니다.

원인

따라서
제 코드의 경우 or를 사용해서 문제가 되었습니다.

and는 쉼표로 대신 사용될 수 있는 null값이 들어오는 무시하기 때문에
아무런 값을 주지 않으면 모든 DB를 검색해서 보여줍니다.

하지만 or를 사용하는 경우 null 값이 그대로 들어오게 됩니다. (BooleanExpression이 null을 반환한 경우) 즉 null.or 이 되기 때문에 NullPointException이 발생하게 됩니다.

한가지 느낀 점은 intellij에서 디버거를 잘 활용하자 입니다.
관련 링크를 참고해서 공부해보았고 연습해보려고 합니다.
https://www.youtube.com/watch?v=OHrLRg150As

reference

https://jojoldu.tistory.com/394

profile
꾸준하게 Ready, Set, Go!

0개의 댓글