Как обойти странное поведение Session.get() с составным идентификатором?

Я пытаюсь выполнить вход в сеанс для объекта, который объявляет составной идентификатор без сопоставленного составного идентификатора.
Используемая версия Hibernate — 3.5.5.

Код выборки является универсальным и считывает объекты-контейнеры, упаковывая фактические данные:

ClassMetadata metadata = 
          session.getSessionFactory().getClassMetadata(wrapper.getDomainClass());
Serializable id = metadata.getIdentifier(wrapper, EntityMode.POJO);
return session.get(wrapper.getDomainClass(), id, LockOptions.UPGRADE);

Код ничего не знает о фактическом сопоставлении, поэтому ему приходится обращаться к метаданным об идентификаторе.

Если сопоставление определено как:

<hibernate-mapping default-access="field">
  <class name="Wrapper"
      entity-name="Data"
      table="DATA">
    <composite-id>
      <key-property name="identifier" column="identifier" />
      <key-property name="version" column="version" />
    </composite-id>

    <component name="domainObject" class="Data">
      <property name="source" column="source" />
    </component>                    
  </class>
</hibernate-mapping>

без класса составного идентификатора id равен самому объекту и равен ссылке на оболочку.
Когда я делаю session.get() вместо извлечения объекта из базы данных, он возвращает тот же объект, который был передан в id (не равный объект, но тот же экземпляр объекта).
Обновление: На самом деле session.get() загружает объект из базы данных in переданный объект id и возвращает его обратно. Я следил за тем, чтобы сначала подумал, что он пропускает загрузку.

Решение, которое я нашел до сих пор, заключается в том, чтобы ввести сопоставленный составной идентификатор и изменить сопоставление на:

<hibernate-mapping default-access="field">
  <class name="Wrapper"
      entity-name="Data1"
      table="DATA_1">
    <composite-id  class="SurrogateKey" mapped="true">
      <key-property name="identifier" column="identifier" />
      <key-property name="version" column="version" />
    </composite-id>

    <component name="domainObject" class="Data">
      <property name="source" column="source" />
    </component>                    
  </class>
</hibernate-mapping>

SurrogateKey определяется как объект с двумя полями и равенством/хэш-кодом по мере необходимости.
При этом идентификаторе изменения, возвращаемом metadata.getIdentifier(), является экземпляром SurrogateKey, а session.get() извлекает объект из базы данных, если он существует.

Проблема с исправлением сопоставления заключается в том, что имена свойств для критериев и HQL меняются с identifier на id.identifier, что на самом деле нарушает большую часть существующего кода.

Вещи, которые я изучаю в данный момент:

  1. Есть ли способ заставить session.get() работать без объявления класса Id (я знаю, что это не рекомендуется, но количество необходимых изменений может быть непомерно высоким)?
  2. Может ли альтернатива сказать hibernate обработать свойства как раньше, без добавления id. перед ними?
  3. Обновить спящий режим до версии 4 (нелегко из-за зависимых проектов и процесса утверждения)?
  4. Есть ли какие-либо другие варианты/обходные пути?

До сих пор мне удалось заставить работать только описанное выше решение, но я ищу менее навязчивое решение и буду признателен за любые подсказки, предложения, указатели на соответствующие документы.


person aliher    schedule 05.07.2012    source источник


Ответы (2)


Что такое wrapper в вашем первом фрагменте кода? Если это присоединенный объект (как я подозреваю), очевидно, session.get() вернет присоединенный объект с тем же идентификатором, и, поскольку идентификатор является самим объектом, он вернет данный присоединенный объект. В сеансе всегда есть только один экземпляр любого данного объекта.

Теперь, чтобы ответить на ваши вопросы:

  1. AFAIK, он работает так, как ожидалось.
  2. No.
  3. Это вопрос?
  4. Лучший обходной путь — поступить правильно и прекратить использование составных идентификаторов. Используйте одноколоночные автоматически сгенерированные идентификаторы, как советует Hibernate, и все станет намного проще.
person JB Nizet    schedule 05.07.2012
comment
Оболочка не привязана к сеансу во время получения. У него есть как минимум ключ объекта, который мы хотим заменить. Предыдущая версия могла быть либо в базе данных, либо сохранена в том же сеансе. Это процесс пакетного сохранения/обновления, и автоматически сгенерированные идентификаторы не подходят, поскольку идентификаторы генерируются внешней системой. Если мы будем их использовать, то нам придется переключиться на выполнение запросов и ручную корреляцию объектов в пакетах. Если 1 - правильное поведение только для прикрепленных объектов, но для отдельных объектов это выглядит неправильно, и оно отличается для сопоставленных и несопоставленных ключей. - person aliher; 05.07.2012
comment
С помощью аннотаций вы можете делать то, что хотите, во втором вопросе, сопоставляя несколько полей объекта с @Id и указывая класс идентификатора, который имеет идентичные имена свойств и типы. Не знаю, возможно ли это с XML-сопоставлением. - person JB Nizet; 05.07.2012
comment
Я обновил исходный пост и опубликовал решение. Теперь это похоже на ошибку в спящем режиме. Сессия не принимает во внимание некоторые аспекты поведения создателя экземпляра. - person aliher; 05.07.2012

Проблема возникла из-за использования значимого объекта в качестве идентификатора для метода session.get().

Оказывается, что session.get() имеет побочные эффекты и изменяет свои аргументы (что происходит глубоко внутри PojoInstantiator.instantiate()). При загрузке, когда hibernate обнаруживает, что класс id равен классу сопоставления, он пропускает создание экземпляра и использует объект id, переданный методу, вместо создания экземпляра нового экземпляра. Этот объект удаляется из базы данных, перезаписывая существующие поля.

Решение состоит в том, чтобы создать клон объекта, если metadata.getIdentifier() возвращает объект обратно, когда ключ не сопоставлен. Этот клон будет гидратирован и возвращен функцией get().

person aliher    schedule 05.07.2012