-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: mapping unidirectional one-to-many with join table
PostPosted: Thu Jun 03, 2010 8:12 am 
Beginner
Beginner

Joined: Wed Jul 01, 2009 8:11 am
Posts: 34
Hi,

i'm mapping a unidirectional one-to-many assocication via a join table like described here. My mapping files just look like in the example:

Code:
<class name="Parent">
    <id column="PARENT_ID" name="id" type="long">
      <generator class="native"/>
    </id>

    <set cascade="save-update" name="children" table="ParentChild">
      <key column="PARENT_ID"/> <-- does this have to be the same as the id-column above?
      <many-to-many class="Child" column="CHILD_ID" unique="true"/>
    </set>
</class>

<class name="Child">
    <id column="CHILD_ID" name="id" type="long">
      <generator class="native"/>
    </id>
</class>

Here are the tables that are created in HSQLDB by Hibernate's hibernate.hbm2ddl.auto:
Code:
create table Parent (PARENT_ID bigint generated by default as identity (start with 1), primary key (PARENT_ID))
create table Child (CHILD_ID bigint generated by default as identity (start with 1), primary key (CHILD_ID))
create table ParentChild (PARENT_ID bigint not null, CHILD_ID bigint not null, primary key (PARENT_ID, CHILD_ID), unique (CHILD_ID))

alter table ParentChild add constraint FKFFA9F958339A6D9 foreign key (Child_ID) references Child
alter table ParentChild add constraint FKFFA9F958D6EADC61 foreign key (PARENT_ID) references Parent

When i save a new parent-object with a set of child-objects the following SQL inserts are made: (i approved with the debugger that the parent looks like expected before saving)
Code:
INSERT INTO PARENT VALUES(1)
INSERT INTO CHILD VALUES(1)

When i load the parent-object, its child-set is empty. The ParentChild table also is.

what am i doing wrong? i can't see it.

best regards


Top
 Profile  
 
 Post subject: Re: mapping unidirectional one-to-many with join table
PostPosted: Tue Jun 08, 2010 8:59 am 
Beginner
Beginner

Joined: Wed Jul 01, 2009 8:11 am
Posts: 34
still having the same problem. trying to fix it since 5 days now! this problem is really absurd, this is the most basic thing Hibernate should be able to do but it doesn't work for me.

these are the things i've tried/checked so far with no success:
* used MySQL instead of HSQLDB
* changed the mapping from many-to-many to one to many:
Code:
    <set name="children">
      <key column="PARENT_ID"/>
      <one-to-many class="Child"/>
    </set>
i mean there isn't even a way to make a mistake here, just copying from the reference and replacing some strings
* checked the implementation classes if they correctly use a set as collection type
* checked Hibernate dependencies if the packages have the correct version
* deleted and re-deployed the project
* deleted the database and re-deployed by letting the schemas be generated by Hibernate hibernate.hbm2ddl.auto -> create
edit: * created a new eclipse project from scratch, updated every needed library to its last version, made a clean deploy

i also went through the Hibernate sources with the debugger but it doesn't tell me too much what is going wrong when i don't know how it should look like the right way.

i'm assuming this problem must be a really simple one causing this much trouble. what else could i check? does anyone has experience with simple associations not working?

edit: using Hibernate version hibernate-distribution-3.5.2-Final.
edit2: furthermore using HSQLDB 1.8.1.2
edit3: using dialect: org.hibernate.dialect.HSQLDialect

greets garz


Top
 Profile  
 
 Post subject: Re: mapping unidirectional one-to-many with join table
PostPosted: Wed Jun 09, 2010 9:41 am 
Beginner
Beginner

Joined: Wed Jul 01, 2009 8:11 am
Posts: 34
Shouldn't Hibernate at least produce some kind of query for the join-table? Jugding from the Hibernate logs and the MySQL/HSQLDB logs no such query is created. Since i don't know how a working many-to-many unidirectional mapping should look like, maybe somebody could tell me?

I know changed the cascading style of the many-to-many to nothing to not have any differences to the example of the Hibernate references:
Code:
<class name="Parent">
    <id column="PARENT_ID" name="id" type="long">
      <generator class="native"/>
    </id>

    <set name="children" table="ParentChild">
      <key column="PARENT_ID"/>
      <many-to-many class="Child" column="CHILD_ID" unique="true"/>
    </set>
</class>
Therefor i now save the Child-objects by hand before i save the Parent-object.

