Hibernate and JSON - is there a final solution for circular dependencies?

These days I struggle with Hibernate and JSON entities, and although there are many questions about the object, I am not yet able to serialize in the presence of circular dependencies. I tried with both Gsen and Jackson, but I did not have much success. Here is an excerpt from my objects. This is the "parent" class.

@Entity
public class User extends RecognizedServerEntities implements java.io.Serializable
{
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
    @Cascade({CascadeType.SAVE_UPDATE})
    private Set<Thread> threads = new HashSet<Thread>(0);
    //...other attributes, getters and setters
}

and this is the class "children"

@Entity
@Table(name = "thread")
public class Thread extends RecognizedServerEntities implements java.io.Serializable
{
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author", nullable = true)
    private User user;
    //...other attributes, getters and setters
}

I wrote a simple class to test the gson and jackson functions; as said, they both raise an exception.

public class MyJsonsTest
{
    private static User u;
    public static void main(String[] args)
    {
        u = new User("mail", "password", "nickname", new Date());
        u.setId(1); // Added with EDIT 1
    //  testGson();
        testJackson();
    }

    private static void testJackson()
    {
        Thread t = new Thread("Test", u, new Date(), new Date());
        t.setId(1); // Added with EDIT 1
        u.getThreads().add(t);

        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        try
        {
            mapper.writeValue(new File("result.json"), u);
        }
        catch {/[various exceptions catched, but a JsonMappingException was thrown]}
    }

    private static void testGson()
    {
        Gson gson = new Gson();
        System.out.println(u.toString());
        System.out.println(gson.toJson(u, User.class));

        Thread t = new Thread("Test", u, new Date(), new Date());
        u.getThreads().add(t);

        //This raise an exception overflow
        System.out.println(gson.toJson(u, User.class));
    }
}

To solve the problem, on Jackson's side, I tried using this annotation

@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")

User, Thread. . gson GraphAdapterBuilder, . , / .

 private final ConstructorConstructor constructorConstructor = new ConstructorConstructor();

ConstructorConstructor() - undefined;

ConstructorConstructor(Map<Type>, InstanceCreator<?> instanceCreators)

, ? , transient.

1

- . id ( ), . id, .

{
  "id" : 1,
  "email" : "mail",
  "password" : "password",
  "nick" : "nickname",
  "registeredDate" : 1414703168409,
  "threads" : [ {
    "id" : 1,
    "thread" : null,
    "user" : 1,
    "title" : "Test",
    "lastModifiedDate" : 1414703168410,
    "createdDate" : 1414703168410,
    "messages" : [ ],
    "threads" : [ ]
  } ],
  "messages" : [ ]
}
+4
4

, ,

@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=MyEntity.class)` 

, . scope , , "id" . , scope, , ,

com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id java.lang.String) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey@3372bb3f] (through reference chain: ParentEntity["children"]->java.util.ArrayList[0]->ChildEntity["id"])
...stacktrace...
Caused by: java.lang.IllegalStateException: Already had POJO for id (java.lang.String) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey@3372bb3f]
...stacktrace...

Gson

.

+3

JSON , .

, - , -- , -- @JsonIgnore:

@Entity
@Table(name = "thread")
public class Thread extends RecognizedServerEntities implements java.io.Serializable
{
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @org.codehaus.jackson.annotate.JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author", nullable = true)
    private User user;
    //...other attributes, getters and setters
}

, Json.

+2

, org.codehaus.jackson.annotate.JsonManagedReference org.codehaus.jackson.annotate.JsonBackReference ...

, @JsonManagedReference

 @Id
 @TableGenerator(name="CatId", table="catTable",pkColumnName="catKey",pkColumnValue="catValue", allocationSize=1)
 @GeneratedValue(strategy=GenerationType.TABLE, generator="CatId")
 @Column(name = "CategId", unique = true, nullable = false)
 private long categoryId;
 private String productCategory;
 @JsonManagedReference("product-category")
 @OneToMany(targetEntity=ProductDatabase.class,mappedBy="category", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
 private List<ProductDatabase> catProducts;

@JsonBackReference, .

@Id@GeneratedValue
private int productId;
private String description;
private int productPrice;
private String productName;
private String ProductImageURL;
@JsonBackReference("product-category")
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "CategId")
private Category category;

, .

+1
source

Its a good design for serializing Hibernate POJO for the client. Since you can send some data to the location of the client, which he does not allow to view. You must create a client POJO and copy the data from hibernate POJO to the client POJO that you want to send to the client. If you do not want to do this, you can use @JsonIgnore or Fetch all the data with impatience.

0
source

All Articles