Как решить" не удалось лениво инициализировать коллекцию роли " Hibernate исключение

у меня такая проблема:

org.зимовать.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 затем измените отображение поля на comments в:

@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();

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


из моего опыта у меня есть следующие методы для решения знаменитого LazyInitializationException:

(1) Используйте Hibernate.инициализировать

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());

инициализатор hibernate связывает ваши классы с hibernate технология. Если вы стремитесь быть гибким-это не лучший способ пойти.

надеюсь, что это помогает


причина в том, что при использовании lazy load сеанс закрывается.

есть два решения.

  1. не использовать отложенную загрузку.

    Set lazy=false в XML или Set @OneToMany(fetch = FetchType.EAGER) в аннотации.

  2. использовать отложенную загрузку.

    Set lazy=true в XML или Set @OneToMany(fetch = FetchType.LAZY) В аннотации.

    и добавить OpenSessionInViewFilter filter в своем web.xml

подробно см. Мой в должности.


для ленивой загрузки коллекции должен быть активный сеанс. В веб-приложении есть два способа сделать это. Вы можете использовать Открыть Сеанс В View шаблон, где вы используете перехватчик открыть сеанс в начале запроса и закрыть его в конце. Риск заключается в том, что вы должны иметь надежную обработку исключений, или вы можете связать все свои сеансы, и ваше приложение может зависнуть.

другой способ справиться с этим - соберите все необходимые данные в контроллере, закройте сеанс, а затем вставьте данные в модель. Я лично предпочитаю этот подход, поскольку он кажется немного ближе к духу шаблона MVC. Кроме того, если вы получаете ошибку из базы данных таким образом, вы можете справиться с ней намного лучше, чем если это происходит в вашем представлении. Ваш друг в этом сценарии спящий режим.инициализировать(раздел mytopic.getComments()). Вам также придется повторно присоединить объект к сеансу, так как вы создаете новую транзакцию с каждым запросом. Используйте сеанс.lock (myTopic, LockMode.Нет) за это.


проблема вызвана доступом к атрибуту с закрытым сеансом гибернации. У вас нет транзакции hibernate в контроллере.

возможные решения:

  1. сделайте всю эту логику, на уровне сервиса, (С @Transactional), не в контроллере. Должно быть правильное место для этого, это часть логики приложения, а не в контроллере (в данном случае интерфейс для загрузки модели). Все операции на уровне сервиса должна быть транзакционная. т. е.: переместите эту строку в TopicService.метод findTopicByID:

    коллекция commentList = topicById.getComments();

  2. используйте "нетерпеливый" вместо "ленивый". Теперь вы не используете "ленивый".. это не реальное решение, если вы хотите использовать ленивый, работает как временное (очень временное) решение.

  3. используйте @Transactional в контроллере. Его не следует использовать здесь вы смешиваете сервисный слой с презентацией, это не очень хороший дизайн.
  4. использовать OpenSessionInViewFilter, сообщается о многих недостатках, возможной нестабильности.

в общем, лучшим решением является 1.


@Controller
@RequestMapping(value = "/topic")
@Transactional

Я решаю эту проблему, добавляя @Transactional, Я думаю, что это может сделать сессию открытой


Если вы пытаетесь иметь отношение между сущностью и коллекцией или списком объектов java( например, Long type), это было бы что-то вроде этого:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;

я узнал, что объявление @PersistenceContext as EXTENDED также решает эту проблему:

@PersistenceContext(type = PersistenceContextType.EXTENDED)

@ транзакционная аннотация на контроллере отсутствует

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}

Это была проблема, с которой я недавно столкнулся, которую я решил с помощью

<f:attribute name="collectionType" value="java.util.ArrayList" />

более подробное описание здесь и это спасло мой день.


ваш список ленивая загрузка, поэтому список не был загружен. звоните, чтобы попасть в список недостаточно. используйте в спящем режиме.инициализации для инициализации списка. Если dosnt работает, запустите элемент list и вызовите Hibernate.инициализировать для каждого . это должно быть до того, как вы вернетесь из области транзакции. посмотреть этой пост.
искать -

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" />

в файле контекста приложения.

на @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

вы всегда должны избегать следующих анти-шаблонов:

поэтому убедитесь, что это твой FetchType.LAZY ассоциации инициализируются во время запроса или в исходном @Transactional объем, используя Hibernate.initialize для вторичной коллекций.


С помощью hibernate @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, факт вызова ticket.getSales() сделайте еще один запрос для получения продаж, потому что вы явно спросили его.


причина вы пытаетесь получить commentList на геймпаде после закрытия сессии в службе.

topicById.getComments();

выше загрузит список комментариев, только если ваш сеанс гибернации активен, который, я думаю, вы закрыли в своем сервисе.

Итак, вы должны получить список комментариев перед закрытием сеанса.


в моем cae у меня было отображение b / w A и B, как

A имеет

@OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
Set<B> bs;

в слое DAO метод необходимо аннотировать с @Transactional Если вы не аннотировали отображение с помощью Fetch Type-Нетерпеливый


коллекция 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

решение может быть:

  1. аннотации comments С fetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
    
  2. если вы все еще хотите, чтобы комментарии были лениво загружены, используйте сеансы состояния Hibernate, так что вы сможете получить комментарии позже по требованию.


Привет всем постинг довольно поздно надеюсь, что это поможет другим, Заранее благодарю @GMK за этот пост спящий режим.initialize (object)

когда 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 " даже после закрытия сеанса гибернации.


одним из лучших решений является добавление в приложение следующего.файл свойств: весна.jpa.свойства.зимовать.enable_lazy_load_no_trans=true


еще один способ, чтобы сделать это, вы можете использовать TransactionTemplate обернуть вокруг ленивого fetch. Как

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>();