2

I have three entities as below:
Parent:

@Setter
@Getter
@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String parentName;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, targetEntity = Child.class)
    private List<Child> children;
}

Child:

@Setter
@Getter
@Entity
public class Child {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String childName;

    @ManyToOne(targetEntity = Parent.class, cascade = CascadeType.MERGE)
    @JoinColumn(name = "parent_id")
    private Parent parent;

    @OneToMany(mappedBy = "child", cascade = CascadeType.ALL)
    private List<GrandChild> grandChildren;
}

GrandChild:

@Getter
@Setter
@Entity
public class GrandChild {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(cascade = CascadeType.MERGE, targetEntity = Child.class)
    @JoinColumn(name = "child_id")
    private Child child;
}

When i call parentRepository.findById(id), i would get a parent with its children and children's grandChildren. However, this would cause 2 problems:

  • Infinite recursion com.fasterxml.jackson.databind.JsonMappingException: because all entities contain references to each other.

  • Unnecessary queries from Hibernate since my ParentDto only contains List<Long> childrenIds

Is there anyway to make Hibernate to query only Id or PK from each entity's references rather then fetch the whole object ? Something like:

Parent: {
  "parentName" : "Parent Name",
  "children" : [ {
    "id" : 0
  }, {
    "id" : 1
  }, {
    "id" : 2
  } ]
}

Or: Parent: {"parentName" : "Parent Name", "childrenIds" : [0, 1, 2]}

linhndk94
  • 23
  • 3
  • *i would get a parent with its children and children's grandChildren*: no, you wouldn't. OneToMany associations are lazy. *Infinite recursion*: no: Hibernate can of course deal with that, and since you're serializing a DTO to JSON, why would you serialize the entities to JSON? – JB Nizet Mar 31 '18 at 21:06
  • @JBNizet i created 1 parent with 3 children, and with each child 3 grandChildren. When i retrieve that parent, i've got 3 children with all 9 grandChildren. What am i doing wrong ? [Image Link]: https://imgur.com/JYypVjc [Image Link]: https://imgur.com/vC08yPo – linhndk94 Apr 01 '18 at 02:42
  • @JBNizet About infinite recursion: It's just sometime i log data out to see and it throws exceptions. – linhndk94 Apr 01 '18 at 02:44
  • You want to fetch only the `childIds` and not the `grandChildrenIds`. Is that right? – Madhusudana Reddy Sunnapu Apr 01 '18 at 05:19
  • By serializing the parent to JSON, Jackson navigates through the collection of children, then to the collections of grandchildren, in order to generate the JSON. This forces JPA to initialize the lazy collections, and thus to load everything from the database. If you didn't have that logging statement asking to serialize the parent to JSON, and instead only transformed the parent to a DTO, without navigating though the grand-children (since you don't want them in your DTO), JPA would not initialize th grand-children collections. – JB Nizet Apr 01 '18 at 05:43
  • @JBNizet thank you very much, sir. I really appreciate your help. You've solved my problem :D. Would you like to make an answer so i can vote to accept your answer ? – linhndk94 Apr 01 '18 at 06:27
  • @MadhusudanaReddySunnapu yes, i'd like to fetch only the `childIds` and not the `grandChildrenIds` – linhndk94 Apr 01 '18 at 06:28

2 Answers2

1

Concerning the first question - to avoid infinite recursion just add annotation @JsonIgnoreProperties to every 'child' collection, for example:

@JsonIgnoreProperties("parent")
private List<Child> children;

@JsonIgnoreProperties("child")
private List<GrandChild> grandChildren;
Cepr0
  • 28,144
  • 8
  • 75
  • 101
1

So given a parentId you want to retrieve the parentName and only the associated childIds -

Instead of going via parentRepository, I would prefer adding an new method with @Query in the childRepository. The method and @Query would like below:

@Query("select c.parent.id, c.parent.parentName, c.id from Child c where c.parent.id = ?1")

List<Object[]> findChildIdsByParentId(Long parentId);

Each object [] will contain parentId at index 0, parentName at index 1 and childId at 2.

Once you have this data you can populate this in a DTO (which jackson serializes later) that meets you json format.