web-dev-qa-db-fra.com

Spring + Hibernate: un objet différent avec le même identifiant a déjà été associé à la session

Dans mon application, qui utilise Spring et Hibernate, j'analyse un fichier CSV et je complète la base de données en appelant handleRow() chaque fois qu'un enregistrement est lu à partir du fichier CSV.

Mon modèle de domaine:

«Famille» a beaucoup de «Sous-famille»

'Sous-famille' a beaucoup de 'Locus' 

un "locus" appartient à une "espèce"

Family <-> SubFamily <-> Locus sont tous des mappages bidirectionnels. 

Code:

public void handleRow(Family dummyFamily, SubFamily dummySubFamily, Locus dummyLocus) {
  //Service method which access DAO layers
  CommonService serv = ctx.getCommonService();

  boolean newFamily=false;
  Family family=serv.getFamilyByFamilyId(dummyFamily.getFamilyId());
  if(family==null){
    newFamily=true;
    family=new Family();
    family.setFamilyId(dummyFamily.getFamilyId());
    family.setFamilyIPRId(dummyFamily.getFamilyIPRId());
    family.setFamilyName(dummyFamily.getFamilyName());
    family.setFamilyPattern(dummyFamily.getFamilyPattern());
    family.setRifID(dummyFamily.getRifID());
  }

  SubFamily subFamily = family.getSubFamilyBySubFamilyId( dummySubFamily.getSubFamilyId() );
  if(subFamily==null){   
    subFamily=new SubFamily();
    subFamily.setRifID(dummySubFamily.getRifID());   
    subFamily.setSubFamilyId(dummySubFamily.getSubFamilyId());
    subFamily.setSubFamilyIPRId(dummySubFamily.getSubFamilyIPRId());
    subFamily.setSubFamilyName(dummySubFamily.getSubFamilyName());
    subFamily.setSubFamilyPattern(dummySubFamily.getSubFamilyPattern());

    family.addSubFamily(subFamily);
  }

  //use the save reference, to update from GFF handler
  Locus locus = dummyLocus;

  subFamily.addLocus(locus);

  assignSpecies(serv,locus);
  //Persist object
  if(newFamily){
    serv.createFamily(family);
  } else {
    serv.updateFamily(family);
  }
}

une espèce est assignée à un locus en utilisant la méthode suivante, qui accède simplement à la couche DAO:

private void assignSpecies (CommonService serv, Locus locus) {
  String locusId = locus.getLocusId();
  String speciesId = CommonUtils.getLocusSpecies(locusId, ctx.getSpeciesList()).getSpeciesId();
  //Simply get Species object from DAO
  Species sp = serv.getSpeciesBySpeciesId(speciesId);
  locus.setSpecies(sp);  
}

Hibernate donne l'erreur suivante:

