Beszélj az N+1 problémáról a Hibernate-ben.
sobes.tech MI
Válasz az MI-től
N+1 probléma akkor fordul elő, amikor az eager betöltést kapcsolódó entitásokkal végezzük. Ahelyett, hogy egy lekérdezés történne a fő entités és kapcsolatai lekérésére, a Hibernate egy fő lekérdezést és N további lekérdezést hajt végre minden kapcsolódó entitásra, ahol N a fő entitások száma. Ez jelentős lassulást okozhat az alkalmazás működésében és növeli az adatbázis terhelését.
Példa:
// Fő entitás
@Entity
public class Order {
@OneToMany(fetch = FetchType.EAGER, mappedBy = "order")
private List<OrderItem> orderItems;
// ...
}
// Rendelések listájának lekérése
List<Order> orders = entityManager.createQuery("SELECT o FROM Order o").getResultList();
// Minden rendeléshez külön lekérdezés a OrderItem lekérésére
for (Order order : orders) {
order.getOrderItems().size(); // N további lekérdezést indít az adatbázisban
}
Probléma megoldási lehetőségei:
-
Lazy Loading (késleltetett betöltés): A
FetchType.LAZYhasználata (alapértelmezett kollekciókra ésOneToMany/ManyToMany). A kapcsolódó entitások csak akkor töltődnek be, amikor hozzáférünk hozzájuk.@OneToMany(fetch = FetchType.LAZY, mappedBy = "order") private List<OrderItem> orderItems;Ez a megközelítés problémákat okozhat a "lazy initialization" (
LazyInitializationException) esetén, ha megpróbálunk hozzáférni a lazy betöltött adatokhoz egy aktív Hibernate sessionön kívül. -
Fetch Joins: Egyértelmű utasítás a Hibernate-nek, hogy egy lekérdezéssel töltse be a kapcsolódó entitásokat a
JOIN FETCHhasználatával.List<Order> orders = entityManager.createQuery("SELECT o FROM Order o JOIN FETCH o.orderItems").getResultList();A Fetch Joins elkerülik az N+1 problémát, de duplikált sorokat eredményezhetnek a lekérdezés eredményében, ha a fő entitásnak sok kapcsolódó eleme van.
-
Batch Fetching: A kapcsolódó entitások csoportos betöltése (
BatchSize). A Hibernate több fő entitáshoz tartozó kapcsolódó entitásokat tölt be egy kérésben.@OneToMany(mappedBy = "order") @BatchSize(size = 10) // 10 rendeléshez tartozó OrderItem betöltése egyszerre private List<OrderItem> orderItems; -
Subselect Fetching: A kapcsolódó entitások külön kérésben töltődnek be, azonosítók alapján,
INklauzulákat használva.@OneToMany(mappedBy = "order") @Fetch(FetchMode.SUBSELECT) private List<OrderItem> orderItems;Ez kevésbé lehet hatékony nagy számú fő entitás esetén.
-
Entity Graphs: A JPA 2.1 mechanizmusa, amely lehetővé teszi az entitás gráfjának meghatározását, amelyet be kell tölteni. Egyértelműen ellenőrzi, mely kapcsolatok töltődjenek be.
// Entity Graph meghatározása @NamedEntityGraph(name = "order-with-items", attributeNodes = @NamedAttributeNode("orderItems")) @Entity public class Order { ... } // Entity Graph használata lekérdezéskor Map<String, Object> hints = new HashMap<>(); hints.put("javax.persistence.fetchgraph", entityManager.getEntityGraph("order-with-items")); List<Order> orders = entityManager.createQuery("SELECT o FROM Order o").setHint("javax.persistence.fetchgraph", entityManager.getEntityGraph("order-with-items")).getResultList();