ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JPA-Error] 빌더 패턴을 사용하여 양방향 연관관계를 맺을때 NullPointerException 예외 발생
    legacy/JPA 2024. 1. 21. 16:32

    문제 발생

    @ManyToOne 그리고 @OneToMany로 양방향 연관관계를 맺으면 NullPointerException이 발생하였다.

    Board(게시물)와 View(조회수, 좋아요) Entity 간의 연관관계를 맺었다.

     

    Board Entity

    @Entity
    @Getter
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Board {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "board_id")
        private Long id;
    
        private String title; // 제목
        private String content; // 내용
        private long viewCnt; // 조회수
        private long likeCnt; // 좋아요
        private LocalDateTime createdDate; // 작성 일자
        private LocalDateTime editDate; // 수정 일자
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "member_id")
        private Member member;
    
    	// 양방향 연관관계
        @OneToMany(fetch = FetchType.LAZY, mappedBy = "board")
        private List<View> views = new ArrayList<>();
    
    	// 연관관계 편의 메소드
        public void relationshipToMember(Member member) {
            this.member = member;
            member.getBoards().add(this);
        }
    }

     

    View Entity

    @Getter
    @Entity
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class View {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "view_id")
        private Long id;
    
        private long viewCnt;
        private long likeCnt;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "member_id")
        Member member;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "board_id")
        Board board;
    
    	// 연관관계 편의 메소드
        public void relationshipToBoard(Board board) {
            this.board = board;
            board.getViews().add(this);
        }
    
    	// 연관관계 편의 메소드
        public void relationshipToMember(Member member) {
            this.member = member;
            member.getViews().add(this);
        }
    }

     

    위 Entity가 정상적으로 사용되는지 테스트를 해보았다. View와 Board를 연관관계를 맺어주는 편의 메서드를 호출하는 순간 NullPointerException이 발생한다. 그 이유로는 "Board.getViews()" 반환값이 null이라고 한다.

     

    그런데 아래 Board Entity를 보면 Builder Pattern을 사용하고 있고 views 필드를 생설할 때 new ArrayList<>()를 통해 초기화도 해주었다. 정황상 views 필드에 null이 저장될 수 없다.

    문제 원인

    views 필드가 null로 저장된 이유는 Builder Pattern을 사용했기 때문이다. 클래스가 인스턴스화될 때 기본적으로는 필드에 명시한 값으로 초기화된다. 그런데 위 코드는 Builder Pattern을 사용하여 객체를 생성하기 때문에 명시적으로 지정한 값으로 초기화되지 않는다.

     

    그렇다면 Builder Pattern을 통해 객체를 생성할 때 값을 지정하지 않으면 어떻게 되는지 확인해보자.

    BuilderClass

    @Builder
    @Data
    public class BuilderClass {
    
        List<String> list = new ArrayList<>();
        Integer num1;
        int num2;
    }

     

    BuilderClassTest

    @SpringBootTest
    public class BuilderClassTest {
        @Test
        void test() {
            BuilderClass build = BuilderClass.builder()
                    .build();
            System.out.println("build = " + build);
        }
    }

     

    지정하지 않은 값들은 다음과 같이 저장됨을 알 수 있다.

    문제 해결

    @Builder.Default 애너테이션을 사용하면 명시적으로 지정한 값으로 초기화된다. 즉, Builder Pattern으로 객체를 생성할 때 값을 지정하지 않은 필드에는 Entity에 명시한 값으로 초기화된다는 의미이다. 

    Builder Pattern으로 객체를 생성할 때 필드 값을 지정하지 않으면 views 필드가 new ArrayList<>()로 초기화된다.

Designed by Tistory.