Spring JPA Json循环依赖的问题分析

Posted bladestone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;

以上是关于Spring JPA Json循环依赖的问题分析的主要内容,如果未能解决你的问题,请参考以下文章

Spring依赖注入和循环依赖问题分析

Spring依赖注入和循环依赖问题分析

Spring Boot JPA:在自联接关系中的 JSON 视图上递归

Spring循环依赖原理分析

使用 Spring Boot 的 RESTful API 中的循环依赖

spring源码分析IOC容器初始化