[JPA] Hibernate entity에 Lombok 사용 시 주의사항

Jungwoong (David) Yoon·2023년 8월 14일

JPA

목록 보기
1/5

개요

오늘 개인 프로젝트에 JPA를 적용하고 잘 작동하는지 테스트하던 도중, StackOverflowError 가 생겼다. 자세히 들여다보니 Entity에 붙인 @ToStirng 때문에 생긴 오류인 것을 알아냈다.


원인은?


문제가 발생한 entity를 살펴보자.

@Getter
@ToString
@EqualsAndHashCode
@Entity(name = "Member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String email;

    private String password;

    @OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Image> images = new ArrayList<>();
    
    ...
}

@Getter
@ToString
@EqualsAndHashCode
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Image {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String prompt;

    @Column(name = "image_size")
    private String size;

    private String url;

    @ManyToOne
    private User owner;
    
    ...
}

위의 User Image는 서로 연관 관계를 갖고 있다.


이때, 다음과 같이 삭제 할 Image entity를 찾고

Image imageToDelete = imageRepository.findById(1L).get();

아래와 같이 User 에서 담고 있던 Image 를 삭제하면

owner.getImages().remove(imageToDelete);

이렇게 StackOverflowError 가 발생한다. 왜 이런 일이 발생하는 것일까?

위의 오류 문구를 자세히 읽어보면, 사이 사이에 Image.toString()User.toString() 이 반복적으로 호출되는 것을 알 수 있다.

각 entity에 붙은 @ToString 에 의한 순환참조가 일어나다 보니 결국에는 각 객체가 반복해서 .toString() 을 호출하게 된 것이다.


해결 방법


해결 방법은 매우 간단하다. @ToString 에 의한 순환 참조를 끊어주면 된다.

  1. @ToString(exclude = ...) 사용

    • 기본적으로 Lombok 의 toString() method는 해당 클래스의 모든 필드와 값을 String 값으로 표현한다. 그렇기 때문에 제외할 필드를 지정한다면 해결이 가능하다.
    @ToString(exclude = "bar")
    public class Foo {
    
    	@ManyToOne
    	private Bar bar;
        
        ...
    }
    • 혹은 @ToString.exclude 를 제외하고 싶은 필드에 직접 붙여서 사용 가능하다.
    @ToString
    public class Foo {
    
    	@ManyToOne
        @ToString.exclude
    	private Bar bar;
        
        ...
    }

  1. toString() 재정의

    • @ToString annotation을 사용하지 않고, 직접 toString() 을 override해서 제외할 필드를 빼고 정의 가능
    public class Foo {
    
    	@ManyToOne
    	private Bar bar;
        
        ...
        
        
      	public String toString() {
        	... // bar 제외
        }
    }

마무리


아주 사소하지만 자주 일어날 수 있는 문제라 생각해서 이 글에 정리했다. 작은 기능 하나라도 제대로 알고 사용해야 한다는 것을 다시 한 번 깨닫게 되는 계기가 되었다.



Reference:

profile
깨부하는 개발자

0개의 댓글