Here is what Hibernate is doing. This is the place the save is performed:
Code:
DefaultSaveEventListener(AbstractSaveEventListener).performSaveOrReplicate(Object, EntityKey, EntityPersister, boolean, Object, EventSource, boolean) line: 259
DefaultSaveEventListener(AbstractSaveEventListener).performSave(Object, Serializable, EntityPersister, boolean, Object, EventSource, boolean) line: 204   
DefaultSaveEventListener(AbstractSaveEventListener).saveWithGeneratedId(Object, String, Object, EventSource, boolean) line: 130   
DefaultSaveEventListener(DefaultSaveOrUpdateEventListener).saveWithGeneratedOrRequestedId(SaveOrUpdateEvent) line: 210   
DefaultSaveEventListener.saveWithGeneratedOrRequestedId(SaveOrUpdateEvent) line: 56   
DefaultSaveEventListener(DefaultSaveOrUpdateEventListener).entityIsTransient(SaveOrUpdateEvent) line: 195   
DefaultSaveEventListener.performSaveOrUpdate(SaveOrUpdateEvent) line: 50   
DefaultSaveEventListener(DefaultSaveOrUpdateEventListener).onSaveOrUpdate(SaveOrUpdateEvent) line: 93   
SessionImpl.fireSave(SaveOrUpdateEvent) line: 705   
SessionImpl.save(String, Object) line: 693   
SessionImpl.save(Object) line: 689   


