Этот вопрос задавался много раз, но я все еще не могу найти решение для моего варианта использования:
У меня есть приложение Struts2, использующее Hibernate 5.x.
Приложение представляет собой приложение «Контакты». Он состоит из двух объектов: «Контакт», который может иметь ноль или более «Заметок».
Вот как я получаю контакты:
@Override
public List<Contact> getContacts() {
//Note: Hibernate 5++ supports Java try-with-resource blocks
try (Session session = HibernateUtil.openSession()) {
List<Contact> contacts = session.createQuery("FROM Contact").list();
return contacts;
...
Отлично работает. Пока я не попробую что-то вроде этого:
ObjectMapper mapper = new ObjectMapper();
jsonString = mapper.writeValueAsString(contacts);
ОШИБКА:
16:05:16.174 [http-nio-8080-exec-2] DEBUG org.apache.struts2.dispatcher.Dispatcher - Dispatcher serviceAction failed
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.example.contactsapp.models.Contact.notes, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.example.contactsapp.models.Contact["notes"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394) ~[jackson-databind-2.10.0.jar:2.10.0]
...
at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:4094) ~[jackson-databind-2.10.0.jar:2.10.0]
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3404) ~[jackson-databind-2.10.0.jar:2.10.0]
at com.example.contactsapp.actions.ContactsAction.getContacts(ContactsAction.java:36) ~[classes/:?]
...
ВОЗМОЖНЫЕ РЕШЕНИЯ
Я НЕ хочу изменить на FetchType.Eager
.
Поскольку я не использую Spring, я не могу используйте @Transactional
или OpenSessionInView
. Но я бы с удовольствием, если бы были эквиваленты только для Hibernate.
Это то, что я пробовал (в основном на Как решить «не удалось лениво инициализировать» коллекция ролей »Hibernate исключение ):
@Override
public List<Contact> getContactsFetchAll() {
//Note: Hibernate 5++ supports Java try-with-resource blocks
try (Session session = HibernateUtil.openSession()) {
// Jackson mapper.writeValueAsString() => "failed to lazily initialize a collection"
// List<Contact> contacts = session.createQuery("FROM Contact").list();
// Plan A: Causes same "failed to lazily initialize a collection" runtime error
// List<Contact> contacts = session.createQuery("FROM Contact").list();
// Hibernate.initialize(contacts);
// Plan B: Still no-go: returns [] empty set
// List<Contact> contacts = session.createQuery("SELECT c FROM Contact c JOIN FETCH c.notes n").list();
// Plan C: Same: "failed to lazily initialize a collection of role..."
// Query query = session.createQuery("FROM Contact");
// Hibernate.initialize(query);
// List<Contact> contacts = query.list();
// Plan D: Same: "ERROR: failed to lazily initialize a collection of role"
// List<Contact> contacts = session.createQuery("FROM Contact").list();
// for (Contact c : contacts) {
// Set<Note> n = c.getNotes();
// }
return contacts;
}
}
Q: Есть предложения?
Q: Вам нужна дополнительная информация?
Я провел немного больше исследований.
По-видимому, в этом конкретном сценарии Hibernate вообще не выполняет "соединение".
Вместо этого:
- Делает "выбор", чтобы получить родителя.
- Если вы попытаетесь прочитать что-либо из какого-либо из детей, тогда он ДРУГОЙ"выберет", чтобы получить детей. Один «выбор» для родителя, второй для детей.
- Если объект настроен на «активную выборку», он всегда все выбирает заранее.
- Для "отложенной выборки" вы ДОЛЖНЫ пытаться прочитать дочерние элементы (таким образом, вызывая второй выбор) перед закрытием сеанса. В противном случае, если вы попытаетесь прочитать дочерние данные после закрытия сеанса, вы получите «не удалось лениво инициализировать коллекцию».
- В этом сценарии левое соединение не работает: набор результатов, возвращаемый базой данных просто не соответствует сущности. Чтобы это работало, мне нужно было прочитать строку набора результатов за раз и построить объект вручную.
- Я все еще ищу способ заставить "Join Fetch" работать в моем сценарий ...