Как отключить или запретить использование прокси-серверов Hibernate после закрытия сессии? - PullRequest
0 голосов
/ 28 мая 2019

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

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

У меня есть класс Employee, сопоставленный как объект Hibernateэто выглядит так

@Entity
public class Employee {
    @Id
    Integer id;
    String name;
    Double salary;
    String department;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "company_id")
    Company company;

//    Getters/Setters ommitted


    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                ", department='" + department + '\'' +
                ", company=" + company +
                '}';
    }
}

И еще один класс сущностей Company выглядит так:

@Entity
public class Company {

    @Id
    Integer id;
    String name;
    String address;
    @OneToMany(mappedBy = "company", fetch = FetchType.LAZY)
    List<Employee> employees;

//    Getters and setters ommited


    @Override
    public String toString() {
        return "Company{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", employees=" + employees +
                '}';
    }
}

База данных выглядит примерно так:

Employee:
| id | name     | salary | department | company_id |
|----|----------|--------|------------|------------|
| 1  | john doe | 3000   | Finance    | 1          |
| 2  | mary sue | 3300   | HR         | 1          |

Company:
| id | name | address    |
|----|------|------------|
| 1  | ACME | St. Sesame |

Теперь предположим, что я хочу получить данные от определенного сотрудника, мне нужны только данные сотрудника + идентификатор компании (и только идентификатор) в качестве справки, если мне понадобятся данные компании позже.Это выглядело бы примерно так:

Session session = SessionManager.openSession();

Employee emp = session.get(Employee.class, 1);

session.close();

Теперь я хочу показать данные сотрудника

System.out.println(emp);

Вывод, который ожидается (или, по крайней мере, я ожидал), вот что-токак это:

Employee{id=1,name='john doe',salary=3000.0,department='Finance',company=Company{id=1,name=null,address=null,employees=null}}

Это достаточно просто, проблема в том, что он выдаст исключение LazyInitializationException, потому что Hibernate пытался использовать прокси-объект для получения данных компании, когда это требовалось при вызове toString()Сотрудник, но сессия уже закрыта.

Дело в том, что если я закрыл сессию без извлечения дополнительных данных, то ЭТО, больше никаких данных не требуется, HIBERNATE!Если я открою сессию, я получу все данные, которые мне нужны, и ТОЛЬКО данные, которые мне нужны , и после того, как я закрою его, это означает, что мне больше не нужны данные, спасибовы спите , но прокси-объекты Hibernate остаются активными даже после закрытия сессии.

Я искал по всей сети, даже здесь, в StackOverflow, и есть некоторые так называемые "решения" или "обходные пути ", например:

  • Почему вы не используете HQL с конструкторами?

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

  • Вы можете использовать HQL с конструкторами и создавать сущности для каждой таблицы базы данных, к которой вам нужно получить доступ по запросу

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

  • Затем используйте собственный запрос, и вы можете поставить NULL as columnName, когда вам не нужен этот столбец.data

Я делаю это тоже в некоторых случаях, проблема в том, что этот столбец является столбцом соединения в отношении сущности, Hibernate устанавливает прокси-объект, и я не хочупрокси-объект, только данные столбца соединения, если, И ТОЛЬКО ЕСЛИ , эти данные необходимы для другой части кода, где я могу открыть новый сеанс и получить необходимые данные.

  • Почему бы вам просто не оставить сеанс открытым, используя этот открытый сеанс в представлении * шаблон 1066 *?

Нет, не могу,это API RESTful, что если клиент запрашивает данные сотрудника по первому запросу, а затем позже делает еще один запрос данных о компании этого предыдущего сотрудника, передавая идентификатор компании в теле запроса?Это обычный случай использования.

  • session.detach ()?

Не работает.

  • Сеансы без сохранения состояния?

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

  • Зачем?Вы используете DTO?

И создаете новый класс для каждого варианта использования запроса частичных данных моего API?Хахаха!

Видите ли, единственная причина, по которой я использую решение ORM, такое как Hibernate, заключается в том, что я боюсь, что ручная обработка ResultSets делает DEATH . Забудьте о HQL, Criteria API и т. Д. Я не возражаю создавать собственные запросы из строк, если у меня есть только Hibernate для сопоставления ResultSet с объектом (или списком объектов) для меня. И я хочу, чтобы Hibernate знал, что если существует отношение сущностей, которое я не получил до закрытия сеанса, это означает, что Я НЕ хочу получать его , поэтому прокси-объектов нет.

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

EDIT

Итак, некоторые из вас пометили мой вопрос как дубликат Преобразование прокси Hibernate в объект реальной сущности , но есть различия:

  • Этот вопрос вращается вокруг того, как «снять с прокси» объект, например, как инициировать инициализацию прокси Hibernate.

  • У меня вопрос о том, как заставить Hibernate NOT использовать прокси для невыгруженного объекта отношения, и вместо этого просто использовать экземпляр класса отношения только с его идентификатором.

В моем предыдущем примере, вместо использования прокси-сервера для отложенной выборки объекта Company, связанного с Employee, позже, когда это необходимо, я бы хотел, чтобы hibernate просто создал new Company(company_id) и оставил его вот так, НЕТ ПРОКСИ .

РЕДАКТИРОВАТЬ 2

Я только что узнал, что проект Джексона (который я использую для чтения / записи данных как JSON в моем API) имеет модуль, который игнорирует прокси Hibernate при сериализации объектов.

Зависимость Maven:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>${jackson-version}</version>
</dependency>

Тогда просто зарегистрируйте модуль на вас Object Mapper экземпляр:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate5Module());

1 Ответ

0 голосов
/ 28 мая 2019

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

jackson-datatype-hibernate5 изначально игнорирует неоткрытые прокси, устанавливая их как nullпри сериализации объектов, но класс Hibernate5Module (или любую версию модуля Hibernate, которую вы хотите использовать) имеет действительно удобную конфигурацию, которая позволяет Джексону сериализовать только идентификатор для невыполненных прокси, что я и искал.

// Configuring `ObjectMapper` and module:
ObjectMapper mapper = new ObjectMapper();
Hibernate5Module module = new Hibernate5Module()
    .configure(Hibernate5Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);

mapper.registerModule(module);

Таким образом, вы можете использовать ObjectMapper для создания ObjectReaders для ваших классов, которые НЕ будут пытаться инициализировать прокси-объекты при сериализации, а только сериализуют ID объекта (если это отношение OneToOne или ManyToOne,конечно).

И я сказал, что это "своего рода" решение, потому что Hibernate по-прежнему использует прокси-объекты для незатронутых отношений (когда я хотел только значение ID прокси-объекта для последующего использования), единственное, чточто я настроил Джексона для сохранения значения идентификатора и игнорирования прокси-объекта.

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