How to solve circular reference in json serializer caused by hibernate bidirectional mapping?

Viewed 97477

I am writing a serializer to serialize POJO to JSON but stuck in circular reference problem. In hibernate bidirectional one-to-many relation, parent references child and child references back to parent and here my serializer dies. (see example code below)
How to break this cycle? Can we get owner tree of an object to see whether object itself exists somewhere in its own owner hierarchy? Any other way to find if the reference is going to be circular? or any other idea to resolve this problem?

14 Answers

if you are using spring boot,Jackson throws error while creating response from circular/bidirectional data, so use

@JsonIgnoreProperties

to ignore circularity

At Parent:
@OneToMany(mappedBy="dbApp")
@JsonIgnoreProperties("dbApp")
private Set<DBQuery> queries;

At child:
@ManyToOne
@JoinColumn(name = "db_app_id")
@JsonIgnoreProperties("queries")
private DBApp dbApp;

Jackson provides JsonIdentityInfo annotation to prevent circular references. You can check the tutorial here.

If you use GSON to convert Java class in JSON you can avoid the fields that cause the circular reference and the infinitive loop, you only have to put the annotation @Expose in the fields that you want to appear in the JSON, and the fields without the annotation @Expose do not appear in the JSON.

The circular reference appears for example if we try to serialize the class User with the field routes of class Route, and the class Route have the field user of the class User, then GSON try to serialize the class User and when try to serialize routes, serialize the class Route and in the class Route try to serialize the field user, and again try to serialize the class User, there is a circular reference that provoke the infinitive loop. I show the class User and Route that mentioned.

import com.google.gson.annotations.Expose;

Class User

@Entity
@Table(name = "user")
public class User {
    
@Column(name = "name", nullable = false)
@Expose
private String name;

@OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
@OnDelete(action = OnDeleteAction.CASCADE)
private Set<Route> routes;

@ManyToMany(fetch = FetchType.EAGER)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinTable(name = "like_", joinColumns = @JoinColumn(name = "id_user"),
        inverseJoinColumns = @JoinColumn(name = "id_route"),
        foreignKey = @ForeignKey(name = ""),
        inverseForeignKey = @ForeignKey(name = ""))
private Set<Route> likes;

Class Route

  @Entity
  @Table(name = "route")
  public class Route {
      
  @ManyToOne()
  @JoinColumn(nullable = false, name = "id_user", foreignKey = 
  @ForeignKey(name = "c"))    
  private User user;

To avoid the infinitive loop, we use the annotation @Expose that offer GSON.

I show in format JSON the result of serialize with GSON the class User.

{
    "name": "ignacio"  
}

We can see that the field route and likes do not exist in the format JSON, only the field name. Because of this, the circular reference is avoid.

If we want to use that, we have to create an object GSON on a specific way.

Gson converterJavaToJson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create();

In the end, we transform the java class of the model of hibernate user using the conversor GSON created.

 User user = createUserWithHibernate();
 String json = converterJavaToJson.toJson(user);

For example, ProductBean has got serialBean. The mapping would be bi-directional relationship. If we now try to use gson.toJson(), it will end up with circular reference. In order to avoid that problem, you can follow these steps:

  1. Retrieve the results from datasource.
  2. Iterate the list and make sure the serialBean is not null, and then
  3. Set productBean.serialBean.productBean = null;
  4. Then try to use gson.toJson();

That should solve the problem

Related