Search code examples
spring-boothibernateone-to-many

Hibernate N+1 issue for Multiple children


I have a entity class which has multiple children with oneToMany association:

public class A{
  private Long id;
  private String name;

  @OneToMany(mappedBy = "A", fetch = FetchType.LAZY, cascade = CascadeType.ALL, 
  orphanRemoval = true)
  private List<B>bList= new ArrayList<>();

 @OneToMany(mappedBy = "A", fetch = FetchType.LAZY, cascade = CascadeType.ALL, 
  orphanRemoval = true)
  private List<C>cList= new ArrayList<>();

 @OneToMany(mappedBy = "A", fetch = FetchType.LAZY, cascade = CascadeType.ALL, 
  orphanRemoval = true)
  private List<D>dList= new ArrayList<>();

  //getters and setters

}

For B,C and D I have set ManyToOne. In a word, they are in a bi-directional relationship.

Now, If I fetch A by id, I see a lot of queries get fired which turns out to be N+1 problem. To solve this, I added @Fetch(FetchMode.SUBSELECT) to all of the oneToMany relationships above which cause less queries to be fired. My question is:

  1. is it okay using @Fetch(FetchMode.SUBSELECT) or I can optimize it further?

  2. what if I want to fetch all "As" by calling findAll() method? What should be the syntax for multiple children? Like

    "select a from A a join fetch a.b then ??"
    List< A > findAll()


Solution

  • Now, If I fetch A by id, I see a lot of queries get fired which turns out to be N+1 problem. To solve this, I added @Fetch(FetchMode.SUBSELECT) to all of the oneToMany relationships above which cause less queries to be fired.

    You are not saying how/when these queries are fired, so a possible reason for the problem is that you are returning entities from your HTTP endpoints which are then serialized. Using @Fetch(FetchMode.SUBSELECT) is one way to "improve" the performance but will only work nicely if the base query which you use to fetch A is simple. If it gets too complex (pagination, complex predicates, etc.) you should stick to the default which is SELECT and instead configure a proper batch size via the @BatchSize( size = 32 ) annotation. A good value for the batch size would be the amount of A instances that you expect to be returned such that only a single query is executed per collection. If you allow a max page size of e.g. 50, you setting the value to 50 would be perfect.

    List< A > findAll()

    Do not ever do this if you care about performance and usability. It rarely makes sense to allow returning all elements, as no user can handle more than ~20 elements at a time anyway. As mentioned before, always have some kind of upper limit on the page size to prevent misuses that can cause performance issues.