[INFO] Starting scheduled refresh cache with period [5000ms]
Hibernate: insert into species (species_id, name) values (?, ?)
Hibernate: insert into species (species_id, name) values (?, ?)
Hibernate: insert into species (species_id, name) values (?, ?)
############################ROW#####################1
SubFamiyID#######RIF0005913
Hibernate: select this_.id as id1_0_, this_.family_id as family2_1_0_, this_.rif_iD as rif3_1_0_, this_.family_name as family4_1_0_, this_.family_ipr_id as family5_1_0_, this_.family_pattern as family6_1_0_ from family this_ where this_.family_id=?
Creating NEW SubFamiyID#######RIF0005913
Hibernate: select this_.id as id3_0_, this_.species_id as species2_3_0_, this_.name as name3_0_ from species this_ where this_.species_id=?
Hibernate: insert into family (family_id, rif_iD, family_name, family_ipr_id, family_pattern) values (?, ?, ?, ?, ?)
Hibernate: insert into subfamily (sub_family_id, rif_iD, sub_family_name, sub_family_ipr_id, sub_family_pattern, family_id, sub_family_index) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into locus (locus_id, refTrans_id, function, species_id, sub_family_id, sub_family_index) values (?, ?, ?, ?, ?, ?)
Hibernate: update species set species_id=?, name=? where id=?
Hibernate: update subfamily set family_id=?, sub_family_index=? where id=?
Hibernate: update locus set sub_family_id=?, sub_family_index=? where id=?
############################ROW#####################2
SubFamiyID#######RIF0005913
Hibernate: select this_.id as id1_0_, this_.family_id as family2_1_0_, this_.rif_iD as rif3_1_0_, this_.family_name as family4_1_0_, this_.family_ipr_id as family5_1_0_, this_.family_pattern as family6_1_0_ from family this_ where this_.family_id=?
Hibernate: select subfamilie0_.family_id as family7_1_, subfamilie0_.id as id1_, subfamilie0_.sub_family_index as sub8_1_, subfamilie0_.id as id0_0_, subfamilie0_.sub_family_id as sub2_0_0_, subfamilie0_.rif_iD as rif3_0_0_, subfamilie0_.sub_family_name as sub4_0_0_, subfamilie0_.sub_family_ipr_id as sub5_0_0_, subfamilie0_.sub_family_pattern as sub6_0_0_, subfamilie0_.family_id as family7_0_0_ from subfamily subfamilie0_ where subfamilie0_.family_id=?
Hibernate: select locuslist0_.sub_family_id as sub5_1_, locuslist0_.id as id1_, locuslist0_.sub_family_index as sub7_1_, locuslist0_.id as id2_0_, locuslist0_.locus_id as locus2_2_0_, locuslist0_.refTrans_id as refTrans3_2_0_, locuslist0_.function as function2_0_, locuslist0_.sub_family_id as sub5_2_0_, locuslist0_.species_id as species6_2_0_ from locus locuslist0_ where locuslist0_.sub_family_id=?
Hibernate: select species0_.id as id3_0_, species0_.species_id as species2_3_0_, species0_.name as name3_0_ from species species0_ where species0_.id=?
Hibernate: select this_.id as id1_0_, this_.family_id as family2_1_0_, this_.rif_iD as rif3_1_0_, this_.family_name as family4_1_0_, this_.family_ipr_id as family5_1_0_, this_.family_pattern as family6_1_0_ from family this_ where this_.family_id=?
Hibernate: select this_.id as id3_0_, this_.species_id as species2_3_0_, this_.name as name3_0_ from species this_ where this_.species_id=?
Exception in thread "main" [INFO] Closing Compass [compass]
org.springframework.orm.hibernate3.HibernateSystemException: a different object with the same identifier value was already associated with the session: [com.bigg.nihonbare.common.domain.Species#1]; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.bigg.nihonbare.common.domain.Species#1]
Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.bigg.nihonbare.common.domain.Species#1]
 at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.Java:590)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.Java:284)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.Java:223)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.Java:89)
 at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.Java:70)
 at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.Java:507)
 at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.Java:499)
 at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.Java:218)
 at org.hibernate.engine.Cascade.cascadeToOne(Cascade.Java:268)

Des conseils?

19

Utilisez merge(). L'exception signifie que la session en cours est déjà consciente de l'entité que vous passez. Sinon, vérifiez comment vous avez remplacé hashCode() et equals() - il devrait retourner des valeurs différentes pour différentes entités.

33
Bozho

Vous pouvez également rencontrer ce problème si vous effectuez une delete() ou une update(). Le problème est susceptible de se produire si vous créez vous-même le pojo hibernate-mappé, éventuellement à partir d'un DTO. Ce pojo a maintenant le même identifiant que celui qui se trouve déjà dans la variable Session, ce qui pose problème.

Vous avez maintenant deux options. Soit ce que @Bozho a dit et la première merge() l'objet. Cela prend en charge la mise à jour. Pour la suppression, prenez l'objet renvoyé par merge() et supprimez-le.

L'autre option consiste à interroger d'abord la Session à l'aide de l'ID de l'objet, puis à le supprimer ou le mettre à jour.

11
Niel de Wet

J'ai résolu donc: Sur la méthode de suppression:

    this.getHibernateTemplate().clear();

    this.getHibernateTemplate().delete(obj);

    // Esta línea realiza el "commit" del comando
    this.getHibernateTemplate().flush();

Sur la méthode de mise à jour:

    this.getHibernateTemplate().merge(obj);

    // Esta línea realiza el "commit" del comando
    this.getHibernateTemplate().flush();
1
Gabriel

Si vous mettez à jour un objet evict () à partir d'une session après l'appel de saveOrUpdate (), vérifiez également l'implémentation hashCode de l'objet.

0
Praveena DG

J'ai vu cela lorsqu'une entité ne possède pas d'annotation GeneratedValue pour sa colonne ID:

@GeneratedValue(strategy = GenerationType.AUTO)
0
Tom Chamberlain

Vous avez peut-être créé deux instances de Session

Session session = factory.openSession();

Si vous avez ouvert une session dans une fonction et en avez exécuté une autre en créant une autre session, ce problème se produit.