Problem N+1 zapytań

Problem N+1 zapytań polega na tym, że w sytuacji kiedy pomiędzy dwoma tabelami np. "Owner" i "Car" mamy relacje jeden do wielu próba zaczytania danych z tabeli "Car", skończy się na tym, że najpierw zostanie wykonane jedno zapytanie dla tabeli "Owner" a następnie przy każdej próbie "dostania" się do obiektów "Car" kolejne zapytania. W tej sytuacji zamiast jednego zapytania wykona się N + 1 zapytań.  

1 - zapytanie dla całej tabeli Owner :

  • select * from owner


N - liczba zapytań, która odpowiada liczbie wierszy w tabeli Owner, ponieważ każde zapytanie sięga po wiersze z tabeli Car :

  • select * from car where owner_id = 1;  
  • select * from car where owner_id = 2; 
  • ...


zamiast tego w sql można wywołać jedno zapytanie które to zwróci:

select * from car, owner where owner_id = car_id



 Stworzenie klas Entity w relacji jednokierunkowej

👉 Entity Company

package myhibernate.onedirection.entity;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "owner")
public class Owner {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "owner_id")
private long ownerId;
@Column(name = "full_name")
private String fullName;

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name="owner_id")
List<Car> cars;

public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}

public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}

public void AddCar(Car car) {
if (cars == null) {
cars = new ArrayList<Car>();
}

cars.add(car);
}
@Override
public String toString() {
return "Owner [ownerId=" + ownerId + ", fullName=" + fullName + "]";
}
}


❗ Aktualy stan w bazie danych:

1. tabela owner


2. tabela car




 Klasa testująca przeglądanie obiektów "Car" 

Występuje tutaj problem "N + 1". Zaczytywanie danych jest  LAZY


package myhibernate.onedirection;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import myhibernate.onedirection.entity.Car;
import myhibernate.onedirection.entity.Owner;

public class ReadOneToManyApp {
public static void main(String[] args) {

Configuration conf = new Configuration();
conf.configure("hibernate.cfg.xml");
conf.addAnnotatedClass(Owner.class);
conf.addAnnotatedClass(Car.class);

SessionFactory factory = conf.buildSessionFactory();
Session session = factory.getCurrentSession();

session.beginTransaction();

String sql = "select distinct o from Owner o ";
Query<Owner> query = session.createQuery(sql);
for(Owner owner : query.list()) {
System.out.println("Owner istnieje w baze:" + owner);

for(Car car : owner.getCars()) {
System.out.println("Car: " + car);
}
}

session.getTransaction().commit();
factory.close();
}
}

❗ Wynik działania w konsoli: 






 Klasa testująca przeglądanie obiektów Car  - poprawiony problem "N + 1" 


W celu pozbycia się problemu "N+1" zapytań, musimy zastosować w zapytaniu HQL składni join fetch


package myhibernate.onedirection;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;

import myhibernate.onedirection.entity.Car;
import myhibernate.onedirection.entity.Owner;

public class ReadOneToManyApp {

public static void main(String[] args) {
Configuration conf = new Configuration();
conf.configure("hibernate.cfg.xml");
conf.addAnnotatedClass(Owner.class);
conf.addAnnotatedClass(Car.class);
SessionFactory factory = conf.buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
String sql = "select distinct o from Owner o join fetch o.cars ";
Query<Owner> query = session.createQuery(sql);
for(Owner owner : query.list()) {
System.out.println("Owner istnieje w baze:" + owner);
for(Car car : owner.getCars()) {
System.out.println("Car: " + car);
}
}
session.getTransaction().commit();
factory.close();
}

}

❗ Wynik działania w konsoli: 



SELECT DISTINCT owner0_.owner_id  AS owner_id1_1_0_,
                cars1_.car_id     AS car_id1_0_1_,
                owner0_.full_name AS full_nam2_1_0_,
                cars1_.brand      AS brand2_0_1_,
                cars1_.owner_id   AS owner_id3_0_0__,
                cars1_.car_id     AS car_id1_0_0__
FROM   owner owner0_
       INNER JOIN car cars1_
               ON owner0_.owner_id = cars1_.owner_id 




 Stworzenie klas Entity w relacji jednokierunkowej z fetch EAGER

👉 Entity Company

package myhibernate.onedirection.entity;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "owner")
public class Owner {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "owner_id")
private long ownerId;
@Column(name = "full_name")
private String fullName;

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name="owner_id")
List<Car> cars;

public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}

public List<Car> getCars() {
return cars;
}
public void setCars(List<Car> cars) {
this.cars = cars;
}

public void AddCar(Car car) {
if (cars == null) {
cars = new ArrayList<Car>();
}

cars.add(car);
}
@Override
public String toString() {
return "Owner [ownerId=" + ownerId + ", fullName=" + fullName + "]";
}
}



 Klasa testująca przeglądanie obiektów "Car" 

Występuje tutaj problem "N + 1". Zaczytywanie danych jest  EAGER

Różnica jest następująca, wszystko zaczytuje się od razu jeszcze przed pobraniem obiektu Car








Brak komentarzy:

Prześlij komentarz