Search code examples
hibernatejpaspring-data-jpabidirectionalmulti-level

Spring JPA, Hibernate fetch PK or Ids only from other entities


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]}


Solution

  • 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.