Question

J'essaie d'ajouter un pojo à une collection d'un autre pojo. Je suis sûr que je commets une erreur vraiment stupide quelque part le long des lignes, mais je ne peux pas comprendre comment le résoudre.

J'ai un tableau LookupTable contenant une liste de colonnes:

public class LookupTable {
  private long id;
  // More properties go here...
  private List<Column> columns;

  public void addColumn(Column column) {
    this.columns.add(column);
  }

  // More methods go here...
}

Dans ma configuration d'hibernation, j'ai:

<class name="LookupTable" table="ARR_LOOKUP_TABLE">
  <id name="id" column="ID">
    <generator class="native"/>
  </id>
  <!-- Some properties here -->
  <bag name="columns" cascade="all,delete-orphan" access="field">
    <key column="LOOKUP_TABLE" not-null="true"/>
    <one-to-many class="Column"/>
  </bag>
</class>

<class name="Column" table="ARR_LOOKUP_COLUMN">
  <id name="id" column="ID">
    <generator class="native"/>
  </id>
  <!-- Some properties here -->
</class>

Dans mon fichier de configuration Spring, j'ai:

<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>
<aop:config>
  <aop:pointcut id="managers" expression="execution(public * com.foo.*Manager.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="managers"/>
</aop:config>

Et enfin, le code où tout cela échoue dans ma classe de gestionnaire (com.foo.LookupTableManager):

public void addColumnToTable(Column column, long tableId) {
  LookupTable lookupTable = this.lookupTableDao.findById(tableId);
  lookupTable.addColumn(column);
  this.lookupTableDao.saveOrUpdate(lookupTable);
}

La variable lookupTableDao fait ici référence à une classe DAO simple qui étend HibernateDaoSupport.

L'erreur que je reçois est la suivante:

org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
  at org.hibernate.collection.AbstractPersistentCollection.setCurrentSession(AbstractPersistentCollection.java:410)
  at org.hibernate.event.def.OnUpdateVisitor.processCollection(OnUpdateVisitor.java:43)
  at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101)
  at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61)
  at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55)
  at org.hibernate.event.def.AbstractVisitor.process(AbstractVisitor.java:123)
  at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:293)
  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.impl.SessionImpl.saveOrUpdate(SessionImpl.java:495)
  at com.foo.AbstractDao.saveOrUpdate(AbstractDao.java:29)
  at com.foo.LookupTableManager.addColumnToTable(LookupTableManager.java:338)
... etc ...

OK, je comprends le message de base que je reçois. Mais ce que je ne comprends pas, c’est où j’obtiens la deuxième session ... Quelqu'un peut-il m'aider à cet égard?

J'utilise Hibernate 3.2.6.ga, Spring 2.5.5 et Tomcat 6.0

Était-ce utile?

La solution

Il s’est avéré que je n’avais pas du tout de transaction. J'ai utilisé presque la même configuration de transaction dans l'un de mes autres fichiers de configuration.

La coupure de point située là-bas était également appelée "gestionnaires", de sorte que mon conseiller ici faisait référence à la coupure de point dans l'autre fichier.

Renommer le pointcut a résolu mon problème.

Autres conseils

Je suppose que l'appel lookupTableDao.findById récupère votre objet dans une session, mais que le lookupTableDao.saveOrUpdate est différent - comment obtenez-vous le < code> Session , via Spring?

D'où provient l'objet Column - cet objet existe-t-il déjà dans la base de données ou est-il neuf?

Le problème était lié au mappage de filtre de la vue Open Session In. Il créait une session sur getSession Et autre sur la sauvegarde.

Vous ne pouvez modifier que singleSession comme faux défaut son vrai

Apparemment, l’utilisation de la fusion résoudra probablement le problème

myInstance = (myInstanceClass) Session.merge(myInstance);

J'ai eu le même problème avec le code suivant: Je voulais mettre à jour une certaine entité Store. Je le récupérais de cette méthode:

@Override
public Store getStoreByStoreId(final long storeId) {
    getHibernateTemplate().execute(new HibernateCallback<Store>() {
        @Override
        public StoredoInHibernate(Session session) throws HibernateException, SQLException {
            return (Store) session.createCriteria(Store.class)
                    .add(Restrictions.eq(Store.PROP_ID, storeId))
                    .uniqueResult();
        }
    });
}

Ensuite, je mettais à jour le magasin selon la méthode suivante:

@Override
public void updateStoreByStoreId(final long storeId) {
    getHibernateTemplate().execute(new HibernateCallback<Void>() {
        @Override
        public Void doInHibernate(Session session) throws HibernateException, SQLException {
            Store toBeUpdated =  getStoreByStoreId(storeId);

            if (toBeUpdated != null){
                // ..change values for certain fields
                session.update(toBeUpdated);
            }
            return null;
        }
    });
} 

Il s’avère que j’ai eu la "tentative illégale ...". erreur car la première session de la méthode get ne fermait pas et j'essayais de mettre à jour le magasin avec l'autre session. La solution pour moi était de déplacer l’appel de récupération du magasin à mettre à jour dans la méthode de mise à jour.

@Override
public void updateStoreByStoreId(final long storeId) {
    getHibernateTemplate().execute(new HibernateCallback<Void>() {
        @Override
        public Void doInHibernate(Session session) throws HibernateException, SQLException {
            Store toBeUpdated =  (Store) session.createCriteria(Store.class)
                    .add(Restrictions.eq(Store.PROP_ID, storeId))
                    .uniqueResult();

            if (toBeUpdated != null){
                // ..change values for certain fields
                session.update(toBeUpdated );
            }
            return null;
        }
    });
} 

Si vous voulez seulement une transaction sur une partie de votre code, vous pouvez utiliser quelque chose comme ceci:

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

et dans la classe:

@Autowired private SessionFactory sessionFactory;

Plus tard, autour du code devant faire les choses dans la même transaction:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

Stuff stuff = getStuff();
manipulate(stuff);
send(stuff);
save(stuff);

tx.commit();

Je l'utilise à l'intérieur d'une boucle dans des travaux par lots de longue durée.

J'utilisais le serveur IIS Windows Server et le simple fait de changer le mode Pipeline géré AppPool de Intégré à Classique a résolu mon problème.

Merci

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top