IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Hibernate Java Discussion :

Detached Entity après un saveOrUpdate


Sujet :

Hibernate Java

  1. #1
    Membre émérite
    Avatar de Cafeinoman
    Homme Profil pro
    Couteau suisse d'une PME
    Inscrit en
    Octobre 2012
    Messages
    629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Couteau suisse d'une PME

    Informations forums :
    Inscription : Octobre 2012
    Messages : 629
    Par défaut Detached Entity après un saveOrUpdate
    Bonjour,

    je suis confronté à un soucis dans mes tests que je ne comprend pas. Je test ma couche de DAO, et j'ai l'exception suivante de levée (en résumé) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    javax.ejb.EJBException: java.lang.IllegalArgumentException: Removing a detached instance org.javacrea.gestioncontacts.pu.Contact#1
                        at org.javacrea.test.gestioncontacts.TestDAO.createContact(TestDAO.java:91)
    Caused by: java.lang.IllegalArgumentException: Removing a detached instance org.javacrea.gestioncontacts.pu.Contact#1
    Le code du test est le suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    @RunWith(Arquillian.class)
    public class TestDAO {
     
    	@Inject
    	private GestionContacts fichier;
     
    	@Deployment
    	public static Archive<?> createDeployment() {
    		return ShrinkWrap
    				.create(JavaArchive.class, "DAOTest.jar")
    				.addClasses(GestionContacts.class, GestionContactsLocal.class, GestionContactsRemote.class, GestionContactsImpl.class, NullCriteriaValueException.class, MissingCVLineDatesException.class)
    				.addPackage(Contact.class.getPackage())
    				.addAsManifestResource("META-INF/test-beans.xml", "beans.xml")
    				.addAsResource("META-INF/test-persist.xml","META-INF/persistence.xml");
    	}
     
     
    	public void injectFichier(){
    		Assert.assertNotNull(fichier);
    		Assert.assertEquals("OK", fichier.test());
    	}
     
     
    	public void vider(){
    		fichier.closeSession();
    		List<Contact> all = fichier.chargerFichier();
    		for(Contact c : all){
    			fichier.updateContact(c);
    			fichier.deleteContact(c);
    		}
    		fichier.saveSession();
    	}
     
     
    	public void createContact() throws ParseException, MissingCVLineDatesException{
    		Contact moi = fichier.createContact("Monsieur","Delalleau", "François");
    		Assert.assertNotNull(moi);
    		Assert.assertTrue(moi.getId()!=null);
    		int i = moi.getId();
    		moi.addAdressePerso(new Adresse("mon adresse", "92110", "Clichy", null));
    		moi.addEmail(new Email("f.delalleau@gmail.com"));
    		moi.addTelephone(new LigneDirecte("0686000000"));
    		boolean saved = fichier.updateContact(moi);
    		Assert.assertTrue(saved);
    		moi = fichier.trouverContact("Delalleau").get(0);
    		Assert.assertEquals("François", moi.getPrenom());
    		fichier.closeSession();
    		Assert.assertNotNull(fichier.readContact(i));
    		moi = fichier.readContact(i);
    		boolean	persistent = fichier.updateContact(moi);
    		Assert.assertTrue(persistent);
    		fichier.deleteContact(moi);                         // => l'erreur est ici
    		Assert.assertNull(fichier.readContact(i));
    		System.out.println("CRUD contact OK!");
    		fichier.closeSession();
    	}
     
    	@Test
    	public void runTest() throws ParseException, MissingCVLineDatesException{
    		injectFichier();
    		vider();
    		createContact();
    		vider();
    	}
    }
    Et la classe de DAO (je zappe les méthodes non employées dans ce test):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    @Singleton
    @ApplicationScoped
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public class GestionContactsImpl implements GestionContactsLocal,
    		GestionContactsRemote {
     
    	private Integer dirtyCount = 0;
     
    	@PersistenceContext(unitName = "GestionContacts")
    	private Session session;
     
    	@PostConstruct
    	public void checkFlush() {
    		session.setFlushMode(FlushMode.COMMIT);
    	}
     
    	public String test() {
    		return "OK";
    	}
     
    	@SuppressWarnings("unchecked")
    	public List<Contact> chargerFichier() {
    		isTooDirty();
    		return session.createQuery("select c from Contact c").list();
    	}
     
    	@SuppressWarnings("unchecked")
    	public List<Contact> trouverContact(String nom) {
    		isTooDirty();
    		return session
    				.createQuery("select c from Contact c where c.nom like :nom")
    				.setParameter("nom", nom).list();
    	}
     
    	...
     
    	@Override
    	public Contact createContact(String titre, String nom, String prenom) {
    		Contact contact = new Contact();
    		contact.setTitre(titre);
    		contact.setNom(nom);
    		contact.setPrenom(prenom);
    		session.save(contact);
    		isTooDirty();
    		return contact;
    	}
     
    	@Override
    	public Contact readContact(Integer id) {
    		isTooDirty();
    		Contact c = (Contact) session.get(Contact.class, id);
    		if(c!=null){
    		return c;
    		} else {
    			throw new EntityNotFoundException();
    		}
    	}
     
    	@Override
    	public boolean updateContact(Contact contact) {
    		session.saveOrUpdate(contact);
    		isTooDirty();
    		return session.contains(contact);
    	}
     
    	@Override
    	public boolean deleteContact(Contact contact) {
    		session.delete(contact);
    		isTooDirty();
    		return !session.contains(contact);
    	}
     
    	@Override
    	public void saveSession() {
    		session.flush();
    	}
     
    	@Override
    	public void closeSession() {
    		session.flush();
    		session.clear();
    	}
     
    	private void isTooDirty() {
    		if(session.isDirty()){
    			dirtyCount++;
    		}
    		if(dirtyCount>10){
    			saveSession();
    		}
    	}
    J'ai probablement mal compris quelque chose dans l'utilisation de Hibernate, si quelqu'un peut m'éclairer.

  2. #2
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    tu peux nous expliquer comment tu gère tes sessions? Parce que je vois un closeSession, mais pas de réouverture par la suite. Tout ça m'a l'air "bizzare". Normalement après avoir fermé une session hibernate tu devrais en ouvrir une nouvelle. Ici tu ne le fais pas, donc je suppose que hibernate travaille hors session, ce qui pose problème pour le delete.

  3. #3
    Membre émérite
    Avatar de Cafeinoman
    Homme Profil pro
    Couteau suisse d'une PME
    Inscrit en
    Octobre 2012
    Messages
    629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Couteau suisse d'une PME

    Informations forums :
    Inscription : Octobre 2012
    Messages : 629
    Par défaut
    La session est gérée en JTA par le serveur (EAP6). La méthode fichier.closeSession() est en fait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	@Override
    	public void closeSession() {
    		session.flush();
    		session.clear();
    	}
    Elle est appelé à la déconnexion d'un utilisateur, par pure paranoïa. J'aurai du appeler la méthode, je suis d'accord. Mais donc la session hibernate n'est pas fermé. Les opérations sont, dans l'ordre :

    création d'une instance avec new;
    session.save() de l'instance;
    récupération de l'id de l'instance;
    initialisation de colonnes optionnelle de l'instance;
    session.saveOrUpdate() de l'instance;
    remplacement de l'instance avec une query sur la colonne nom;
    session.flush();
    session.clear();
    récupération d'une instance avec session.get() sur l'id;
    session.saveOrUpdate() de l'instance;
    session.delete() de l'instance;
    session.flush();
    session.clear();

  4. #4
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 483
    Par défaut
    c'est une manière bizarre de faire. D'abord lancer des commit lors d'un flush n'est pas très propre. Ensuite les noms des méthodes sont ambigus. Mais soit. Tu peux nous donner la stacktrace complète?

  5. #5
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Il y a un truc que je ne comprends pas, avec un contexte JTA, c'est le conteneur qui gère la transaction, pas le programme.
    Tu ne serais pas en EXTENDED ?
    Peux-tu montrer le persistence.xml ?
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  6. #6
    Membre émérite
    Avatar de Cafeinoman
    Homme Profil pro
    Couteau suisse d'une PME
    Inscrit en
    Octobre 2012
    Messages
    629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Couteau suisse d'une PME

    Informations forums :
    Inscription : Octobre 2012
    Messages : 629
    Par défaut
    La stacktrace

    et le persistence :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    	version="2.0">
    	<persistence-unit name="GestionContacts"
    		transaction-type='JTA'>
    		<jta-data-source>java:jboss/datasources/gestioncontactsV2</jta-data-source>
    		<class>org.javacrea.gestioncontacts.pu.Adresse</class>
    		<class>org.javacrea.gestioncontacts.pu.AdressePerso</class>
    		<class>org.javacrea.gestioncontacts.pu.AdressePro</class>
    		<class>org.javacrea.gestioncontacts.pu.Collectivite</class>
    		<class>org.javacrea.gestioncontacts.pu.Commune</class>
    		<class>org.javacrea.gestioncontacts.pu.Contact</class>
    		<class>org.javacrea.gestioncontacts.pu.CV</class>
    		<class>org.javacrea.gestioncontacts.pu.Departement</class>
    		<class>org.javacrea.gestioncontacts.pu.Elu</class>
    		<class>org.javacrea.gestioncontacts.pu.Email</class>
    		<class>org.javacrea.gestioncontacts.pu.Entreprise</class>
    		<class>org.javacrea.gestioncontacts.pu.EtablissementSuperieur</class>
    		<class>org.javacrea.gestioncontacts.pu.Etudiant</class>
    		<class>org.javacrea.gestioncontacts.pu.LigneCV</class>
    		<class>org.javacrea.gestioncontacts.pu.LigneDirecte</class>
    		<class>org.javacrea.gestioncontacts.pu.Orga</class>
    		<class>org.javacrea.gestioncontacts.pu.Region</class>
    		<class>org.javacrea.gestioncontacts.pu.Salarie</class>
    		<class>org.javacrea.gestioncontacts.pu.Statut</class>
    		<class>org.javacrea.gestioncontacts.pu.Telephone</class>
    		<properties>
    			<property name="hibernate.show_sql" value="true" />
    		</properties>
    	</persistence-unit>
    </persistence>
    Par contre j'ai du mal comprendre un truc. Pour moi, je ne fait que des flush et des clear de la session hibernate, donc je force les requêtes vers la base et je vide le cache hibernate. Je ne touche pas à la transaction en elle-même, pas de commit, rien. Et le flushMode est sur commit, c'est pour ça que je fais des flush quand je juge que la session a trop de données non sauvegardées. Après si vous me dite qu'il vaut mieux ne pas toucher à tout ça et laisser le flushmode auto sans jamais faire de flush ou clear manuel, je veux bien vous croire...

  7. #7
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Je pense que tu devrais éviter de mixer JTA et des transactions gérées par l'application.
    Bon, sans être un spécialiste de la question (j'utilise des EJB avec JPA/JTA), il semble que dans un environnement JTA, les transactions gérées par l'application soient vues comme des sous-transactions de JTA.
    Dans ta séquences
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    session.delete();
    session.flush();
    tu forces la suppression mais ta transaction JTA est en attente de le faire.
    Un test à faire serait de retirer le flush() après le delete().

    Sinon, j'ai trouvé un code qui pourrait t'intéresser pour faire un clear dans ce contexte
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public void clearCache() 
    {   
       EntityManager em = getEntityManager();
       em.unwrap(JpaEntityManager.class).getServerSession().getIdentityMapAccessor().initializeAllIdentityMaps(); 
    }
    Ceci dit, si tu veux gérer les transactions à la main, je te conseille d'utiliser un environnement EXTENDED
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  8. #8
    Membre Expert Avatar de jeffray03
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2008
    Messages
    1 501
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 501
    Par défaut
    Une question:
    a quoi dte sert :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    mis au debut de ta sessionBeans
    Eric

  9. #9
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par jeffray03 Voir le message
    Une question:
    a quoi dte sert :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    mis au debut de ta sessionBeans
    Eric
    C'est juste pour dire que toutes les méthodes de la classe nécessitent une transaction.
    Ça évite d'avoir à le mettre sur chaque méthode.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  10. #10
    Membre Expert Avatar de jeffray03
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2008
    Messages
    1 501
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 501
    Par défaut
    a ma connaissance, cela doit etre celle par defaut, donc pas besoin explicite de le mettre.

  11. #11
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par jeffray03 Voir le message
    a ma connaissance, cela doit etre celle par defaut, donc pas besoin explicite de le mettre.
    Je ne sais pas trop quel est le défaut et si c'est le même d'un serveur à l'autre, dans tous les cas, là, on sait ce qu'on a (même si le défaut changeait)
    J'ai un petit côté parano faut croire mais à ma décharge, on m'a déjà fait le coup avec le lazy loading
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  12. #12
    Membre Expert Avatar de jeffray03
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2008
    Messages
    1 501
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 501
    Par défaut
    as-tu essayé avec:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    @PersistenceContext(unitName = "GestionContacts", type = PersistenceContextType.EXTENDED)
    Eric

Discussions similaires

  1. Réponses: 9
    Dernier message: 09/05/2011, 22h25
  2. detached entity passed to persist
    Par snipes dans le forum JPA
    Réponses: 2
    Dernier message: 06/08/2009, 16h19
  3. (debian,eclipse 3.4)detached entity pased to persist
    Par olivier57b dans le forum JPA
    Réponses: 9
    Dernier message: 07/03/2009, 13h27
  4. [EJB3 Entity] detached entity passed to persist
    Par maysam dans le forum Java EE
    Réponses: 3
    Dernier message: 03/12/2008, 14h43
  5. Réponses: 4
    Dernier message: 07/03/2007, 13h08

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo