Spring JPA Json循環依賴的問題分析

背景

Lombok中的@Data,其等價於:
setter/getter、equals、canEqual、hashCode、toString

在Spring Data JPA中,對於存在雙向依賴的情況,類似如下:

@Entity
@Table(name="t_user")
@Data
public class User {
      private Long id;
      private String name;
      
     @OneToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
	 @JoinColumn(name="user_ext_id", referencedColumnName="id")
      private UserExtEntity userExtEntity;
}

@Entity
@Table(name="t_user")
@Data
public class UserExtEntity {
      private String info;

    @OneToOne(mappedBy = "userExtEntity")
    private UserEntity userEntity;
}

問題描述

測試代碼:

@Test
public void testJsonInfinite() {
      List<UserEntity> userEntities = this.userRepository.findAll();

		log.info("User entities:{}", Arrays.toString(userEntities.toArray())); 
}

異常信息如下:

java.lang.StackOverflowError
	at sun.misc.FloatingDecimal$BinaryToASCIIBuffer.dtoa(FloatingDecimal.java:431)
	at sun.misc.FloatingDecimal$BinaryToASCIIBuffer.access$100(FloatingDecimal.java:259)
	at sun.misc.FloatingDecimal.getBinaryToASCIIConverter(FloatingDecimal.java:1825)
	at sun.misc.FloatingDecimal.toJavaFormatString(FloatingDecimal.java:80)
	at java.lang.Float.toString(Float.java:206)
	at java.lang.Float.toString(Float.java:568)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at org.spb.data.dao.entity.UserExtEntity.toString(UserExtEntity.java:16)
	at sun.reflect.GeneratedMethodAccessor38.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:56)
	at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
	at org.spb.data.dao.entity.UserExtEntity$HibernateProxy$OYDwMbzs.toString(Unknown Source)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at org.spb.data.dao.entity.UserEntity.toString(UserEntity.java:23)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at org.spb.data.dao.entity.UserExtEntity.toString(UserExtEntity.java:16)
	at sun.reflect.GeneratedMethodAccessor38.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:56)
	at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
	at org.spb.data.dao.entity.UserExtEntity$HibernateProxy$OYDwMbzs.toString(Unknown Source)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at org.spb.data.dao.entity.UserEntity.toString(UserEntity.java:23)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at org.spb.data.dao.entity.UserExtEntity.toString(UserExtEntity.java:16)
	at sun.reflect.GeneratedMethodAccessor38.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:56)
	at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
	at org.spb.data.dao.entity.UserExtEntity$HibernateProxy$OYDwMbzs.toString(Unknown Source)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at org.spb.data.dao.entity.UserEntity.toString(UserEntity.java:23)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at org.spb.data.dao.entity.UserExtEntity.toString(UserExtEntity.java:16)
	at sun.reflect.GeneratedMethodAccessor38.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:56)
	at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
	at org.spb.data.dao.entity.UserExtEntity$HibernateProxy$OYDwMbzs.toString(Unknown Source)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at org.spb.data.dao.entity.UserEntity.toString(UserEntity.java:23)

使用瞭解決循環依賴的策略:

  1. @JsonManagedReference/ @JsonBackReference
  2. @JsonIdentityInfo(
    generator = ObjectIdGenerators.PropertyGenerator.class,
    property = “id”)

但是上述的問題依舊。 但是在簡單的Case中是可以正常工作的.

解決辦法

移除其中一個@Data方法,其中默認重寫了@ToString的方法,導致上述的兩個方法不起作用,從異常日誌上看,應該是有Proxy代理相關的內容引發的。

解決的代碼:

@Entity
@Table(name="t_user")
@Data
public class User {
      private Long id;
      private String name;
      
      @JsonManagedReference
     @OneToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
	 @JoinColumn(name="user_ext_id", referencedColumnName="id")
      private UserExtEntity userExtEntity;
}

@Entity
@Table(name="t_user")
@Getter
@Setter
public class UserExtEntity {
      private String info;

   @JsonBackReference
    @OneToOne(mappedBy = "userExtEntity")
    private UserEntity userEntity;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章