Проблема наследования прокси Hibernate - PullRequest
3 голосов
/ 28 сентября 2011

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

У меня есть следующие классы:

@Entity
@Table(name="...")
@Inheritance(strategy=InheritanceType.JOINED)
public class A implements Serializable{...}

@Entity
@Table(name="...")
@PrimaryKeyJoinColumn(name="...")
public class B extends A{...}

Выглядит довольно просто, и запрос, который я использую для получения записей, в основном таков:

FROM A

Это также возвращает экземпляры B, что я и ожидал. Записи загружаются в базовый компонент для представления (компонент находится в области видимости, контекст постоянства завершен). Представление обращается к компоненту через EL, чтобы получить записи для dataTable:

<h:dataTable value="#{bean.entries}" var="entry">...</h:dataTable>

Внутри таблицы данных у меня есть команда linkLink:

<h:commandLink value="click" actionListener="#{bean.doSomething}">
    <f:setPropertyActionListener value="#{entry}" target="#{bean.selected}" />
</h:commandLink>

Бин работает с объектами типа A, но CDI-Decorator, который вызывается при вызове actionListener-Expression, будет выполнять дополнительные действия, если тип выбранной записи является подклассом:

public void doSomething(ActionEvent event){
    if(delegate.getSelected() instanceof B){
        // special
    }else{
        delegate.doSomething(event);
    }
}

А теперь все становится сложнее. If вводится «иногда», но не тогда, когда ожидается. Отладка показала, что объект, возвращаемый delegate.getSelected(), имеет тип A_javassisst, ДАЖЕ, когда он должен быть экземпляром B. Лучшее в этом то, что метод toString () возвращает B @ 123, что позволяет мне верить в Во-первых, объект относится к типу B, но это не так ...

Теперь мы подошли к моему вопросу ... Что, черт возьми, там происходит ??? Я уже думал о некоторых проблемах сериализации, которые могут возникнуть при сохранении состояния dataTable, но я не уверен (не должен ли datatable извлекать значения, возвращаемые выражением-значением, и использовать их для обхода, или это может сломаться государство?).

dataTable является таблицей данных PrimeFaces, не пытался датировать JSF, но не может быть ошибкой простых лиц ...

Все это в следующих условиях:

  • WAS 8.0.0.1 (=> OpenWebBeans)
  • CODI 1.1.1
  • MyFaces 2.1.1
  • Hibernate 3.6.5

Заранее спасибо за помощь!

EDIT:

Все мои объекты относятся к типу B, это означает, что БД содержит запись в таблице "b" для каждой записи в таблице "a", но иногда я получаю возвращаемые объекты из JPA / Hibernate, которые не являются экземплярами B !! Пожалуйста, мне нужна помощь в этом, я понятия не имею, почему это могло произойти!?!

EDIT:

Мой диагноз был неверным, возвращенные типы правильные! У меня действительно есть Proxy типа B, а не Proxy типа A. Моя проблема заключалась в том, что декорированный метод вызывался перед установщиком. Это больше связано с JSF, чем с Hibernate!

Я не сталкивался с проблемами и ловушками, о которых вы сообщали, использование instanceof отлично работает для меня!

Ответы [ 3 ]

13 голосов
/ 29 сентября 2011

Симптомы соответствуют ограничению гибернации.Вкратце, прокси с отложенной загрузкой для полиморфных сущностей по-разному реагируют на instanceof, чем сущности, которые они проксируют.Это связано с тем, что прокси создается в тот момент, когда фактический тип объекта еще не известен, и, будучи объектом Java, не может изменить свой класс времени выполнения после создания.

Hibernate будет возвращать прокси-серверы lazy-loading-прокси вместоматериализованная сущность, если

  1. Вы явно запрашиваете прокси с session.load()
  2. в качестве замены для сущности, на которую ссылается однозначная, лениво извлеченная ассоциация от другой загруженной сущности
  3. прокси был ранее создан в том же сеансе гибернации с использованием вышеуказанных средств

Случай 2 является наиболее распространенным.Я зашел так далеко, что написал модульный тест, который проверяет мое сопоставление на наличие полиморфных ленивых ненулевых однозначных ассоциаций, чтобы предупредить меня об этой возможности.

Существуют способы использования instanceof и приведения с прокси, но они не тривиальны.Для приведения прокси-интерфейсы должны быть объявлены для полиморфных сущностей, и весь код должен программироваться для этих интерфейсов, а не для классов сущностей.В этом случае Hibernate будет иметь прокси-сервер, реализующий все интерфейсы, которые может иметь объект, что позволяет выполнять все приведения.Для instanceof вы можете объявить:

class A {
    boolean isInstanceOf(Class<X extends A> clazz) {
        return clazz.isInstance(this);
    }
}

, а затем написать

if (entity.isInstanceOf(B.class)) {
    B b = (B) entity;
    // work with b
}

вместо

if (entity instanceof B) {
    ...
1 голос
/ 29 сентября 2011

Таковы проблемы с гибернацией при использовании полиморфизма.

Короче говоря, при тралении модели гибернации, где lazy - истина, hibernate не загружает реальный тип - загрузка реального типа потребует попадания в db, и это не будет ленивым. Если вы хотите, чтобы тип real был возвращен, вы должны настроить классы и установить для lazy «proxy». То, что это будет делать, - это не гидрировать прокси, пока вы не попросите его, только тогда БД будет запрошен для определения его реального типа - однако это поведение доступно только при использовании инструментария, когда инструментарий перехватывает вызов.

Отключите функцию «Ленивый», где бы вы ни увидели, и посмотрите, не изменится ли поведение - оно должно работать с отключенным режимом «Ленивый».

Это одна из ловушек использования спящего режима.

0 голосов
/ 21 февраля 2018

Я думаю, clazz.isInstance (this) работает, потому что это экземпляр целевого объекта.В этом разница с instanceof.Примите во внимание, что объект не проксируется в этом вызове.

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