N+1 Problem in Hibernate & Spring Data JPA
Das N+1-Problem ist ein Leistungsproblem beim objektrelationalen Mapping, bei dem für eine einzige Select-Abfrage in der Anwendungsschicht mehrere Select-Abfragen (N+1 um genau zu sein, wobei N = Anzahl der Datensätze in der Tabelle) in der Datenbank ausgelöst werden. Hibernate und Spring Data JPA bieten mehrere Möglichkeiten, dieses Leistungsproblem abzufangen und zu lösen.
Was ist das N+1 Problem?
Um das N+1 Problem zu verstehen, betrachten wir ein Szenario. Nehmen wir an, wir haben eine Sammlung von Benutzer Objekte, die auf DB_USER Tabelle in der Datenbank, und jeder Benutzer hat eine Sammlung oder Rolle abgebildet auf DB_ROLE Tabelle über eine Verbindungstabelle DB_USER_ROLE . Auf der ORM-Ebene wird eine Benutzer hat viele an viele Beziehung zu Rolle .
Entity Model
@Entity
@Table(name = "DB_USER")
public class User {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
@ManyToMany(fetch = FetchType.LAZY)
private Set<Role> roles;
//Getter and Setters
}
@Entity
@Table(name = "DB_ROLE")
public class Role {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
//Getter and Setters
}
Ein Benutzer kann mehrere Rollen haben. Rollen werden träge geladen. Nehmen wir nun an, wir wollen alle Benutzer aus dieser Tabelle abrufen und die Rollen der einzelnen Benutzer ausdrucken . Eine sehr naive objektrelationale Implementierung könnte - UserRepository con findAllBy Methode
public interface UserRepository extends CrudRepository<User, Long> {
List<User> findAllBy();
}
Die entsprechenden SQL-Abfragen, die von ORM ausgeführt werden, lauten:
Erst bekommen Alle Benutzer (1)
Select * from DB_USER;
Dann erhalten Rollen für jeden Benutzer N-mal ausgeführt (wobei N die Anzahl der Benutzer ist)
Select * from DB_USER_ROLE where userid = <userid>;
Wir brauchen also eine Auswahl für Benutzer y N zusätzliche Selects zum Abrufen von Rollen für jeden Benutzer , wobei N ist die Gesamtzahl der Nutzer . Dies ist ein klassisches N+1-Problem im ORM .
Wie kann man sie identifizieren?
Hibernate bietet eine Tracing-Option, die die SQL-Protokollierung in der Konsole/den Protokollen ermöglicht. Anhand der Protokolle können Sie leicht feststellen, ob Hibernate für einen bestimmten Aufruf N+1 Abfragen ausführt .
Wenn Sie mehrere SQL-Einträge für eine bestimmte Select-Abfrage sehen, ist die Wahrscheinlichkeit groß, dass es sich um ein N+1-Problem handelt.
N+1 Auflösung
Auf SQL-Ebene Was ORM erreichen muss, um N+1 zu vermeiden, ist, dass eine Abfrage auslösen, die die beiden Tabellen verbindet und die kombinierten Ergebnisse in einer einzigen Abfrage erhalten .
Fetch Join SQL, das alles (Benutzer und Rollen) in einer einzigen Abfrage abruft
OR Einfaches SQL
select user0_.id, role2_.id, user0_.name, role2_.name, roles1_.user_id, roles1_.roles_id from db_user user0_ left outer join db_user_roles roles1_ on user0_.id=roles1_.user_id left outer join db_role role2_ on roles1_.roles_id=role2_.id
Hibernate und Spring Data JPA bieten Mechanismen zur Lösung des N+1 ORM-Problems.
1. Spring Data JPA-Ansatz:
Wenn wir Spring Data JPA verwenden, haben wir zwei Möglichkeiten, dies zu erreichen - mit EntityGraph oder mit select-Abfrage mit fetch join.
public interface UserRepository extends CrudRepository<User, Long> {
List<User> findAllBy();
@Query("SELECT p FROM User p LEFT JOIN FETCH p.roles")
List<User> findWithoutNPlusOne();
@EntityGraph(attributePaths = {"roles"})
List<User> findAll();
}
N+1 Abfragen werden auf Datenbankebene mit Left Join Fetch durchgeführt, wir lösen das N+1 Problem mit AttributePaths, Spring Data JPA vermeidet das N+1 Problem
2. Hibernate-Ansatz:
Wenn es sich um einen reinen Hibernate handelt, dann funktionieren die folgenden Lösungen.
Verwendung von HQL :
from User u *join fetch* u.roles roles roles
Verwendung von Kriterien API:
Criteria criteria = session.createCriteria(User.class);
criteria.setFetchMode("roles", FetchMode.EAGER);
Alle diese Ansätze funktionieren ähnlich und sie stellen eine ähnliche Datenbankabfrage mit Left Join Fetch
2 Stimmen
Es gibt einige hilfreiche Beiträge, die sich mit diesem Problem und der möglichen Lösung befassen. Häufige Anwendungsprobleme und deren Behebung: Das Select N + 1 Problem , Die (silberne) Kugel für das N+1 Problem , Langsames Laden - eifriges Laden
0 Stimmen
Für alle, die nach einer Lösung für dieses Problem suchen, habe ich einen Beitrag gefunden, der es beschreibt. stackoverflow.com/questions/32453989/
54 Stimmen
Wenn man die Antworten bedenkt, sollte man dies nicht als 1+N Problem bezeichnen? Da es sich um eine Terminologie zu handeln scheint, frage ich nicht speziell OP.