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
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 + "]";
}
}
✅ Klasa testująca przeglądanie obiektów "Car"
Występuje tutaj problem "N + 1". Zaczytywanie danych jest LAZY
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();
}
}
✅ Klasa testująca przeglądanie obiektów Car - poprawiony problem "N + 1"
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
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