Зачем объединять выборки или Entity Graph генерировать n + 1 с отношением многие ко многим? - PullRequest
3 голосов
/ 13 апреля 2020

Я пытаюсь использовать граф сущностей с некоторыми данными, и я сталкиваюсь с n + 1 запросом

Я создал новый проект с весенней загрузкой 2.2.6 с некоторыми таблицами.

Как я понял, использование Fetch или Entity Graph должно генерировать только один запрос

@Setter
@Getter
@Entity
@NoArgsConstructor
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String firstName;

    private String lastName;

    private String address;

    private LocalDate dateOfBirth;

    @ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
    private Level level;

    @ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
    private Department department;


    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Formations> formations = new HashSet<>();

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", address='" + address + '\'' +
                ", dateOfBirth=" + dateOfBirth +
                ", level=" + level +
                ", formations=" + formations +
                '}';
    }

Сущность образований

 @Entity
 @Data
 @NoArgsConstructor
 @ToString(exclude = "students")
 public class Formations {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;    
    private String name;    
    @ManyToMany(mappedBy = "formations")
    @JsonIgnore
    private Set<Student> students = new HashSet<>();
    public Formations(String name) {
        this.name = name;
    }
}

Репозиторий учащихся

 @Repository
 public interface StudentRepository extends JpaRepository<Student, Long>, 
 CustomStudentRepository {
 @Query("SELECT s FROM Student s  " +
        " join fetch s.level l " +
        " join fetch s.department d " +
        " join fetch s.formations f " +
        "  where s.id =:id ")
 Student fetchWithFormations(@Param("id") Long id);}

Реализация моего пользовательского репозитория

public class StudentRepositoryImpl implements CustomStudentRepository {

@PersistenceContext
private EntityManager em;

private static final String QUERY = " SELECT s FROM Student s join fetch s.formations  where s.id =:id ";

public Student findStudentByEntityGraph(Long id) {
    EntityGraph<Student> graph = this.em.createEntityGraph(Student.class);
    graph.addAttributeNodes("department", "level", "formations");

    return em.createQuery(QUERY, Student.class)
            .setParameter("id", id)
            .setHint("javax.persistence.loadgraph", graph)
            .getSingleResult();
}


public Student findStudentByFetch(Long id) {
    return em.createQuery(QUERY, Student.class).setParameter("id", id).getSingleResult();
}

} ​​

findStudentByFetch и findStudentByEntityGraph генерируют 04 запроса

// Generated Queries, I have 1 student with id 15 associated with 03 formations

Hibernate: select student0_.id as id1_3_0_, formations2_.id as id1_1_1_, department3_.id as id1_0_2_, level4_.id as id1_2_3_, student0_.address as address2_3_0_, student0_.date_of_birth as date_of_3_3_0_, student0_.department as departme6_3_0_, student0_.first_name as first_na4_3_0_, student0_.last_name as last_nam5_3_0_, student0_.level as level7_3_0_, formations2_.name as name2_1_1_, formations1_.students as students1_4_0__, formations1_.formations as formatio2_4_0__, department3_.name as name2_0_2_, level4_.name as name2_2_3_ from student student0_ inner join student_formations formations1_ on student0_.id=formations1_.students inner join formations formations2_ on formations1_.formations=formations2_.id left outer join department department3_ on student0_.department=department3_.id left outer join level level4_ on student0_.level=level4_.id where student0_.id=?
Hibernate: select students0_.formations as formatio2_4_0_, students0_.students as students1_4_0_, student1_.id as id1_3_1_, student1_.address as address2_3_1_, student1_.date_of_birth as date_of_3_3_1_, student1_.department as departme6_3_1_, student1_.first_name as first_na4_3_1_, student1_.last_name as last_nam5_3_1_, student1_.level as level7_3_1_ from student_formations students0_ inner join student student1_ on students0_.students=student1_.id where students0_.formations=?
Hibernate: select students0_.formations as formatio2_4_0_, students0_.students as students1_4_0_, student1_.id as id1_3_1_, student1_.address as address2_3_1_, student1_.date_of_birth as date_of_3_3_1_, student1_.department as departme6_3_1_, student1_.first_name as first_na4_3_1_, student1_.last_name as last_nam5_3_1_, student1_.level as level7_3_1_ from student_formations students0_ inner join student student1_ on students0_.students=student1_.id where students0_.formations=?
Hibernate: select students0_.formations as formatio2_4_0_, students0_.students as students1_4_0_, student1_.id as id1_3_1_, student1_.address as address2_3_1_, student1_.date_of_birth as date_of_3_3_1_, student1_.department as departme6_3_1_, student1_.first_name as first_na4_3_1_, student1_.last_name as last_nam5_3_1_, student1_.level as level7_3_1_ from student_formations students0_ inner join student student1_ on students0_.students=student1_.id where students0_.formations=?

Если я удаляю формирования из addAttributeNodes и из Query, он генерирует один запрос, как ожидается

Нужна помощь, пожалуйста

...