GetEntityName перехватчика не используется. Ошибка в спящем режиме? - PullRequest
1 голос
/ 21 мая 2009

Я реализовал метод getEntityName в моем перехватчике. Я ожидал, что Hibernate будет вызывать метод для разрешения имени объекта (временного) объекта при сохранении объекта. Однако метод getEntityName от перехватчика не использовался в следующих сценариях:

1) session.saveOrUpdate был вызван с некоторым значением имени сущности. Ожидалось, что имя объекта будет перезаписано перехватчиком. Интересно, что когда я удалил имя сущности из вызова метода save, использовался метод getEntityName перехватчика. Похоже, он используется только при вызове сохранения без имени объекта.

2) объекты в коллекции были сохранены при каскадном сопоставлении, и однозначная ассоциация отображения коллекции использовала имя объекта в качестве ссылки на другое отображение класса. Я полагаю, что имя объекта этой ассоциации использовалось в каскадном сохранении аналогично сценарию 1.

Может кто-нибудь сказать мне, это ошибка или особенность в Hibernate?

Ниже приведены мои отображения. Версия Hibernate - 3.3.1.GA.

<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>



<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>

Ваши ответы / комментарии по этому вопросу будут очень оценены.

-------------------- ОБНОВЛЕНО -------------------------

Наверное, я знаю, в чем проблема.

Hibernate не использует перехватчик для перезаписи существующего имени объекта в save / saveOrUpdate.

Hibernate позволяет использовать дискриминаторы и имена сущностей в классе и в отображениях его подклассов.

Дискриминатор используется для идентификации отображения подкласса или суперкласса, когда запись базы данных извлекается из базы данных. Затем, насколько я понимаю, на основе сопоставления найденного подкласса в постоянном контексте создается постоянная запись (объект). Если у подкласса есть свое собственное имя объекта, это имя объекта присваивается постоянной записи. Позже всякий раз Вы обновляете постоянный объект, это имя сущности используется в качестве ключа для поиска отображения подкласса.

Исходя из вышесказанного, насколько я понимаю, Discriminator работает в одном направлении при отображении строки базы данных в объект Java (постоянный объект). Однако, когда дело доходит до отображения временного объекта Java в строке базы данных, для поиска соответствия подкласса / суперкласса используется только имя класса объекта или предоставляемый для объекта (или saveOrUpdate) метода entity-name. Так что Дискриминатор здесь не используется (интересно, почему? Может быть потому, что он не является частью постоянного объекта?).

В моем сценарии отображение класса HBAttribute получило имя сущности «USERPROPERTY», а все его подклассы также получили свои собственные имена сущностей («USERPROPERTY_USERNAME», «USERPROPERTY_TITL» и «USERPROPERTY_EMAIL»). Причина, по которой я использую имена сущностей для подклассов, заключается в том, что мне нужно повторно использовать java-классы отображений подклассов.

Класс HBAttribute имеет двунаправленную связь с классом User (User -> one-to-many -> HBAttribute). В сопоставлении классов пользователя единственный способ указать привязанное сопоставление классов - предоставить имя объекта «USERPROPERTY» HBAttribute в ассоциации «один ко многим» в коллекции пользователя. Коллекция имеет cascade = "all", и, следовательно, все операции должны быть каскадными к объектам коллекции.

И тут возникает проблема. Я создаю временный объект класса User, а затем помещаю только что созданные временные объекты класса HBAttribute в коллекцию. Таким образом, все объекты являются переходными. Затем, когда я сохраняю объект User с помощью метода session.save (user), объекты в коллекции атрибутов сохраняются на каскаде. Однако, поскольку связь «один ко многим» относится к отображению класса HBAttribute с использованием имени объекта «USERPROPERTY», имя объекта передается в метод каскадного сохранения. Сущность «USERPROPERTY» является отображением суперкласса, но существуют подклассы с их собственными именами сущностей, и Hibernate не разрешает подклассы, идентифицируемые именами сущностей (это фактически замечено в коде Hibernate. Я предполагаю, что разработчики Hibernate могли бы использовать Discriminator сделать это в этом случае). Здесь getEntityName Interceptor пригодится, чтобы сообщить Hibernate, какое имя сущности подкласса следует использовать, однако, поскольку сущность «USERPROPERTY» уже установлена ​​/ предоставлена ​​отображением коллекции, нет способа перезаписать ее перехватчиком.

Моя идея заключалась в том, чтобы хранить имена сущностей подкласса в объектах HBAttribute и использовать interceptor.getEntityName для предоставления моих имен сущностей, взятых из объектов.

Ниже приведен код java.org.hibernate.impl.SessionImpl.getEntityPersister, который НЕ позволяет перехватчику перезаписывать исходное ненулевое имя сущности.

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;
            }
        }
    }
}

А это мой взломанный код.

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;
                }
            }
        }  
    }
}

Может ли кто-то более опытный в Hibernate сказать мне, в порядке ли мое изменение и должно ли быть предложено включить его в Hibernate?

Спасибо, Anton

Ответы [ 2 ]

0 голосов
/ 05 октября 2009

Хороший улов. Спасибо за информацию. Будьте осторожны, используя этот распознаватель EntiyName, так как он немного глючит ...: http://opensource.atlassian.com/projects/hibernate/browse/HHH-4036

0 голосов
/ 09 июля 2009

В Hibernate Core 3.3.2 разрешение имен сущностей подкласса реализовано намного лучше, чем в моем исправлении.

Теперь в Tuplizer есть метод defineConcreteSubclassEntityName (Object entityInstance, фабрика SessionFactoryImplementor), который я могу перезаписать в моем случае. Класс Tuplizer может быть зарегистрирован в соответствии с отображением класса.

Существует также API-интерфейс EntityNameResolver, который вы можете реализовать и зарегистрировать в своем туплизере. Поэтому теперь нет необходимости использовать Entity Interceptors для разрешения имен объектов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...