Преобразование Hibernate proxy в реальный объект без перезагрузки

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Для преобразования прокси Hibernate в реальный объект используйте функцию Hibernate.unproxy():

Java
Скопировать код
import org.hibernate.Hibernate;

// ...

YourEntityClass proxyEntity = // Получение прокси-объекта

// Раскрытие реального объекта
YourEntityClass realEntity = (YourEntityClass) Hibernate.unproxy(proxyEntity);

Учитывайте, что эту операцию необходимо проводить в контексте открытой сессии Hibernate, иначе может произойти исключение LazyInitializationException.

Кинга Идем в IT: пошаговый план для смены профессии

Подробности преобразования прокси

Краткое решение, представленное выше, позволяет быстро добраться до сути. Теперь же давайте углубимся в детали и посмотрим, что происходит под капотом.

Ручная инициализация прокси

Метод Hibernate.unproxy() появился не сразу. До его появления мы использовали Hibernate.initialize(), который загружает все связанные данные сущности.

Java
Скопировать код
// Проверка, является ли объект прокси
if (proxyEntity != null && !Hibernate.isInitialized(proxyEntity)) {
    Hibernate.initialize(proxyEntity); // Инициализация объекта
}
YourEntityClass realEntity = (YourEntityClass) proxyEntity;

Работа с прокси-сущностями

Прокси-сущности на первый взгляд не отличаются от обычных, однако внутри они устроены иначе. Чтобы узнать истинный класс за прокси:

Java
Скопировать код
Class<?> realClass = Hibernate.getClass(proxyEntity); // Определение реального класса объекта

Применение EntityManager

Hibernate.unproxy() может быть недоступен из-за каких-то причин. В таких случаях можно воспользоваться EntityManager:

Java
Скопировать код
SessionImplementor sessionImplementor = entityManager.unwrap(SessionImplementor.class);
YourEntityClass realEntity = (YourEntityClass) sessionImplementor.getPersistenceContext().unproxy(proxyEntity);

Управление постоянными коллекциями

Постоянные коллекции типов PersistentBag и PersistentList требуют отдельного внимания. Преобразуйте их в LinkedHashSet для избавления от дубликатов и сохранения порядка элементов:

Java
Скопировать код
if (proxyEntity instanceof PersistentCollection) {
    PersistentCollection collection = (PersistentCollection) proxyEntity;
    return new LinkedHashSet<>(collection); // Управление коллекцией
}

Визуализация

Прокси Hibernate можно представить в виде флеш-накопителя, на котором есть ярлык, указывающий на файл.

Markdown
Скопировать код
Hibernate Proxy (💾): [Ярлык 📄]

"Распаковка" прокси сравнима со следованием по ярлыку к полноценному файлу:

Markdown
Скопировать код
🔄 Распаковка Прокси (💾➡️📄):
1. Вставляем накопитель (вызываем метод на прокси)
2. Следуем по ярлыку (инициируем объект)
3. Доступ к файлу открыт (получаем реальную сущность)

В результате помимо ярлыка у вас появляется полноценный документ.

Продвинутые методы преобразования прокси

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

Рекурсивное преобразование прокси

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

Java
Скопировать код
public Object unproxyDeeply(Object proxyEntity) {
    if (proxyEntity instanceof HibernateProxy) {
        // Рекурсивное раскрытие прокси
        return unproxyDeeply(((HibernateProxy) proxyEntity).getHibernateLazyInitializer().getImplementation());
    }
    if (proxyEntity instanceof PersistentCollection) {
        // Преобразование в обычную коллекцию данных
        return new LinkedHashSet<>(((PersistentCollection) proxyEntity));
    }
    return proxyEntity; // Объект без проксирования
}

Проверка на null для стабильности

Проверяйте объекты на null, чтобы избежать попыток инициализации несуществующих данных:

Java
Скопировать код
if (proxyEntity != null) {
    // Здесь может производиться раскрытие прокси
}

Преимущества использования кеша

"Вытаскивание" данных из кеша звучит приятно, однако лучше использовать unproxy(), чтобы данные оставались в кеше:

Java
Скопировать код
// Хороший тон: данные всегда под рукой, в кеше
YourEntityClass realEntity = (YourEntityClass) Hibernate.unproxy(proxyEntity);

Полезные материалы

  1. Hibernate (Hibernate Javadocs) — Документация по преобразованию Hibernate Proxy в реальный объект.
  2. Преобразование прокси Hibernate в реальный объект сущности – Stack Overflow — Обсуждение практик раскрытия прокси.
  3. Антипаттерн "открытая сессия" – Vlad Mihalcea — Размышления о вредоносных паттернах открытой сессии и их влиянии на прокси.
  4. Руководство пользователя Hibernate ORM — Подробный анализ прокси в Hibernate.
  5. Учебник | DigitalOcean — Особенности жадной и ленивой загрузки в Hibernate.
  6. Подробное руководство по реализации equals() и hashCode() в Hibernate — Об отсоединении и присоединении сущностей при работе с прокси.