Как решить исключение" не удалось лениво инициализировать коллекцию ролей " Hibernate
у меня такая проблема:
орг.зимовать.LazyInitializationException: не лениво инициализировать коллекция роль: mvc3.модель.Тема.комментарии, ни одна сессия или сессия не была закрыта
вот модели:
@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>
исключение rised, при просмотре jsp. В соответствии с c: forEach цикл
26 ответов:
если вы знаете, что вы хотите видеть все
Comments каждый раз, когда вы получаетеTopicзатем измените отображение поля наcommentsto:@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL) private Collection<Comment> comments = new LinkedHashSet<Comment>();коллекции лениво загружаются по умолчанию, взгляните на этой если вы хотите знать больше.
по моему опыту, у меня есть следующие методы для решения знаменитого LazyInitializationException:
(1) Используйте Режим Гибернации.инициализировать
Hibernate.initialize(topics.getComments());(2) Используйте JOIN FETCH
вы можете использовать синтаксис JOIN FETCH в своем JPQL для явного извлечения дочерней коллекции. Это как-то похоже на нетерпеливое извлечение.
(3) Используйте OpenSessionInViewFilter
LazyInitializationException часто происходят в виде слоя. Если вы используете Spring framework, вы можете использовать OpenSessionInViewFilter. Однако я не предлагаю вам этого делать. Это может привести к проблеме производительности, если не использовать правильно.
происхождение вашей проблемы:
по умолчанию hibernate лениво загружает коллекции (отношения), что означает, когда вы используете
collectionв коде(здесь
Я знаю, это старый вопрос, но я хочу помочь. Вы можете поместить транзакционную аннотацию на нужный вам метод обслуживания, в этом случае findTopicByID (id) должен иметь
@Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)более подробную информацию об этой аннотации можно найти здесь
о других решениях:
fetch = FetchType.EAGERне является хорошей практикой, она должна использоваться только в случае необходимости.
Hibernate.initialize(topics.getComments());гибернации инициализатора связывает свои занятия в спящий режим технология. Если вы стремитесь быть гибким-это не лучший способ пойти.
надеюсь, что это помогает
причина в том, что при использовании отложенной загрузки сеанс закрывается.
есть два решения.
не использовать отложенную загрузку.
Set
lazy=falseв XML или Set@OneToMany(fetch = FetchType.EAGER)в аннотации.использовать отложенную загрузку.
Set
lazy=trueв XML или Set@OneToMany(fetch = FetchType.LAZY)В аннотации.и добавить
OpenSessionInViewFilter filterв своемweb.xmlподробно смотрите мой POST.
для ленивой загрузки коллекции должен быть активный сеанс. В веб-приложении есть два способа сделать это. Вы можете использовать Открыть Сеанс В Вид шаблон, где вы используете перехватчик открыть сеанс в начале запроса и закрыть его в конце. Существует риск, что вы должны иметь надежную обработку исключений, или вы можете связать все свои сеансы, и ваше приложение может зависнуть.
другой способ справиться с этим - соберите все необходимые данные в контроллере, закройте сеанс, а затем поместите данные в свою модель. Я лично предпочитаю этот подход, так как он кажется немного ближе к духу шаблона MVC. Кроме того, если вы получаете сообщение об ошибке из базы данных таким образом, вы можете справиться с этим намного лучше, чем если бы это произошло в вашем рендере представления. Ваш друг в этом сценарии спящий режим.инициализировать(раздел mytopic.getComments()). Вам также придется повторно присоединить объект к сеансу, так как вы создаете новую транзакцию с каждым запросом. Используйте сеанс.lock (myTopic,LockMode.Нет) для этого.
проблема вызвана доступом к атрибуту с закрытым сеансом hibernate. У вас нет транзакции гибернации в контроллере.
возможные решения:
сделайте всю эту логику, в сервисном слое, (С @Transactional), не в контроллере. Должно быть правильное место для этого, это часть логики приложения, а не в контроллере (в данном случае интерфейс для загрузки модели). Все операции в сервисном слое должен быть транзакционный. т. е.: переместите эту строку в TopicService.метод findTopicByID:
коллекция commentList = topicById.getComments();
используйте "нетерпеливый" вместо "ленивый". Теперь вы не используете "ленивый".. это не реальное решение, если вы хотите использовать ленивый, работает как временное (очень временное) решение.
- используйте @Transactional в контроллере. Его не следует использовать здесь вы смешиваете сервисный слой с презентацией, это не очень хороший дизайн.
- использовать OpenSessionInViewFilter, сообщается о многих недостатках, возможной нестабильности.
в общем, лучшим решением является 1.
@Controller @RequestMapping(value = "/topic") @TransactionalЯ решаю эту проблему, добавляя
@Transactional, Я думаю, что это может сделать сессию открытой
Если вы пытаетесь установить связь между сущностью и коллекцией или списком объектов java (например, Long type), он хотел бы что-то вроде этого:
@ElementCollection(fetch = FetchType.EAGER) public List<Long> ids;
я узнал, что объявление
@PersistenceContextкакEXTENDEDтакже решает эту проблему:@PersistenceContext(type = PersistenceContextType.EXTENDED)
@ транзакционная аннотация на контроллере отсутствует
@Controller @RequestMapping("/") @Transactional public class UserController { }
Это была проблема, с которой я недавно столкнулся, которую я решил с помощью
<f:attribute name="collectionType" value="java.util.ArrayList" />более подробное описание здесь и это спасло мой день.
ваш список лениво загружается, поэтому список не был загружен. звоните, чтобы попасть в список недостаточно. используйте в спящем режиме.инициализации для инициализации списка. Если и работать на элемент списка и звоните гибернации.инициализировать для каждого . это должно быть до того, как вы вернетесь из области транзакции. посмотри на этой пост.
ищите -Node n = // .. get the node Hibernate.initialize(n); // initializes 'parent' similar to getParent. Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session
чтобы решить проблему в моем случае, просто не хватало этой строки
<tx:annotation-driven transaction-manager="myTxManager" />в файле контекста приложения.
The
@Transactionalаннотация на способ не был принят во внимание.надеюсь, что ответ поможет кому-то
для тех, кто работает с критерии, я обнаружил, что
criteria.setFetchMode("lazily_fetched_member", FetchMode.EAGER);сделал все, что мне нужно было сделать.
начальный режим выборки для коллекций имеет значение FetchMode.Ленивый, чтобы обеспечить производительность, но когда мне нужны данные, я просто добавляю эту строку и наслаждаюсь полностью заполненными объектами.
в моем случае следующий код, проблема:
entityManager.detach(topicById); topicById.getComments() // exception thrownпотому что он отсоединен от базы данных и Hibernate больше не получает список из поля, когда это было необходимо. Поэтому я инициализирую его перед отсоединением:
Hibernate.initialize(topicById.getComments()); entityManager.detach(topicById); topicById.getComments() // works like a charm
как я объяснил в в этой статье лучший способ обработки
LazyInitializationExceptionдолжен получить его во время запроса, например:select t from Topic t left join fetch t.commentsвы всегда должны избегать следующих анти-паттернов:
- заставляя ассоциации рваться
- С помощью OSIV (Open Session in View)
- включение
hibernate.enable_lazy_load_no_transспящий режим конфигурации собственностьпоэтому убедитесь, что это ваш
FetchType.LAZYассоциации инициализируются во время запроса или в исходном@Transactionalобъем, используяHibernate.initializeдля вторичной коллекций.
С помощью гибернации
@Transactionalаннотация, если вы получаете объект из базы данных с ленивыми извлеченными атрибутами, вы можете просто получить их, извлекая эти атрибуты следующим образом:@Transactional public void checkTicketSalePresence(UUID ticketUuid, UUID saleUuid) { Optional<Ticket> savedTicketOpt = ticketRepository.findById(ticketUuid); savedTicketOpt.ifPresent(ticket -> { Optional<Sale> saleOpt = ticket.getSales().stream().filter(sale -> sale.getUuid() == saleUuid).findFirst(); assertThat(saleOpt).isPresent(); }); }здесь, в Hibernate proxy-управляемой транзакции, факт вызова
ticket.getSales()сделайте еще один запрос, чтобы получить продажи, потому что вы явно попросили его.
причина в том, что вы пытаетесь получить список комментариев на своем контроллере после закрытия сеанса внутри службы.
topicById.getComments();выше будет загружать список комментариев только в том случае, если ваш сеанс hibernate активен, который, я думаю, вы закрыли в своем сервисе.
Итак, вы должны получить список комментариев перед закрытием сессии.
в моем cae у меня было отображение b / w A и B как
а
@OneToMany(mappedBy = "a", cascade = CascadeType.ALL) Set<B> bs;в слое DAO,метод должен быть аннотирован
@TransactionalЕсли вы не аннотировали отображение с помощью Fetch Type-Eager
коллекция
commentsв вашем классе моделиTopicлениво загружается, что является поведением по умолчанию, если вы не аннотируете его с помощьюfetch = FetchType.EAGERспециально.скорее всего, что ваш
findTopicByIDслужба использует сеанс гибернации без состояния. сеанс без сохранения состояния не имеет кэша первого уровня, т. е. без контекста персистентности. Позже, когда вы пытаетесь повторитьcomments, Hibernate выдаст исключение.org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: mvc3.model.Topic.comments, no session or session was closedрешение может быть:
аннотации
commentsСfetch = FetchType.EAGER@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL) private Collection<Comment> comments = new LinkedHashSet<Comment>();если вы все еще хотите, чтобы комментарии были лениво загружены, используйте сеансы гибернации с сохранением состояния, так что вы сможете получить комментарии позже по требованию.
Привет всем проводка довольно поздно, надеюсь, что это помогает другим, Заранее благодарим @GMK за этот пост спящий режим.инициализировать(объект)
когда Lazy= "true"
Set<myObject> set=null; hibernateSession.open set=hibernateSession.getMyObjects(); hibernateSession.close();теперь, если я получаю доступ к " set " после закрытия сессии он выдает исключение.
мое решение :
Set<myObject> set=new HashSet<myObject>(); hibernateSession.open set.addAll(hibernateSession.getMyObjects()); hibernateSession.close();теперь я могу получить доступ к " set " даже после закрытия сеанса гибернации.
одним из лучших решений является добавление в приложение следующего.файл свойств: весна.СПД.свойства.зимовать.enable_lazy_load_no_trans=true
еще один способ, чтобы сделать это, вы можете использовать TransactionTemplate чтобы обернуть вокруг ленивой выборки. Как
Collection<Comment> commentList = this.transactionTemplate.execute (status -> topicById.getComments());
добавьте это к вашей настойчивости.xml
<property name="hibernate.enable_lazy_load_no_trans" value="true" />
Я решил использовать List вместо Set:
private List<Categories> children = new ArrayList<Categories>();
Comments