У меня есть эта проблема:
org.hibernate.LazyInitializationException: не удалось лениво инициализировать коллекцию ролей: mvc3.model.Topic.comments, ни один сеанс или сеанс не был закрыт
Вот модель:
@Entity
@Table(name = "T_TOPIC")
public class Topic {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
@ManyToOne
@JoinColumn(name="USER_ID")
private User author;
@Enumerated(EnumType.STRING)
private Tag topicTag;
private String name;
private String text;
@OneToMany(mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
...
public Collection<Comment> getComments() {
return comments;
}
}
Контроллер, который вызывает модель, выглядит следующим образом:
@Controller
@RequestMapping(value = "/topic")
public class TopicController {
@Autowired
private TopicService service;
private static final Logger logger = LoggerFactory.getLogger(TopicController.class);
@RequestMapping(value = "/details/{topicId}", method = RequestMethod.GET)
public ModelAndView details(@PathVariable(value="topicId") int id)
{
Topic topicById = service.findTopicByID(id);
Collection<Comment> commentList = topicById.getComments();
Hashtable modelData = new Hashtable();
modelData.put("topic", topicById);
modelData.put("commentList", commentList);
return new ModelAndView("/topic/details", modelData);
}
}
Страница jsp выглядит следующим образом:
<%@page import="com.epam.mvc3.helpers.Utils"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>View Topic</title>
</head>
<body>
<ul>
<c:forEach items="${commentList}" var="item">
<jsp:useBean id="item" type="mvc3.model.Comment"/>
<li>${item.getText()}</li>
</c:forEach>
</ul>
</body>
</html>
Исключение повышается при просмотре jsp. В строке с циклом c: forEach
Исходя из моего опыта, у меня есть следующие методы для решения известной исключительной ситуации LazyInitializationException:
(1) Используйте Hibernate.initialize
(2) Используйте JOIN FETCH
Вы можете использовать синтаксис JOIN FETCH в вашем JPQL для явного извлечения дочерней коллекции. Это как EAGER выборка.
(3) Используйте OpenSessionInViewFilter
LazyInitializationException часто возникают в слое представления. Если вы используете Spring Framework, вы можете использовать OpenSessionInViewFilter. Тем не менее, я не предлагаю вам сделать это. Это может привести к проблемам с производительностью, если не использовать правильно.
источник
Я знаю, что это старый вопрос, но я хочу помочь. Вы можете поместить транзакционную аннотацию на нужный вам сервисный метод, в этом случае findTopicByID (id) должен иметь
больше информации об этой аннотации можно найти здесь
О других решениях:
не является хорошей практикой, его следует использовать ТОЛЬКО в случае необходимости.
Инициализатор hibernate связывает ваши классы с технологией hibernate. Если вы стремитесь быть гибкими, это не лучший способ.
Надеюсь, поможет
источник
@Transactional
весна не только вещь?Происхождение вашей проблемы:
По умолчанию hibernate лениво загружает коллекции (отношения), что означает, что всякий раз, когда вы используете
collection
в своем коде (здесьcomments
поле вTopic
классе), hibernate получает это из базы данных, теперь проблема в том, что вы получаете коллекцию в своем контроллере (где сеанс JPA закрыт). Это строка кода, которая вызывает исключение (где вы загружаетеcomments
коллекцию):Вы получаете коллекцию "comments" (topic.getComments ()) в вашем контроллере (где
JPA session
это закончилось), и это вызывает исключение. Также, если у вас естьcomments
коллекция в вашем JSP-файле, как это (вместо того, чтобы получить ее в вашем контроллере):У вас все равно будет то же исключение по той же причине.
Решение проблемы:
Поскольку у вас может быть только две коллекции с
FetchType.Eager
(с нетерпением выбранной коллекцией) в классе Entity, и поскольку ленивая загрузка более эффективна, чем с нетерпением загрузка, я думаю, что этот способ решения вашей проблемы лучше, чем просто изменение наFetchType
eager:Если вы хотите инициализировать коллекцию Lazy, а также сделать эту работу, лучше добавить этот фрагмент кода в ваш
web.xml
:Этот код делает то, что он увеличивает длину вашего
JPA session
или, как сказано в документации, он используется"to allow for lazy loading in web views despite the original transactions already being completed."
так, чтобы сессия JPA была открыта немного дольше, и из-за этого вы можете лениво загружать коллекции в ваши файлы jsp и классы контроллеров. ,источник
Причина в том, что когда вы используете ленивую загрузку, сессия закрывается.
Есть два решения.
Не используйте ленивый груз.
Установить
lazy=false
в XML или Установить@OneToMany(fetch = FetchType.EAGER)
в аннотации.Используйте ленивый груз.
Установить
lazy=true
в XML или Установить@OneToMany(fetch = FetchType.LAZY)
в аннотации.и добавить
OpenSessionInViewFilter filter
в свойweb.xml
Подробнее см. Мой пост .
источник
я решаю эту проблему, добавляя
@Transactional
, я думаю, что это может сделать сессию открытойисточник
Проблема вызвана доступом к атрибуту с закрытым сеансом гибернации. У вас нет спящего режима транзакции в контроллере.
Возможные решения:
Выполните всю эту логику на уровне обслуживания (с @Transactional), а не в контроллере. Должно быть правильное место для этого, это часть логики приложения, а не контроллера (в данном случае интерфейс для загрузки модели). Все операции на уровне сервиса должны быть транзакционными. т.е.: переместите эту строку в метод TopicService.findTopicByID:
Коллекция commentList = topicById.getComments ();
Используйте «нетерпеливый» вместо «ленивый» . Теперь вы не используете 'ленивый' .. это не реальное решение, если вы хотите использовать ленивый, работает как временный (очень временный) обходной путь.
В общем, лучшим решением является 1.
источник
Для отложенной загрузки коллекции должен быть активный сеанс. В веб-приложении есть два способа сделать это. Вы можете использовать шаблон Open Session In View , где вы используете перехватчик, чтобы открыть сеанс в начале запроса и закрыть его в конце. Существует риск того, что вам потребуется тщательная обработка исключений, иначе вы можете связать все свои сеансы, и ваше приложение может зависнуть.
Другой способ справиться с этим - собрать все данные, которые вам нужны в вашем контроллере, закрыть сеанс, а затем вставить данные в вашу модель. Я лично предпочитаю такой подход, так как он кажется немного ближе к духу паттерна MVC. Также, если вы получаете ошибку из базы данных таким образом, вы можете справиться с ней намного лучше, чем если бы это происходило в вашем средстве визуализации. Ваш друг в этом сценарии - Hibernate.initialize (myTopic.getComments ()). Вам также придется заново присоединить объект к сеансу, поскольку вы создаете новую транзакцию с каждым запросом. Для этого используйте session.lock (myTopic, LockMode.NONE).
источник
Как я объяснил в этой статье , лучший способ справиться с ним
LazyInitializationException
- получить его по запросу, например:Вы должны ВСЕГДА избегать следующих анти-паттернов:
hibernate.enable_lazy_load_no_trans
свойства конфигурации HibernateПоэтому убедитесь, что ваши
FetchType.LAZY
ассоциации инициализируются во время запроса или в исходной@Transactional
области, используемойHibernate.initialize
для вторичных коллекций.источник
TrassctionInterceptor
в трассировке стека, и это один.Если вы пытаетесь установить связь между сущностью и коллекцией или списком Java-объектов (например, типа Long), она бы хотела что-то вроде этого:
источник
Одним из лучших решений является добавление следующего в файл application.properties: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true
источник
Я обнаружил, что объявление
@PersistenceContext
asEXTENDED
также решает эту проблему:источник
это была проблема, с которой я недавно столкнулся, которую я решил с помощью
Более подробное описание здесь и это спасло мой день.
источник
Ваш список загружается медленно, поэтому список не был загружен. звонка для попадания в список недостаточно. используйте в Hibernate.initialize для инициации списка. Если работа не выполняется, запустите элемент списка и вызовите Hibernate.initialize для каждого элемента. это должно быть до того, как вы вернетесь из области транзакции. посмотрите на этот пост
ищи -
источник
Для решения проблемы в моем случае просто не хватало этой строки
в файле контекста приложения.
@Transactional
Аннотация по методе не была принята во внимание.Надеюсь, что ответ поможет кому-то
источник
@ Транзакционная аннотация на контроллере отсутствует
источник
Используя
@Transactional
аннотацию hibernate , если вы получаете объект из базы данных с лениво извлеченными атрибутами, вы можете просто получить их, извлекая эти атрибуты следующим образом:Здесь, в транзакции, управляемой прокси-сервером Hibernate, факт вызова вызывает
ticket.getSales()
другой запрос для получения продаж, потому что вы явно задали его.источник
Две вещи, которые вы должны иметь
fetch = FetchType.LAZY
.а также
источник
Для тех, кто работает с критериями , я обнаружил, что
сделал все, что мне было нужно.
Режим начальной выборки для коллекций установлен на FetchMode.LAZY, чтобы обеспечить производительность, но когда мне нужны данные, я просто добавляю эту строку и наслаждаюсь полностью заполненными объектами.
источник
В моем случае следующий код был проблемой:
Потому что он отсоединился от базы данных, и Hibernate больше не извлекал список из поля, когда это было необходимо. Поэтому я инициализирую его перед отсоединением:
источник
Причина в том, что вы пытаетесь получить commentList на своем контроллере после закрытия сеанса внутри службы.
Выше будет загружать commentList, только если ваш сеанс гибернации активен, который, я думаю, вы закрыли в своем сервисе.
Таким образом, вы должны получить commentList перед закрытием сессии.
источник
Answer
Коллекция
comments
в вашем классе моделиTopic
загружается лениво, что является поведением по умолчанию, если вы не аннотируете ееfetch = FetchType.EAGER
специально.Скорее всего, ваш
findTopicByID
сервис использует сеанс Hibernate без сохранения состояния. Сеанс без сохранения состояния не имеет кеша первого уровня, т. Е. Отсутствует постоянный контекст. Позже, когда вы попытаетесь выполнить итерациюcomments
, Hibernate выдаст исключение.Решение может быть:
Аннотировать
comments
сfetch = FetchType.EAGER
Если вы все еще хотите, чтобы комментарии загружались лениво, используйте сеансы с сохранением состояния в Hibernate , чтобы вы могли получать комментарии позже по требованию.
источник
В моем случае у меня было отображение ч / б
A
иB
вродеA
имеетв
DAO
слое, метод должен быть аннотирован,@Transactional
если вы не аннотировали отображение с помощью Fetch Type - Eagerисточник
Не лучшее решение, но для тех, кто
LazyInitializationException
особенно сталкивается, вSerialization
этом поможет. Здесь вы проверите лениво инициализированные свойства и их настройкиnull
. Для этого создайте класс нижеВнутри вашего класса Entity, который имеет лениво инициализированные свойства, добавьте метод, как показано ниже. Добавьте все ваши свойства ленивой загрузки внутри этого метода.
Вызовите этот
checkLazyIntialzation()
метод после на всех местах, где вы загружаете данные.источник
Привет Всем, довольно поздняя публикация, надеюсь, что это поможет другим, Заранее благодарю @GMK за этот пост Hibernate.initialize (object)
когда ленивый = "правда"
Теперь, если я получаю доступ к 'set' после закрытия сессии, он генерирует исключение.
Мое решение:
Теперь я могу получить доступ к «set» даже после закрытия Hibernate Session.
источник
Еще один способ сделать это, вы можете использовать TransactionTemplate, чтобы обернуть ленивую выборку. подобно
источник
Проблема возникает из-за того, что код обращается к отложенному отношению JPA, когда «соединение» с базой данных закрыто ( постоянный контекст - это правильное имя в терминах Hibernate / JPA).
Простым способом решения этой проблемы в Spring Boot является определение уровня обслуживания и использование
@Transactional
аннотации. Эта аннотация в методе создает транзакцию, которая распространяется на уровень хранилища и сохраняет открытый контекст постоянства до завершения метода. Если вы обращаетесь к коллекции внутри транзакционного метода, Hibernate / JPA будет извлекать данные из базы данных.В вашем случае вам просто нужно аннотировать с
@Transactional
помощью методаfindTopicByID(id)
в вашемTopicService
и вызвать выборку коллекции в этом методе (например, задавая его размер):источник
Чтобы избавиться от исключения отложенной инициализации, не следует вызывать отложенный сбор при работе с отсоединенным объектом.
На мой взгляд, лучший подход - использовать DTO, а не сущность. В этом случае вы можете явно указать поля, которые хотите использовать. Как обычно, этого достаточно. Не нужно беспокоиться, что-то вроде Джексона
ObjectMapper
илиhashCode
сгенерированное Lombok вызовет ваши методы неявно.Для некоторых конкретных случаев вы можете использовать
@EntityGrpaph
аннотации, которые позволяютeager
загружать данные даже в том случае, если они естьfetchType=lazy
в вашей сущности.источник
Есть многократное решение для этой проблемы Ленивой Инициализации -
1) Измените тип извлечения ассоциации с LAZY на EAGER, но это не очень хорошая практика, потому что это ухудшит производительность.
2) Используйте FetchType.LAZY для связанного объекта, а также используйте аннотацию Transactional в вашем методе уровня службы, чтобы сеанс оставался открытым, а когда вы будете вызывать topicById.getComments (), будет загружен дочерний объект (комментарии).
3) Также попробуйте использовать объект DTO вместо сущности на уровне контроллера. В вашем случае сессия закрыта на уровне контроллера. ТАК лучше конвертировать сущность в DTO на уровне сервиса.
источник
я решил использовать список вместо Set:
источник