21 Ocak 2021 Perşembe

Hibernate FetchMode.SUBSELECT

Giriş
- Eğer ilişki türü LAZY ise FetchMode.SELECT bir sürü SQL çalışmasına sebep oluyor. Bu durumda FetchMode.SUBSELECT kullanılabilir. Açıklaması şöyle
FetchMode.SUBSELECT is useful as it solves the N+1 query issue of FetchMode.SELECT.
- Eğer ilişki türü - EAGER ise FetchMode.SELECT her bir ilişki için bir select çalıştırılmasına sebep oluyor.

Çalışma Şekli
One tarafı yükleyince Hibernate bu sorguyu hatırlar ve Many tarafı için subselect olarak kullanır. Böylece şu tür kodlar tek bir select ile çalışır
List<Author> authors = authorDataService.findAll();
for (Author author : authors) {
  ...
}

Örnek
Şöyle yaparız. Sadece randevusu olan doktorları yükler.
@Entity
public class Doctor { ... @OneToMany(mappedBy = "doctor", fetch = FetchType.LAZY) private Collection<Appointment> appointments; } @Entity public class Appointment { ... @JoinColumn(nullable = false) @ManyToOne private Doctor doctor; }
Açıklaması şöyle. Hem sadece randevusu olan doktorları yükler hem de az SQL kullanır.
Hibernate remembers the original query retrieving Doctors and uses it in a subselect to initialize all related Appointments. This way all Doctors in Persistence Context have their Appointments initialized with one additional SQL query visible below.

SELECT appointmen0_.doctor_id, 
 appointmen0_.id, 
 appointmen0_.appointmentTime, 
 appointmen0_.doctor_id 
FROM Appointment appointmen0_ 
 WHERE appointmen0_.doctor_id IN (SELECT doctor0_.id FROM Doctor doctor0_)

The total number of SQL selects executed by Hibernate with @Fetch annotation is two. First one fetches Doctors and the second one fetches all the Appointments related to already fetched Doctors.
Örnek
Elimizde şöyle bir kod olsun
@Entity
public class Author { ... @OneToMany(fetch=FetchType.LAZY, mappedBy="author") private List<Book> books; } @Entity public class Book { ... @ManyToOne private Author author; }
Şu SQL üretilir
select ... from author author0_

select ...from book books0_ where books0_.author_id in 
  (select author0_.id from author author0_)
Örnek
Elimizde şöyle bir kod olsun
@Entity
public class Doctor {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @OneToMany(mappedBy = "doctor", fetch = FetchType.LAZY)
  @org.hibernate.annotations.Fetch(value = FetchMode.SUBSELECT)    
  private Collection<Appointment> appointments;
}
@Entity
public class Appointment {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @JoinColumn(nullable = false)
  @ManyToOne
  private Doctor doctor;
}
Şu SQL üretilir
select ... from Appointment appointmen0_ 
where appointmen0_.doctor_id in (select doctor0_.id from Doctor doctor0_)
Örnek
Elimizde şöyle bir kod olsun.
@Entity
public class Dept {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @OneToMany(
        mappedBy = "dept",
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
  @Fetch(FetchMode.SUBSELECT)
  private List<Employee> employees;
}
@Entity
public class Employee {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @ManyToOne(fetch = FetchType.LAZY)
  private Dept dept;
   
}
İlk 10 tane Dept nesnesini şu kodla yükleyelim
final Page<Dept> depts = deptRepository.findAll(pageable);

for (Dept dept : depts) {
    final List<Employee> employees = dept.getEmployees();
    System.out.println(employees);
}
Dept nesneleri yüklenirken şu SQL üretilir
select ... from dept dept0_ limit ?
select ... from employee employees0_ where employees0_.dept_id in
(select dept0_.id from dept dept0_) 
Örnek
Elimizde şöyle bir kod olsun
@Entity
public class Customer {
  ...
  @OneToMany(mappedBy = "customer", cascade = CascadeType.PERSIST)
  @Fetch(FetchMode.SUBSELECT)
  private Set products;
  ...
}

@Entity
public class Product {
  ...
  @ManyToOne
  private Customer customer;
  ...
}
select
 ...
from product products0 
where
products0.customerid in (
  select
  customer0.id 
  from
  customer customer0_
)

Hiç yorum yok:

Yorum Gönder