Code:
      if ( substitute ) {
         persister.setPropertyValues( entity, values, source.getEntityMode() );
Here Set of my parent object becomes a PersistentSet which holds the Child-objects as expected. Also the Child-objects have an identifier as expected as they now get persisted before the Parent-object gets persisted.

Code:
      if ( useIdentityColumn ) {
         EntityIdentityInsertAction insert = new EntityIdentityInsertAction(
               values, entity, persister, source, shouldDelayIdentityInserts
         );
         if ( !shouldDelayIdentityInserts ) {
            log.debug( "executing identity-insert immediately" );
            source.getActionQueue().execute( insert );
            id = insert.getGeneratedId();
            key = new EntityKey( id, persister, source.getEntityMode() );
            source.getPersistenceContext().checkUniqueness( key, entity );
         }
Here the useIdentityColumn is true and shouldDelayIdentityInserts is false, therefor Hibernate executes the insert in this branch.

From there the journey continues to the AbstractEntityPersister:
Code:
SingleTableEntityPersister(AbstractEntityPersister).insert(Object[], Object, SessionImplementor) line: 2836
EntityIdentityInsertAction.execute() line: 71   
ActionQueue.execute(Executable) line: 268   
DefaultSaveEventListener(AbstractSaveEventListener).performSaveOrReplicate(Object, EntityKey, EntityPersister, boolean, Object, EventSource, boolean) line: 321   
Code:
      if ( entityMetamodel.isDynamicInsert() ) {
         // For the case of dynamic-insert="true", we need to generate the INSERT SQL
         boolean[] notNull = getPropertiesToInsert( fields );
         id = insert( fields, notNull, generateInsertString( true, notNull ), object, session );
         for ( int j = 1; j < span; j++ ) {
   2836:      insert( id, fields, notNull, j, generateInsertString( notNull, j ), object, session );
         }
      }
      else {
         // For the case of dynamic-insert="false", use the static SQL
         id = insert( fields, getPropertyInsertability(), getSQLIdentityInsertString(), object, session );
         for ( int j = 1; j < span; j++ ) {
            insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
         }
isDynamicInsert() is false so the else branch is taken. The paremeter have the following values:
fields still contain the PersistentSet with the expected Child-objects.
getPropertyInsertability() (notNull parameter) is true for the children.
getSQLIdentityInsertString() (sql parameter) is "insert into Parent values ( )"
And session is: SessionImpl(PersistenceContext[entityKeys=[EntityKey[components.Child#1]],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]])

Maybe the ActionQueue is too empty?

Inside the insert-statement the following statement in AbstractEntityPersister on line 2329 is called:
Code:
return identityDelegate.performInsert( sql, session, binder );
Then it goes straight for the insert to:
Code:
DelegatingPreparedStatement.executeUpdate() line: 102   
IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(PreparedStatement) line: 94   
IdentityGenerator$GetGeneratedKeysDelegate(AbstractReturningDelegate).performInsert(String, SessionImplementor, Binder) line: 57   
SingleTableEntityPersister(AbstractEntityPersister).insert(Object[], boolean[], String, Object, SessionImplementor) line: 2329   
There the new Parent is inserted. The journey goes back to SingleTableEntityPersister(AbstractEntityPersister).insert(Object[], Object, SessionImplementor) line: 2836:
Code:
2836:   id = insert( fields, getPropertyInsertability(), getSQLIdentityInsertString(), object, session );
         for ( int j = 1; j < span; j++ ) {
            insert( id, fields, getPropertyInsertability(), j, getSQLInsertStrings()[j], object, session );
span here is 1 so the for-loop doesn't do anything. from here it goes back to EntityIdentityInsertAction.execute() line: 71
Code:
      if ( !veto ) {
   71:   generatedId = persister.insert( state, instance, session );
         if ( persister.hasInsertGeneratedProperties() ) {
            persister.processInsertGeneratedProperties( generatedId, instance, state, session );
         }
         //need to do that here rather than in the save event listener to let
         //the post insert events to have a id-filled entity when IDENTITY is used (EJB3)
         persister.setIdentifier( instance, generatedId, session );
      }
persister.hasInsertGeneratedProperties() is false, therefor nothing is done there.

Afterwards postInsert() is done:
Code:
      if ( isDelayed ) {
         getSession().getPersistenceContext().replaceDelayedEntityIdentityInsertKeys( delayedEntityKey, generatedId );
      }
      PostInsertEventListener[] postListeners = getSession().getListeners()
            .getPostInsertEventListeners();
      if (postListeners.length>0) {
         PostInsertEvent postEvent = new PostInsertEvent(
               getInstance(),
               generatedId,
               state,
               getPersister(),
               (EventSource) getSession()
         );
         for ( int i = 0; i < postListeners.length; i++ ) {
            postListeners[i].onPostInsert(postEvent);
         }
      }
isDelayed is false and (postListeners.length>0) too. Therefor nothing is done here.

Maybe this is the place the join-table ought to be filled?

After this it goes back to ActionQueue.execute(Executable) line: 271
Code:
      finally {
   271:   beforeTransactionProcesses.register( executable.getBeforeTransactionCompletionProcess() );
         if ( session.getFactory().getSettings().isQueryCacheEnabled() ) {
            final String[] spaces = (String[]) executable.getPropertySpaces();
            afterTransactionProcesses.addSpacesToInvalidate( spaces );
            session.getFactory().getUpdateTimestampsCache().preinvalidate( executable.getPropertySpaces() );
         }
         afterTransactionProcesses.register( executable.getAfterTransactionCompletionProcess() );
      }
executable.getBeforeTransactionCompletionProcess() returns null. isQueryCacheEnabled() is false too. executable.getAfterTransactionCompletionProcess() is null.

Then it goes back to DefaultSaveEventListener(AbstractSaveEventListener).performSaveOrReplicate(Object, EntityKey, EntityPersister, boolean, Object, EventSource, boolean) line: 348
Code:
      if ( !useIdentityColumn ) {
         source.getActionQueue().addAction(
               new EntityInsertAction( id, values, entity, version, persister, source )
         );
      }

      cascadeAfterSave( source, persister, entity, anything );
useIdentityColumn is true therefor the if-statement is jumped over. cascadeAfterSave also does nothing because cascading isn't used anymore.

Then it goes back to SessionImpl.fireSave(SaveOrUpdateEvent) line: 704
Code:
   private Serializable fireSave(SaveOrUpdateEvent event) {
      errorIfClosed();
      checkTransactionSynchStatus();
      SaveOrUpdateEventListener[] saveEventListener = listeners.getSaveEventListeners();
704:   for ( int i = 0; i < saveEventListener.length; i++ ) {
         saveEventListener[i].onSaveOrUpdate(event);
      }
      return event.getResultId();
   }
saveEventListener.length is 1 therefor the for-loop ends here.

And that's it. Where should have Hibernate did something different? Please help me i'm really stuck on this problem. I don't know what to do anymore and this is already the 6. day of fighting.


Top
 Profile  
 
 Post subject: Re: mapping unidirectional one-to-many with join table
PostPosted: Thu Jun 10, 2010 11:20 am 
Beginner
Beginner

Joined: Wed Jul 01, 2009 8:11 am
Posts: 34
ok i found the shit after already giving up. i use spring to configure transactions via declaritive aop. i moved my manager classes into another directory and therefor the pointcut didnt match anymore. that way there were not any transactions used and thats why Hibernate didn't do shit.

good bye


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.