Я реализовал метод 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