title: Interceptor's getEntityName is not used. Bug in Hibernate?
body: <p>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: </p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>Can someone tell me whether it is a bug or feature in Hibernate?</p>
  
  <p>Below are my mappings. Version of Hibernate is 3.3.1.GA. </p>
  
  <pre><code>&lt;class name="User" table="HUBUSER"&gt;
  &lt;id name="id" type="integer"&gt;
    &lt;column name="USERID"/&gt;
    &lt;generator class="sequence"&gt;
      &lt;param name="sequence"&gt;USERID&lt;/param&gt;
    &lt;/generator&gt;
  &lt;/id&gt;
  ...
  &lt;map cascade="all" inverse="true" name="attributes"&gt;
    &lt;key on-delete="cascade" update="false"&gt;
      &lt;column name="USERID"/&gt;
    &lt;/key&gt;
    &lt;map-key column="PROPERTYKEY" type="string"/&gt;
    &lt;one-to-many class="HBAttribute" entity-name="USERPROPERTY"/&gt;
  &lt;/map&gt;
  ...
  &lt;/class&gt;
  
  
  
  &lt;class discriminator-value="HBAttribute" entity-name="USERPROPERTY" name="HBAttribute" table="USERPROPERTY"&gt;
  &lt;id name="id" type="integer"&gt;
    &lt;column name="USERPROPERTYID"/&gt;
    &lt;generator class="sequence"&gt;
      &lt;param name="sequence"&gt;USERPROPERTYID&lt;/param&gt;
    &lt;/generator&gt;
  &lt;/id&gt;
  &lt;discriminator column="PROPERTYKEY" type="string"/&gt;
  &lt;property insert="false" name="propertyKey" not-null="false" type="string" update="false"&gt;
    &lt;column name="PROPERTYKEY"/&gt;
  &lt;/property&gt;
  &lt;many-to-one class="User" name="hubObject" not-null="true" update="false"&gt;
    &lt;column name="USERID"/&gt;
  &lt;/many-to-one&gt;
  &lt;!-- Subclass for USERNAME --&gt;
  &lt;subclass discriminator-value="USERNAME" entity-name="USERPROPERTY_USERNAME" name="HBAttributeString"&gt;
    &lt;property name="value" not-null="false" type="string"&gt;
      &lt;column name="PROPVALCHAR"/&gt;
    &lt;/property&gt;
  &lt;/subclass&gt;
  &lt;!-- Subclass for TITL --&gt;
  &lt;subclass discriminator-value="TITL" entity-name="USERPROPERTY_TITL" name="HBAttributeString"&gt;
    &lt;property name="value" not-null="false" type="string"&gt;
      &lt;column name="PROPVALCHAR"/&gt;
    &lt;/property&gt;
  &lt;/subclass&gt;
  &lt;!-- Subclass for EMAIL --&gt;
  &lt;subclass discriminator-value="EMAIL" entity-name="USERPROPERTY_EMAIL" name="HBAttributeString"&gt;
    &lt;property name="value" not-null="false" type="string"&gt;
      &lt;column length="80" name="PROPVALCHAR"/&gt;
    &lt;/property&gt;
  &lt;/subclass&gt;
  </code></pre>
  
  <p></p>
  
  <p>Your answers/comments on this issue would be very much appriciated.</p>
  
  <p>--------------------UPDATED-------------------------</p>
  
  <p>I guess I know what the problem is.</p>
  
  <p>Hibernate does not use an interceptor to overwrite existing entity name on save/saveOrUpdate.</p>
  
  <p>Hibernate allows to use discriminators and entity-names in a class and its subclasses mappings.</p>
  
  <p>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.</p>
  
  <p>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?).</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>Below is the code of java.org.hibernate.impl.SessionImpl.getEntityPersister which does NOT allow an interceptor to overwrite original not null entity-name.</p>
  
  <pre><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;
              }
          }
      }
  }
  </code></pre>
  
  <p>And this is my hacked code.</p>
  
  <pre><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;
                  }
              }
          }  
      }
  }
  </code></pre>
  
  <p>Could someone more experienced in Hibernate tell me whether my change is OK and should be proposed to be included in Hibernate?</p>
  
  <p>Thanks,
  Anton</p>