"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의 몇가지 특성을 나열해보려고 합니다.
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);
}
위와 같이 메서드로 조건을 따로 분리함으로써 재사용성과 가독성이 훨씬 좋아졌습니다.
우리가 검색을 할 때 검색 조건을 여러개 설정할 수 있습니다.
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