[error] @Builder 애너테이션 사용시 NullPointerException 에러

2023. 11. 7. 16:45
728x90

 

Input은 이렇게 책 검색 정보 API 갖고 오고, 

Board의 정보도 같이 입력해서 폼 제출한 후, 각각의 엔터티에 저장시켜주려고 했다. 

 

엔터티는 이런 형식임

이때 외래키가 공유되므로 이미지 파일 올리면서 책 정보도 올리고 나눔글 정보까지 같이 올라가야만 한다. 

근데 각각의 외래키에 정보를 하나씩 넣기보다 한 테이블에서 입력을 하고 저장하면 다른 테이블에는 자동으로 올라갈 수 있도록

연관관계 메서드를 만들어서 넣는 것이 좋다!!

 

그래서 만든 연관관계 메서드는

Books 엔터티에 Image 추가 메서드,

// 연관관계 메서드
    // 이미지를 올리면 Books의 이미지리스트에 추가가 되면서 또 Images 엔티티에 추가가 된다.
    public void addImage(Images image) {
        imagesList.add(image);
        image.setBooks(this);
    }

 

Boards 엔터티에 Book 추가 메서드

    // 연관관계 메서드
    // 책 한 권을 올리면 Boards에 Book 추가되면서 Books엔티티에도 추가
    public void addBook(Books book) {
        book.setBoards(this);
        this.getBooksList().add(book);
    }

 

이렇게 만들어두고 service단에서 이 메서드를 불러들였다! 

 @Transactional
    public void uploadIntoDB(List<String> imgUrlList, InsertBookDTO bookDTO, InsertBoardDTO boardDTO) {
		
        // board로 메시지값 받아와서 build
        Boards boards = Boards.builder()
                .message(boardDTO.getMessage())
        		.build();
        
        // 그것을 DB에 저장
        Boards board = boardRepository.save(boards);
        
        // bookDTO에서 값을 받아와서 buiild
        Books books = Books.builder()
                        .title(bookDTO.getTitle())
                        .isbn(bookDTO.getIsbn())
                        .description(bookDTO.getDescription())
                        .author(bookDTO.getAuthor())
                        .publisher(bookDTO.getPublisher())
                        .pubdate(bookDTO.getPubdate())
                        .build();
		
       // 그 book 값을 board와 book에 함께 저장.
        boards.addBook(books);

	// 이미지 값을 urlList에서 각각 url로 저장 후 book에도 add
        for(String imgUrl : imgUrlList) {
            Images image = new Images();
            image.setImage_url(imgUrl);
            books.addImage(image);
        }
        // book값을 DB에 저장
        bookRepository.save(books);

    }

 

요렇게 해서 실행을 했는데..............

자꾸 null이라는 오류가 난다 ㅠㅡㅠ 

널친구는 항상 모때문인지 모르겠어서 어렵다 

NullPointerException

 

 

addBook에서 에러가 났다길래 그 엔터티가서 막 살펴봤지만 연관관계 잘 맺어있고,,,,,,,, 

뭐가 문젠지요....? 

또 폭풍 서치 그리고 짝꿍 찬스...

짝꿍도 해결 못하고 

혼자 또 찾아보다가

알아낸 것..............!!!!!!!!!!!!!!!!!

 

@Builder가 클래스에 사용될 때 npe가 난다는 것

이유는 build() 과정에서 포함되지 않은 필드들은 모두 null 또는 0이 된다고 한다. 

그래서 그럴 때에는 대신 default를 필드에 설정해주어야 한다는 것. (@Builder.Default로!!!) 

 

공식 문서 내용
@Builder.Default

If a certain field/parameter is never set during a build session, then it always gets 0 / null / false. If you've put @Builder on a class (and not a method or constructor) you can instead specify the default directly on the field, and annotate the field with @Builder.Default:
@Builder.Default private final long created = System.currentTimeMillis();

 

 

그래서 

Boards 엔터티에 Book 맵핑해준 것을 수정.

    @Builder.Default
    @OneToMany(
            mappedBy = "boards",
            cascade = CascadeType.ALL,
            orphanRemoval = true)
    private List<Books> booksList = new ArrayList<>();

 

그리고 Books 엔터티에 Image 매핑해준 것을 수정.

@Builder.Default
@OneToMany(
            mappedBy = "books", // mappedBy는 주인이 아님을 명시
            cascade = CascadeType.ALL)
            /* cascade옵션은 부모 엔티티에 대한 동작을 자식 엔티티로 전파할 때 사용됨.
             모든 영속성 동작(PERSIST, MERGE, REMOVE, REFRESH, DETACH)을 자식 엔티티로 전파하겠다는 의미.
             즉 부모엔티티에 대한 변경사항이 자식 엔티티에도 적용된다. */
    private List<Images> imagesList = new ArrayList<>();

 

이렇게 해주니까 

같은 builder를 써도 결국은 

 

요렇게 DB에 잘 들어갔다!!!!!!!!!!

예에ㅔㅔㅔㅔㅔㅔㅔ!!!!!

너무 힘겨웠다

1일 1오류 마주하기...ㅎㅎ

많이 배우는 중

 

 

 

 


++ 추가 

꼭 @Builder.Default를 사용하여야 하는가? 에 대한 의문

 

1. 필드를 즉시 초기화하지 않는 경우 : Lombok의 @Builder는 필드의 기본 초기화를 무시하기 때문에 필드의 기본값이 필요한 경우 반드시 @Builder.Default를 사용해야 한다.

공식 문서 내용
If you use @Builder and you want a default value for a field, you must use @Builder.Default. Otherwise, even if you initialize the field at the declaration, the generated builder will not apply that default value unless you explicitly set it during the build process. Instead, fields that are not set in the builder will remain null (or whatever their default value would be without initialization).

 

-> @Builder는 필드의 기본 초기화 값을 무시하고, 빌더를 통해 값을 설정하지 않은 필드는 기본적으로 null로 처리된다.

 

@Builder
public class Example {
    @Builder.Default
    private List<String> list = new ArrayList<>();  // @Builder.Default로 기본값 설정
}

 

2. 필드를 미리 초기화하는 경우 :  빌더 패턴이 아닌 방식으로 객체를 생성하거나, @Builder 사용하지 않을 때는 @Builder.Default가 굳이 필요하지 않다. 객체를 생성하는 시점에 필드가 이미 초기화되기 때문.

public class Example {
    private List<String> list = new ArrayList<>();  // 필드를 미리 초기화
}
728x90

BELATED ARTICLES

more