JPA Troubleshooting - JPA 양방향 순환 참조

choko's avatar
Jun 29, 2024
JPA Troubleshooting - JPA 양방향 순환 참조
 
Summary
  • Entity의 OneToMany, ManyToOne 관계에서 출력 할 경우 무한 양방향 순환 참조가 일어난다.
  • Entity를 그대로 출력하지 말고, Entity → Model(dto)로 변환시킨 후 출력하면 해결된다.
  • @JsonIgnore, @JsonIgnoreProperties, @JsonIgnoreType으로도 해결할 수 있다.
 
 

증상

  • User, Authority Entity
@Entity public class USERTest { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column() @NotNull private String username; @Column() @NotNull private String password; @JsonIgnore() @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL) private List<AUTHORITYTEST> authorities = new ArrayList<>(); } public class AUTHORITYTEST { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; //@ColumnDefault("ROLE_USER") @Column private String authority; @Column private String userName; @ManyToOne @JoinColumn(name= "user_id") @JsonIgnore() private USERTest user; }
 
  • Test
@Test void jpaTest2() { USERTest user = new USERTest(); user.setUsername("test0"); List<AUTHORITYTEST> authorityList = authorityTestRepository.findAuthorityByUserName(user.getUsername()); for (AUTHORITYTEST authoritytest : authorityList) { log.info("{}", authoritytest); } }
 
  • 결과
java.lang.StackOverflowError at tom.study.domain.test.AUTHORITYTEST.toString(AUTHORITYTEST.java:12) at java.base/java.lang.String.valueOf(String.java:4220) at java.base/java.lang.StringBuilder.append(StringBuilder.java:173) at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457) at org.hibernate.collection.spi.PersistentBag.toString(PersistentBag.java:590) at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) at tom.study.domain.test.USERTest.toString(USERTest.java:14) at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) at tom.study.domain.test.AUTHORITYTEST.toString(AUTHORITYTEST.java:12) at java.base/java.lang.String.valueOf(String.java:4220) at java.base/java.lang.StringBuilder.append(StringBuilder.java:173) at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457) at org.hibernate.collection.spi.PersistentBag.toString(PersistentBag.java:590) at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) at tom.study.domain.test.USERTest.toString(USERTest.java:14) at java.base/java.lang.StringConcatHelper.stringOf(StringConcatHelper.java:453) at tom.study.domain.test.AUTHORITYTEST.toString(AUTHORITYTEST.java:12) at java.base/java.lang.String.valueOf(String.java:4220) at java.base/java.lang.StringBuilder.append(StringBuilder.java:173) at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457) ...
 
 

원인

  • USER와 AUTHORITY는 서로를 자신의 필드로 가지고 있다. 이 때 Jpa Repo로 entity를 가져와 출력시키면 AUTHORITY→USER→AUTHORITY→USER→ … 형태로 무한루프에 빠지게 된다.
 
 

해결법

  1. Entity를 dto로 변환 후 직렬화 한다.
  1. @JsonIgnore, @JsonIgnoreProperties, @JsonIgnoreType 어노테이션을 사용한다.
  • @JsonIgnore- 어노테이션들은 해당 필드or클래스의 Json 직렬화를 무시한다.
    • @JsonIgnore : 클래스의 속성에 명시
    • @JsonIgnoreProperties : 클래스에 명시, @JsonProperty에 명시된 필드를 무시한다.
    • @JsonIgnoreType : 클래스에 명시, 해당 클래스는 Json 직렬화 불가
    •  
Share article

Tom의 TIL 정리방