2010-12-15 82 views
2

J'utilise Hibernate en tant qu'ORM dans mon application Java. J'ai un projet < -> Personne manytomany-relation et le projet est le propriétaire de la cartographie.Mise en veille prolongée: mise à jour d'un objet -> des instructions de suppression non désirées sont également effectuées

Maintenant, j'ai le problème, que je veux mettre à jour le projet. Le projet a un identifiant, un nom, ... et un ensemble de personnes. Faire une mise à jour, les personnes sont tous supprimés dans la table de jointure Project_Person, les instructions SQL sur la console:

Hibernate: update Project set description=?, name=? where id=? 
Hibernate: delete from Project_Person where project_id=? 

Mais je ne veux pas que la déclaration de suppression est exécutée.

J'ai dans mon application Java:

this.projService.updateProject(p); 

où p est le projet, mais d'une manière POJO sans l'Ensemble de personnes. Je pensais que, pour le rendre « Mise en veille prolongée-ready » Je fais cela dans une transaction séparée d'un findProjectById, donc je vais chercher le projet de la base de données:

Project proj = this.projService.findProjectById(p.getId()); 
proj.setName(p.getName()); 
proj.setDescription(p.getDescription()); 

donc j'obtenir l'objet du projet Hibernate prêt, changer la valeurs, puis dites-lui de mettre à jour le projet. Mais l'ensemble est dans la vue de débogueur un PersistentSet et il n'y a aucune personne dedans (je pense, parce qu'ils sont chargés paresseux). Mais la mise à jour avec un

session.update(p); 

dans une nouvelle transaction, je reçois la déclaration de mise à jour et la déclaration de suppression indésirable. Comment puis-je éviter l'instruction delete?

Dois-je créer une instruction SQL spéciale pour que seuls les champs de la table de base de données que je veux mettre à jour soient mis à jour? Ou existe-t-il d'autres/meilleures solutions?

Cordialement.


Mise à jour Cela jette un LazyInitializationException:

public Project findProjectById(int projectId) { 
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); 
    Session sess = sessionFactory.getCurrentSession(); 
    Transaction tx = sess.beginTransaction(); 
    try { 
     Project project = (Project)sess.createQuery("from Project where id = "+projectId).list().get(0); 
     tx.commit(); 
     System.out.println(project.getPersons().size()); 
     return project; 
    } catch (IndexOutOfBoundsException ex) { 
     return null; 
    } 
} 

Capture d'écran du Débogueur:

http://img94.imageshack.us/img94/2020/screendebugger.png

HibernateUtil

import org.hibernate.SessionFactory; 
import org.hibernate.cfg.Configuration; 

public class HibernateUtil { 
    // SessionFactory of Hibernate 
    private static final SessionFactory sessionFactory; 
    static { 
     try { 
      sessionFactory = new Configuration().configure("/hibernate.cfg.xml").buildSessionFactory(); 
     } 
     catch (Throwable ex) { 
      System.err.println("Initial SessionFactory creation failed. " + ex); 
      throw new ExceptionInInitializerError(ex); 
     } 
    } 

    public static SessionFactory getSessionFactory() { 
     return sessionFactory; 
    } 
} 

écrant avant l'tx.commit()

http://img808.imageshack.us/img808/9624/screendebugger2.png

+0

très étrange. essayez d'imprimer la taille avant de valider la transaction. – Bozho

+0

J'ai mis une capture d'écran du débogueur juste avant la ligne tx.commit(). Mise à jour: Mettre ici avant la ligne tx.commit() le getPersons(). Size(), je comprends! – Tim

+0

Ainsi, la session est fermée après validation. Alors, que puis-je faire pour résoudre ce problème avec l'instruction de suppression SQL indésirable? – Tim

Répondre

2

Ce moyen (comme vous le suggérez) que votre collection est vide. Mais ce n'est pas dû au chargement paresseux - le débogueur vous permettrait de charger la collection. Peut-être est-ce vide pour une autre raison. Confirmez ceci en montrant collection.size().

L'autre partie du problème est la cascade. Si vous avez défini la cascade delete-orphan, supprimez-la ou assurez-vous que la collection est effectivement remplie.

+0

Je n'ai pas défini de cascade comme delete-orphelin. Je regarde de plus près dans le Set personnes du projet: il n'y a pas de Personne. Une taille() de la collection retournera toujours une erreur LazyInitialization. – Tim

+0

Je mets un exemple, qui jette une exception LazyInit dans ma question. – Tim

+0

Placez ensuite votre point d'arrêt dans une méthode où la session n'est toujours pas fermée. – Bozho

0

Vous n'avez pas besoin de l'appel "mise à jour".

Le code suivant:

Project proj = this.projService.findProjectById(p.getId()); 
proj.setName(p.getName()); 
proj.setDescription(p.getDescription()); 

n'a pas besoin alors la mise à jour comme la référence proj est persistante et toute modification, il sera sauvé. C'est une caractéristique d'Hibernate et d'autres ORM connus sous le nom de persistance transparente.

voir here pour plus d'informations sur la méthode de mise à jour d'hibernation.

+0

Oui, mais mon modèle clôture la transaction, j'ai donc besoin de l'appeler dans mon cas. Votre lien est correct, mais je ne l'utilise pas dans ce cas. – Tim

0

Si vous mettez à jour l'objet de projet dans une nouvelle transaction, vous devez probablement associer l'objet à la nouvelle session en utilisant

session.merge(project) 

Peut-être qui détecte la non initialisée PersistentSet, le remplace par un nouveau de la nouvelle session et s'assurera que les entrées du Set ne sont pas effacées. Sinon, Hibernate ne sera peut-être pas capable de gérer l'ensemble puisque la transaction qui l'a créé est déjà fermée et suppose qu'elle est vide.