-->
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.  [ 2 posts ] 
Author Message
 Post subject: Interceptor's getEntityName is not used. Bug in Hibernate?
PostPosted: Wed May 20, 2009 8:21 pm 
Newbie

Joined: Mon Apr 06, 2009 9:55 pm
Posts: 12
Hi All,

I implemented getEntityName method in my interceptor. I was expecting that the method will be called by Hibernate to resolve entity name of a (transient) object when I save the object. However, the method getEntityName from the interceptor was not used in the following scenarios:

1) session.saveOrUpdate was called with some entity name value. It was expected that the entity name will be overwritten by the interceptor.
The interesting thing it that when I removed the entity name from save method call, the interceptor's getEntityName was used. Looks like it is only used when save is called without entity name.

2) objects in a collection were saved on cascading and the one-to-many association of the collection mapping used entity-name as reference to another class mapping. I beleive that this association's entity-name was used in cascading save similarly as in scenario 1.

Can someone tell me whether it is a bug or feature in Hibernate?

Below are my mappings. Version of Hibernate is 3.3.1.GA.

Code:
  <class name="User" table="HUBUSER">
    <id name="id" type="integer">
      <column name="USERID"/>
      <generator class="sequence">
        <param name="sequence">USERID</param>
      </generator>
    </id>
    ...
    <map cascade="all" inverse="true" name="attributes">
      <key on-delete="cascade" update="false">
        <column name="USERID"/>
      </key>
      <map-key column="PROPERTYKEY" type="string"/>
      <one-to-many class="HBAttribute" entity-name="USERPROPERTY"/>
    </map>
    ...
  </class>


Code:
 
    <class discriminator-value="HBAttribute" entity-name="USERPROPERTY" name="HBAttribute" table="USERPROPERTY">
    <id name="id" type="integer">
      <column name="USERPROPERTYID"/>
      <generator class="sequence">
        <param name="sequence">USERPROPERTYID</param>
      </generator>
    </id>
    <discriminator column="PROPERTYKEY" type="string"/>
    <property insert="false" name="propertyKey" not-null="false" type="string" update="false">
      <column name="PROPERTYKEY"/>
    </property>
    <many-to-one class="User" name="hubObject" not-null="true" update="false">
      <column name="USERID"/>
    </many-to-one>
    <!-- Subclass for USERNAME -->
    <subclass discriminator-value="USERNAME" entity-name="USERPROPERTY_USERNAME" name="HBAttributeString">
      <property name="value" not-null="false" type="string">
        <column name="PROPVALCHAR"/>
      </property>
    </subclass>
    <!-- Subclass for TITL -->
    <subclass discriminator-value="TITL" entity-name="USERPROPERTY_TITL" name="HBAttributeString">
      <property name="value" not-null="false" type="string">
        <column name="PROPVALCHAR"/>
      </property>
    </subclass>
    <!-- Subclass for EMAIL -->
    <subclass discriminator-value="EMAIL" entity-name="USERPROPERTY_EMAIL" name="HBAttributeString">
      <property name="value" not-null="false" type="string">
        <column length="80" name="PROPVALCHAR"/>
      </property>
    </subclass>
  </class>


Your answers/comments on this issue would be very much appriciated.

Thanks,
Anton


Top
 Profile  
 
 Post subject: Re: Interceptor's getEntityName is not used. Bug in Hibernate?
PostPosted: Thu May 21, 2009 6:07 am 
Newbie

Joined: Mon Apr 06, 2009 9:55 pm
Posts: 12
I guess I know what the problem is.

Hibernate does not use an interceptor to overwrite existing entity name on save/saveOrUpdate.

Hibernate allows to use discriminators and entity-names in a class and its subclasses mappings.

Discriminator is used to identify a subclass or super class mapping when a database record is fetched from a database.
Then as far as I understand, based on the found subclass mapping a persistent entry (object) is created in persistent context.
If the subclass has got its own entity-name this entity-name gets assigned to the persistent entry. Later whenever
you update the persistent object this entity-name is used as a key to find the subclass mapping.

