gengminy
갱미니의 코딩일지
gengminy
전체 방문자
오늘
어제
  • 분류 전체보기 (61)
    • 🚀 프로젝트 (16)
      • 🎸 고스락 티켓 (4)
      • 🌞 내친소 (5)
      • 🥁 두둥 (7)
    • 📡 백엔드 (31)
      • 🌱 Spring Boot (13)
      • 🐱 Nest.js (10)
      • ⭐ Node.js (8)
    • 🏭 Infra (11)
      • ⚙ 준비를 위한 준비 (2)
      • 🥑 AWS (3)
      • 🐳 Docker (3)
      • ⚡ Github Actions (3)
    • 🌊 프론트엔드 (1)
      • 🌌 React.js (1)
    • 😎 주저리 (2)

블로그 메뉴

  • 💻Github
  • 📸Instagram
  • ✨Blog

공지사항

인기 글

태그

  • springboot
  • nodejs
  • nest
  • nestjs
  • JWT
  • 슬랙알림
  • JSON
  • 도커
  • GithubActions
  • OAuth
  • oauth2
  • AWS
  • SlackAPI
  • docker
  • 스프링
  • github
  • 깃헙액션
  • Spring
  • 스프링부트
  • 네스트

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
gengminy

갱미니의 코딩일지

[Spring] JPA + Lombok 사용할 때 @OneToOne 에서 발생하는 StackOverflowError 해결
🚀 프로젝트/🌞 내친소

[Spring] JPA + Lombok 사용할 때 @OneToOne 에서 발생하는 StackOverflowError 해결

2022. 9. 24. 17:56

JPA 와 Lombok 을 같이 사용중이라면 발생할 수 있는 에러

 

❌ 원인

@OneToOne 또는 @OneToMany 를 통해 연관관계를 정의했고

이후 데이터를 꺼내올 때 hashCode 또는 toString 을 호출하면서

무한 순환 참조에 의해 스택 오버플로우가 발생하는 에러이다

 

java.lang.StackOverflowError: null
	at com.tikitaka.naechinso.domain.member.entity.Member.toString(Member.java:21) ~[main/:na]
	at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na]
	at com.tikitaka.naechinso.domain.member.entity.MemberDetail.toString(MemberDetail.java:18) ~[main/:na]
	at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na]
	at com.tikitaka.naechinso.domain.member.entity.Member.toString(Member.java:21) ~[main/:na]
	at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) ~[na:na]
    ...

 

또는

 

Caused by: java.lang.StackOverflowError: null
	at com.tikitaka.naechinso.domain.member.entity.Member.hashCode(Member.java:22) ~[main/:na]
	at com.tikitaka.naechinso.domain.member.entity.MemberDetail.hashCode(MemberDetail.java:19) ~[main/:na]
	at com.tikitaka.naechinso.domain.member.entity.Member.hashCode(Member.java:22) ~[main/:na]
	at com.tikitaka.naechinso.domain.member.entity.MemberDetail.hashCode(MemberDetail.java:19) ~[main/:na]
	at com.tikitaka.naechinso.domain.member.entity.Member.hashCode(Member.java:22) ~[main/:na]

 

📌 Lombok 어노테이션

 

@EqualsAndHashCode 는 equals 와 hashCode 를 자동 생성해준다

  • eqauls : 두 객체의 내용이 같은지 비교 (동일성)
  • hashCode : 두 객체가 같은 객체인지 존재를 비교 (동등성)

@ToString 은 toString 을 자동 생성하여 멤버 데이터를 문자열로 만들어준다

 

@Data는

@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode

를 한번에 정의해준다

 

 

🔍 엔티티 정의

📝 Member.java

@Entity
@Table(name = "member")
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class Member extends BaseEntity {

    @Id
    @Column(name = "mem_id")
    @GeneratedValue
    private Long id;

    @OneToOne(mappedBy = "member")
    @JoinColumn(name = "mem_detail")
    private MemberDetail detail;
    
}

 

📝 MemberDetail.java

@Entity
@Table(name = "member_detail")
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class MemberDetail extends BaseEntity {

    @Id
    @Column(name = "mem_id")
    private Long id;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name = "mem_id")
    private Member member;
    
}

Member 와 멤버의 디테일을 저장하는 MemberDetail 을

1대1 연관관계를 맺었다

 

MemberRepository.findAll 을 통해 Member 정보를 가져올 때

hashCode 를 무한으로 호출하면서 순환 참조에 빠졌다

 

 

🔑 해결 방안

📝 MemberDetail.java

@Entity
@Table(name = "member_detail")
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = {"member"})
@EqualsAndHashCode(exclude = {"member"})
public class MemberDetail extends BaseEntity {

    @Id
    @Column(name = "mem_id")
    private Long id;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name = "mem_id")
    private Member member;
    
}

자식 엔티티에다가

@ToString 과 @EqualsAndHashCode 의 옵션 중에 exclude를 추가해주면 된다

exclude = { "원하는 필드명" } 을 추가하고

원하는 필드명에 연관관계를 맺은 엔티티를 적어준다

 

이렇게 되면 ToString 과 EqualsAndHashCode 를 생성할 때

해당 필드를 제외시키게 되고 무한 순환 참조에 빠지지 않게 된다

 

해결 완료!!

저작자표시 (새창열림)

'🚀 프로젝트 > 🌞 내친소' 카테고리의 다른 글

[Spring] RefreshToken + Redis 사용해서 자동 로그인 + 로그아웃 구현하기  (0) 2022.11.14
[Spring] 스프링 스케쥴러 사용해서 일정 주기로 메소드 실행하기 (Scheduler)  (2) 2022.11.05
[Spring/Nginx] MultipartFile 최대 용량 설정하기 (MaxUploadSizeExceededException / Request Entity Too Large)  (0) 2022.11.01
[Spring] 문자 인증 구현하기 (Redis + 네이버 클라우드 플랫폼 SMS API)  (0) 2022.09.11
    '🚀 프로젝트/🌞 내친소' 카테고리의 다른 글
    • [Spring] RefreshToken + Redis 사용해서 자동 로그인 + 로그아웃 구현하기
    • [Spring] 스프링 스케쥴러 사용해서 일정 주기로 메소드 실행하기 (Scheduler)
    • [Spring/Nginx] MultipartFile 최대 용량 설정하기 (MaxUploadSizeExceededException / Request Entity Too Large)
    • [Spring] 문자 인증 구현하기 (Redis + 네이버 클라우드 플랫폼 SMS API)
    gengminy
    gengminy
    코딩

    티스토리툴바