Итак, я сейчас работаю над 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, что если клиент запрашивает данные сотрудника по первому запросу, а затем позже делает еще один запрос данных о компании этого предыдущего сотрудника, передавая идентификатор компании в теле запроса?Это обычный случай использования.
Не работает.
- Сеансы без сохранения состояния?
По-прежнему создает прокси-объекты, отключает только кэши первого и второго уровня для текущего сеанса.
- Зачем?Вы используете 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());