Based on the above, as far as I understand, Discriminator works in one direction, when mapping database row to a java object (persistent object).
However when it comes to mapping of a transient java object to a database row, only class name of the object or provided to the save (or saveOrUpdate) method entity-name is used to find subclass/super class mapping. So Discriminator here is not used (I wonder why? May be because the it is not a part of the persistent object?).

In my scenario HBAttribute class mapping has got the "USERPROPERTY" entity-name and all its subclasses also have got their own entity-names ("USERPROPERTY_USERNAME", "USERPROPERTY_TITL" and "USERPROPERTY_EMAIL").
The reason why I use entity-names for the subclasses is that I need to reuse the java classes of the subclass mappings.

The HBAttribute class has got bidirectional association with the User class (User -> one-to-many -> HBAttribute) .
In User class mapping the only way to specify referenced class mapping is to provide HBAttribute's "USERPROPERTY" entity-name in one-to-many association of the User's collection. The collection has got cascade="all" and hence all operations are expected to be cascaded to the collection's objects.

And here the problem comes. I create transient object of User class and then put just created transient objects of HBAttribute class to the collection. So all objects are transient. Then when I save the User object via session.save(user) method, the objects in the attributes collection get saved on cascade. However, because the one-to-many association refers HBAttribute class mapping using "USERPROPERTY" entity-name, the entity-name gets passed to the cascading save method. The "USERPROPERTY" entity is a super class mapping, but there are subclasses with their own entity-names and Hibernate does not resolve subclasses identified by entity-names (this is actually noticed in the Hibernate's code. I guess that Hibernate developers could use Discriminator to do that in this case). Here Interceptor's getEntityName would come in handy to tell Hibernate what subclass entity-name should be used, however as the "USERPROPERTY" entity has already been set/provided by the collection mapping there is no way to overwrite it with the interceptor.

My idea was to store subclass entity-names in the HBAttribute objects and use the interceptor.getEntityName to provide my the entity-names taken from the objects.

Below is the code of java.org.hibernate.impl.SessionImpl.getEntityPersister which does NOT allow an interceptor to overwrite original not null entity-name.

Code:
    public EntityPersister getEntityPersister(final String entityName, final Object object) {
        errorIfClosed();
        if (entityName==null) {
            return factory.getEntityPersister( guessEntityName( object ) );
        }
        else {
            // try block is a hack around fact that currently tuplizers are not
            // given the opportunity to resolve a subclass entity name.  this
            // allows the (we assume custom) interceptor the ability to
            // influence this decision if we were not able to based on the
            // given entityName
            try {
                return factory.getEntityPersister( entityName )
                        .getSubclassEntityPersister( object, getFactory(), entityMode );
            }
            catch( HibernateException e ) {
                try {
                    return getEntityPersister( null, object );
                }
                catch( HibernateException e2 ) {
                    throw e;
                }
            }
        }
    }


And this is my hacked code.

Code:
    public EntityPersister getEntityPersister(final String entityName, final Object object) {
        errorIfClosed();
        if (entityName==null) {
            return factory.getEntityPersister( guessEntityName( object ) );
        }
        else {
            //even if the original entity-name is not null try to resolve
            //the entity-name via interceptor, if the returned entity-name
            // is null, then use original entity-name.
            String overwrittenEntityName = interceptor.getEntityName( object );
            if (overwrittenEntityName != null) {
                return factory.getEntityPersister( overwrittenEntityName );
            } else {
                // try block is a hack around fact that currently tuplizers are not
                // given the opportunity to resolve a subclass entity name.  this
                // allows the (we assume custom) interceptor the ability to
                // influence this decision if we were not able to based on the
                // given entityName
                try {
                    return factory.getEntityPersister( entityName )
                            .getSubclassEntityPersister( object, getFactory(), entityMode );
                }
                catch( HibernateException e ) {
                    try {
                        return getEntityPersister( null, object );
                    }
                    catch( HibernateException e2 ) {
                        throw e;
                    }
                }
            }   
        }
    }



Could someone more experienced in Hibernate tell me whether my change is OK and should be proposed to be included in Hibernate?

Thanks,
Anton

--
Regards,
Anton Anufriev


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 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.