FetchMode Rejoindre vs sous-sélection
J'ai deux tables Employé et le Département sont les suivantes classes d'entité pour les deux
Department.java
@Entity
@Table(name = "DEPARTMENT")
public class Department {
@Id
@Column(name = "DEPARTMENT_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer departmentId;
@Column(name = "DEPARTMENT_NAME")
private String departmentName;
@Column(name = "LOCATION")
private String location;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "department", orphanRemoval = true)
@Fetch(FetchMode.SUBSELECT)
//@Fetch(FetchMode.JOIN)
private List<Employee> employees = new ArrayList<>();
}
Employee.java
@Entity
@Table(name = "EMPLOYEE")
public class Employee {
@Id
@SequenceGenerator(name = "emp_seq", sequenceName = "seq_employee")
@GeneratedValue(generator = "emp_seq")
@Column(name = "EMPLOYEE_ID")
private Integer employeeId;
@Column(name = "EMPLOYEE_NAME")
private String employeeName;
@ManyToOne
@JoinColumn(name = "DEPARTMENT_ID")
private Department department;
}
Ci-dessous sont les requêtes tiré quand j'ai fait em.find(Department.class, 1);
-- fetch mode = fetchmode.rejoignez
SELECT department0_.DEPARTMENT_ID AS DEPARTMENT_ID1_0_0_,
department0_.DEPARTMENT_NAME AS DEPARTMENT_NAME2_0_0_,
department0_.LOCATION AS LOCATION3_0_0_,
employees1_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_1_,
employees1_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_1_,
employees1_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_2_,
employees1_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_2_,
employees1_.EMPLOYEE_NAME AS EMPLOYEE_NAME2_1_2_
FROM DEPARTMENT department0_
LEFT OUTER JOIN EMPLOYEE employees1_
ON department0_.DEPARTMENT_ID =employees1_.DEPARTMENT_ID
WHERE department0_.DEPARTMENT_ID=?
-- fetch mode = fetchmode.sous-sélection
SELECT department0_.DEPARTMENT_ID AS DEPARTMENT_ID1_0_0_,
department0_.DEPARTMENT_NAME AS DEPARTMENT_NAME2_0_0_,
department0_.LOCATION AS LOCATION3_0_0_
FROM DEPARTMENT department0_
WHERE department0_.DEPARTMENT_ID=?
SELECT employees0_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_0_,
employees0_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_0_,
employees0_.EMPLOYEE_ID AS EMPLOYEE_ID1_1_1_,
employees0_.DEPARTMENT_ID AS DEPARTMENT_ID3_1_1_,
employees0_.EMPLOYEE_NAME AS EMPLOYEE_NAME2_1_1_
FROM EMPLOYEE employees0_
WHERE employees0_.DEPARTMENT_ID=?
Je voulais juste savoir ce que nous devrions préférer FetchMode.JOIN
ou FetchMode.SUBSELECT
? ce que nous devrions opter dans le scénario?
OriginalL'auteur eatSleepCode | 2015-10-07
Vous devez vous connecter pour publier un commentaire.
La sous-REQUÊTE de la stratégie que la Marmite se réfère est liée à FetchMode.SÉLECTIONNEZ, pas de sous-sélection.
La sortie de la console que vous avez posté fetchmode.sous-sélection est curieux parce que ce n'est pas le chemin qui est censé fonctionner.
La FetchMode.Sous-sélection
Hibernate docs:
FetchMode.Sous-sélection devrait ressembler à quelque chose comme ceci:
Vous pouvez voir que cette deuxième requête apportera à la mémoire tous les employés qui appartient à un département (c'est à dire de l'employé.department_id n'est pas nulle), il n'a pas d'importance si ce n'est pas le service que vous récupérez dans votre première requête.
C'est donc potentiellement un problème majeur si la table des employés est élevé, car il peut être accidentellement le chargement d'un ensemble de base de données en mémoire.
Cependant, FetchMode.Sous-sélection réduit significatly le nombre de requêtes car ne prend que deux requêtes en comparaison à la (N+1 requêtes de la FecthMode.SÉLECTIONNEZ.
Vous pensez peut-être que FetchMode.REJOIGNEZ encore moins de requêtes, à seulement 1, alors pourquoi utiliser-CI? Eh bien, c'est vrai, mais au coût de duplication des données et une amplification de la réponse.
Si un seul proxy être récupérées avec une JOINTURE, la requête peut récupérer:
Les données de l'employé de la le patron est dupliqué si il dirige plus d'un ministère et il a un coût en bande passante.
Si la paresse de la collection doit être récupérée avec une JOINTURE, la requête peut récupérer:
Le département les données sont dupliquées si elle contient plus d'un employé (le naturel de cas).
Nous n'avons pas seulement souffrent d'un coût en bande passante, mais aussi nous obtenir un double dupliqué Département des objets et nous devons les utiliser ENSEMBLE ou DISTINCT_ROOT_ENTITY de double.
Cependant, dupliquer des données d'encaissement d'un faible temps de latence est un bon compromis dans de nombreux cas, comme Markus Winand dit.
Donc, le problème principal à l'aide de sous-sélection est qui est difficile à contrôler et peut-être le chargement d'un ensemble graphique des entités en mémoire.
Avec le chargement par Lot vous chercher de l'entité associée à une requête distincte en tant que sous-sélection (si vous ne souffrez pas de doublons), progressivement et le plus important de vous interroger uniquement les entités liées (si vous ne souffrez pas de charger un énorme graphique) parce que la sous-requête est filtré par l'Id récupéré par le outter requête).
(Il peut être intéressant de tester si le chargement par Lot avec une très grande taille de lot agirait comme une sous-sélection, mais sans que la question de la charge de l'ensemble de la table)
Un couple de postes montrant les différentes stratégies de chargement et les journaux SQL (très important):
Résumé:
Les tables ont été construites à l'aide de ascii-tables.
Rétrospectivement, je vois que le point que je faisais était un peu pédant. Sélectionner l'extraction a un gros problème avec l'aide de maxResults qui rend les deux fondamentalement incompatibles. Et le cas où il se produirait est totalement inattendu et probablement progrès inaperçu dans la production.
OriginalL'auteur gabrielgiussi
Je dirais que ça dépend...
Nous supposons que vous avez N employés dans un service, qui contient D octets d'informations et un moyen employé se composent de E octets. (Octets somme de l'attribut de longueur avec une surcharge).
À l'aide de la rejoindre stratégie d'effectuer 1 de la requête et des transferts N * (D + E) des données.
À l'aide de la sous-requête stratégie d'effectuer 1 + N requêtes, mais les transferts que les D + N*E de données.
Généralement le N+1 de la requête est le PAS ALLER si N est grand, si la JOINTURE est préféré.
Mais en réalité, vous devez vérifier votre kilométrage entre le nombre de requêtes et le transfert de données.
Remarque que je ne suis pas d'envisager d'autres aspects que la mise en veille de la mise en cache.
Supplémentaires subtil aspect pourrait être valable si l'employé table est grande et partitionné de la partition de l'élagage sur l'indice d'accès à l'examen.
OriginalL'auteur Marmite Bomber
Un client (services financiers) de la mine a eu un problème similaire, et qu'il voulait "acquérir les données en une seule requête". Eh bien, j'ai expliqué que c'est mieux d'avoir plus d'une requête, pour les raisons suivantes:
Pour FetchMode.REJOINDRE le département, seraient transférés à partir de la base de données de l'application une fois par employé, parce que l'opération de jointure résultats en multipliant le département par employé. Si vous avez 10 départements avec 100 personnes chacun, chacun de ces 10 départements seraient transférés 100 fois dans une requête simple SQL. Ainsi, chaque département, dans ce cas, est transféré 99 fois plus souvent que nécessaire, en provoquant un transfert de données charge pour le ministère.
Pour Fetchmode SÉLECTIONNER deux requêtes sont tirés de la base de données. On pourrait être utilisée pour obtenir les données de l'1000 employés, l'un pour obtenir les 10 départements. Voilà, pour moi, des sons beaucoup plus efficace. Pour sûr que vous assurez-vous que les indices sont en place afin que les données puissent être récupérées immédiatement.
Je préfère FetchMode.Sous-sélection.
Ce serait une autre affaire si chaque département a un seul employé, mais, comme le nom de "ministère" le suggère, ce serait très peu probable d'être le cas.
Je suggère de mesure du temps d'accès à l'appui de cette théorie. Pour mon client, j'ai fait les mesures pour les différents types d'accès, et le "ministère" de la table pour mon client avait beaucoup plus de champs (je n'ai pas le concevoir, si). Donc, il fut bientôt évident que la FetchMode.Sous-sélection a été beaucoup plus rapide.
OriginalL'auteur michaeak
Planky dit
C'est vrai, mais seulement quand il y a plus de Ministère de l'entité hidrated (ce qui signifie plus que l'un des employés de la collection non initialisée), je l'ai testé avec 3.6.10.Final et 4.3.8.Final
Dans les scénarios 2.2 (FetchMode.Sous-sélection hidrating 2 3 Départements) et 3.2 (FetchMode.Sous-sélection hidrating tous les Départements), SubselectFetch.toSubselectString renvoie les éléments suivants (les liens vers Hibernate classes sont prises à partir de la 4.3.8.Étiquette finale):
La sous-requête est après avoir utilisé pour construire la clause where par OneToManyJoinWalker.initStatementString se terminant avec
Alors la clause where est ajouté dans CollectionJoinWalker.whereString se terminant avec
De pentecôte de cette requête, dans les deux cas, tous les Employés sont en cours d'extraction et de s'hydrater.
C'est clairement un problème dans le scénario 2.2 parce que nous sommes hydratant seuls Départements 1 et 2, mais aussi hydratant tous les Employés, même s'ils n'appartiennent pas à ceux des Ministères (dans ce cas, les Employés du Ministère 3).
Si il y a un seul Ministère de l'entité hydraté dans la session avec ses employés collection non initialisé, alors la requête est comme une eatSleepCode écrit. Vérifier scénario 1.2
De FetchStyle
Jusqu'à maintenant, je n'arrivais pas à résoudre ce que cela Javadoc signifie:Mise à JOUR
Planky dit:
C'est vrai et c'est un détail très important que j'ai testé dans la nouvelle scénario 4.2
La requête générée pour aller chercher des employés est
La sous-requête dans la clause where contient l'original restriction this_.department_name>=?, en évitant la charge de tous les Employés.
C'est ce que la javadoc moyens
Tout ce que j'ai dit à propos de FetchMode.REJOINDRE et les différences avec FetchMode.Sous-sélection reste vrai (et s'applique également pour FetchMode.SÉLECTIONNEZ).
Mon point était vraiment que ce n'est pas le genre de problème qui se charge de l'ensemble de la base de données (par le chargement de toutes les associations—un problème qui peut se produire avec mise en veille prolongée lorsqu'il est configuré mal). Au lieu de cela, il va juste pour charger la table au pire, et même alors, seulement si votre requête initiale n'ai pas de clause where. Donc, je dirais que l'utilisation de sous-sélection des requêtes de façon inattendue charge l'ensemble de la table si vous êtes en Limitant les résultats et vous n'avez pas de n'importe OÙ sur les critères.
OriginalL'auteur gabrielgiussi