Demander à Hibernate de faire de simples mises à jour, au lieu d’énormes sélections puis mises à jour
-
06-07-2019 - |
Question
Permet de configurer la question en premier.
Ce que j’ai, c’est > 4 tables: client, adresse, commande, articles de commande. Chacun est mappé à l’aide d’Hibernate Annotations et est accessible via une couche Spring DAO / Services.
Ce que j'essaie de faire est de fusionner des clients en double. Il ne reste donc plus que toutes les commandes et adresses associées au client B, il faut mettre à jour leur clé étrangère customer_id pour qu'elle pointe vers le client A. Ensuite, le bit désactivé du client B doit être défini.
Au lieu d’envoyer ces requêtes simples à notre base de données, hibernate devient fou et émet une tonne de requêtes select, puis mises à jour. En particulier, il sélectionne tous les articles attachés à une commande (car ceci est défini EAGER
, et ce point n'est pas modifiable.). Pour que les sélections avant la mise à jour cessent de se produire, j’ai essayé d’ajouter l’annotation d’entité hibernate au-dessus du javax.persistence.Entity
classique, comme:
@org.hibernate.annotations.Entity(dynamicUpdate = true, selectBeforeUpdate = false, dynamicInsert = true)
Cela semblait n'avoir aucun effet, sauf pour les requêtes simples dans lesquelles il ne mettait à jour que des éléments uniques dans une table.
Je récupère les objets en utilisant les critères d'hibernation suivants:
Criteria c1 = null;
Criteria c2 = null;
Criteria c = session.createCriteria(Customer.class);
c.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
if(resultLimit>0) c.setMaxResults(resultLimit);
if(timeout>0) c.setTimeout(timeout);
for(String filter: sqlFilters) {
if(filter.indexOf("{alias}.customer_")!=-1) c.add(Restrictions.sqlRestriction(filter));
else if(filter.indexOf("{alias}.address_")!=-1 && addrsAttached) {
if(c1==null)
c1 = c.createCriteria("addresses").setFetchMode("type", FetchMode.JOIN);
c1.add(Restrictions.sqlRestriction(filter));
} else if(filter.indexOf("{alias}.order_")!=-1 && ordersAttached) {
if(c2==null)
c2 = c.createCriteria("orders").setFetchMode("orderItems", FetchMode.SELECT);
c2.add(Restrictions.sqlRestriction(filter));
}
}
return (List<Customer>) c.list();
Ensuite, je déplace tous les objets d'adresse et de commande du client B vers le client A et exécute un
.return (Customer) this.getHibernateTemplate().merge(customer);
sur les deux objets client. Ceci finit par créer une tonne d'instructions select qui obtiennent tous les objets associés (par exemple, OrderItems, Products, ProductType, ProductPricingTmpl, etc.)
Je dois faire enlever ces sélections! S'ils n'étaient pas là, la requête aurait l'air normale et serait efficace! Les requêtes de mise à jour en sortie sont parfaites et dynamiques.
Des idées?
La solution
L’indice peut être dans votre choix d’opération merge (). Du javadoc Hibernate:
Copier l'état de l'objet donné sur l'objet persistant avec le même identifiant. Si il n'y a pas instance persistante actuellement associé à la session, il sera être chargé.
Vous obligez donc Hibernate à charger toutes les données avant de les modifier.
Essayez une autre opération, telle que update ():
Mettez à jour l'instance persistante avec l'identifiant du détaché donné exemple.
Aucune promesse, cependant, Hibernate pourrait décider de le charger de toute façon. Le fichier dynamicUpdates = true peut en réalité aggraver la situation, car il faut savoir avant de commencer pour pouvoir lancer des mises à jour dynamiques.