How to solve the “failed to lazily initialize a collection of role” Hibernate exception

627,174

Solution 1

If you know that you'll want to see all Comments every time you retrieve a Topic then change your field mapping for comments to:

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

Collections are lazy-loaded by default, take a look at this if you want to know more.

Solution 2

From my experience, I have the following methods to solved the famous LazyInitializationException:

(1) Use Hibernate.initialize

Hibernate.initialize(topics.getComments());

(2) Use JOIN FETCH

You can use the JOIN FETCH syntax in your JPQL to explicitly fetch the child collection out. This is some how like EAGER fetching.

(3) Use OpenSessionInViewFilter

LazyInitializationException often occur in view layer. If you use Spring framework, you can use OpenSessionInViewFilter. However, I do not suggest you to do so. It may leads to performance issue if not use correctly.

Solution 3

I know it's an old question but I want to help. You can put the transactional annotation on the service method you need, in this case findTopicByID(id) should have

@Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class)

more info about this annotation can be found here

About the other solutions:

fetch = FetchType.EAGER 

is not a good practice, it should be used ONLY if necessary.

Hibernate.initialize(topics.getComments());

The hibernate initializer binds your classes to the hibernate technology. If you are aiming to be flexible is not a good way to go.

Hope it helps

Solution 4

The origin of your problem:

By default hibernate lazily loads the collections (relationships) which means whenver you use the collection in your code(here comments field in Topic class) the hibernate gets that from database, now the problem is that you are getting the collection in your controller (where the JPA session is closed).This is the line of code that causes the exception (where you are loading the comments collection):

    Collection<Comment> commentList = topicById.getComments();

You are getting "comments" collection (topic.getComments()) in your controller(where JPA session has ended) and that causes the exception. Also if you had got the comments collection in your jsp file like this(instead of getting it in your controller):

<c:forEach items="topic.comments" var="item">
//some code
</c:forEach>

You would still have the same exception for the same reason.

Solving the problem:

Because you just can have only two collections with the FetchType.Eager(eagerly fetched collection) in an Entity class and because lazy loading is more efficient than eagerly loading, I think this way of solving your problem is better than just changing the FetchType to eager:

If you want to have collection lazy initialized, and also make this work, it is better to add this snippet of code to your web.xml :

<filter>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SpringOpenEntityManagerInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

What this code does is that it will increase the length of your JPA session or as the documentation says, it is used "to allow for lazy loading in web views despite the original transactions already being completed." so this way the JPA session will be open a bit longer and because of that you can lazily load collections in your jsp files and controller classes.

Solution 5

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

i solve this problem by adding @Transactional,i think this can make session open

Share:
627,174
Eugene
Author by

Eugene

Updated on December 28, 2021

Comments

  • Eugene
    Eugene over 2 years

    I have this problem:

    org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: mvc3.model.Topic.comments, no session or session was closed

    Here is the model:

    @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;
        }
    
    }
    

    The controller, which calls model looks like the following:

    @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);
    
         }
    
    }
    

    The jsp-page looks li the following:

    <%@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>
    

    Exception is rised, when viewing jsp. In the line with c:forEach loop

  • Eugene
    Eugene almost 12 years
    Sorry, but i'd like to use lazy-load. So, I've changed the 'LinkedHashSet' type t the 'PersistentList'. Exception still occurs
  • Eugene
    Eugene over 11 years
    I've changed my mind. It works nice for the well-mapped tutorial project. Thanks )
  • Leonel Sanches da Silva
    Leonel Sanches da Silva over 10 years
    (1) worked for me perfectly. My case: Hibernate.initialize(registry.getVehicle().getOwner().getPer‌​son().getAddress());
  • Dkyc
    Dkyc over 9 years
    This could be used as a workaround, but not an actual solution to the problem. What if we need to fetch lazily?
  • Egidio Caprino
    Egidio Caprino about 9 years
    Is it possible to load them lazy, without that Exception?
  • prashant thakre
    prashant thakre about 9 years
    but in case if we want lazy then this solution will not work and most of the cases we want lazy only.
  • darrengorman
    darrengorman about 9 years
    @prashantthakre clearly it won't work if you want lazy-loading, the whole point of this answer is that it loads the objects eagerly
  • Jay
    Jay over 8 years
    @darrengorman Thanks but such fix for ManyToMany to give error - 'Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags'
  • Dims
    Dims over 8 years
    Why is JPS session closed? How to make it not being closed? How to perform lazy collection?
  • marionmaiden
    marionmaiden about 8 years
    It seems that Hibernate.initialize doesn't work with EntityManager
  • Ced
    Ced almost 8 years
    This is the type of answer that pops up everywhere on stack overflow. Short, to the point, solves the problem and MISLEADING. To future readers, do yourself a favor and learn what exactly is lazy and eagerly fetched, and understand the consequences.
  • darrengorman
    darrengorman almost 8 years
    @Ced in what way does the answer mislead? It clearly states that this is only a solution if the collection is to be loaded every time. Initially the OP stated they'd like to lazily fetch, but then changed their mind and accepted the answer. The answer even links to a concise explanation of the differences between the two strategies. I provided a possible solution, but never claimed it was the only one.
  • Ced
    Ced almost 8 years
    @darrengorman When I started JPA I posted a question around the lines of OP's one. I received the same response as you gave. Soon enough when I ran some test with hundreds of thousands of row, guess what happened ? I think it is misleading because it provides too simple of an answer for a problem that mostly beginners will face and soon enough they will have their whole database loaded in memory if they are not careful (and they won't, because they won't be aware of it) :).
  • Sõber
    Sõber over 7 years
    I would argue that transaction management belongs to service layer where the business logic resides.
  • Ishara Samantha
    Ishara Samantha over 7 years
    Loading with eager may return duplicate result due to join query, To avoid this add @Fetch(FetchMode.SUBSELECT)
  • ben3000
    ben3000 over 7 years
    The @Transactional annotation worked for me, but note that Propagation.REQUIRED is the default, at least in Spring Boot 1.4.2 (Spring 4.3).
  • sarbuLopex
    sarbuLopex over 7 years
    Yes it is, but I thought it could be appreciated to make it clear you could actually change the propagation param
  • kiedysktos
    kiedysktos over 7 years
    Hi, be careful with such changes. TRANSACTION scoped persistence context creation is lazy, which was the OP intent. So the question is whether you want to be stateless or not. This setting is dependent on the purpose of the system and shouldn't be changed too... eagerly. If you know what I mean. Read here stackoverflow.com/questions/2547817/…
  • kiedysktos
    kiedysktos over 7 years
    in many cases, you really don't want to do that. You loose all the benefits of lazy loading here
  • Steve Waters
    Steve Waters about 7 years
    This should be the correct answer. For example in my project at work we are explicitly not supposed to use EAGER fetching. It causes problems in this particular system.
  • Bruno Morais
    Bruno Morais about 7 years
    In my case, the "fetch = FetchType.EAGER" was enouth.
  • 2dor
    2dor about 7 years
    Why did this receive a minus vote? Adding a transaction to the operation extends the session
  • Zhasulan Berdibekov
    Zhasulan Berdibekov almost 7 years
    Fetch type of Eager assumed that hibernate will be pulled all data in the first query, not all places it is correctly
  • Heril Muratovic
    Heril Muratovic over 6 years
    Setting @JsonIgnore annotation do the job for me.
  • Henry
    Henry over 6 years
    This isn't actually a solution to the question, it's just changing to use eager loading, which might not actually be appropriate in many cases.
  • saran3h
    saran3h about 6 years
    The solution is not correct. We need lazy loading most of the time. It will never cover all scenarios.
  • Manjunath M
    Manjunath M about 6 years
    How can i solve this with out changing fetch type to EAGER
  • Sarz
    Sarz about 6 years
    Yes, this is the problem statement, You should also provide an answer in an Answer
  • PeS
    PeS almost 6 years
    Can you tell the OP what it does exactly, any side effects, performance impact?
  • sreekmatta
    sreekmatta almost 6 years
    At the back of the lazy loading, a new session is forked every time an association is loaded lazily, hence more connections are forked and creates a bit of pressure on the connection pool. If you have a limit on the number of connections then this property might not be a correct one to use.
  • Pipo
    Pipo over 5 years
    Seems attractive but lack of documentation to implement in another case... could you please provide some more links or explanation on how to implement this solution?
  • chrisinmtown
    chrisinmtown over 5 years
    What defines a limit of two FetchType.Eager collections per Entity?
  • chrisinmtown
    chrisinmtown over 5 years
    Vlad do you have any suggestions for working with a lazy-initialized collection in an entity fetched by a Spring-generated repository's findById() method? I'm not writing the query and the transaction is outside my code.
  • Michiel Haisma
    Michiel Haisma about 5 years
    Could you clarify what you mean by 'within the original @Transactional scope' This is unclear to me as I seem to get this error while in an open session (but not the right one?)
  • Vlad Mihalcea
    Vlad Mihalcea about 5 years
    While inside the scope o the top-most transactional service method, also known as the transaction gateway. Check out the TrassctionInterceptor in the stack trace and that's the one.
  • Campa
    Campa almost 5 years
    Isn't @Transactional a Spring-only thing?
  • sarbuLopex
    sarbuLopex almost 5 years
    @Campa yes it is. If you want to handle it manually you should put your business logic inside a transaction retrieved from the entity manager
  • Achilles929
    Achilles929 almost 5 years
    How is this the top/accepted answer? Clearly people come to this to find out how to solve the lazy load exception. The solution isn't to switch to use eager loading...
  • Uri Loya
    Uri Loya over 4 years
    for some, it is considered as an anti-pattern vladmihalcea.com/…
  • amarVashishth
    amarVashishth over 4 years
    Difference between FetchType LAZY and EAGER? stackoverflow.com/questions/2990799/…
  • Askar
    Askar over 4 years
    In Spring Boot, you can add 'spring.jpa.open-in-view=true' to 'application.properties'
  • Rafael
    Rafael over 4 years
    This shoudnt be accepted since it's misleading. Suggest enabling EAGER load without further thinking could create big other problems in the future. Is a crappy solution.
  • Rafael
    Rafael over 4 years
    ... and yet both solutions are not good. Suggest using EAGER can create huge problems. Using OpenSessionInViewFilter is an anti-pattern.
  • Rafael
    Rafael over 4 years
    It is bad practice to ad @Transactional to the controller.
  • Rafael
    Rafael over 4 years
    Transactional annotation is not missing. Controller should not have such annotation. Those annotations should be at Service level.
  • Rafael
    Rafael over 4 years
    Dangerous. This is not the correct answer. There are others above much more accurate and safe.
  • Rafael
    Rafael over 4 years
    You should UPPERCASE that the BEST SOLUTION IS 1 ... in fact is the ONLY GOOD solution since all others are anti-patterns!
  • Rafael
    Rafael over 4 years
    One of the best answers by far ... this should marked as the correct. BTW ... assuming OSIV is an anti-pattern how is possible that is enabled by default on spring-boot recent versions? ... maybe is not that bad?
  • Vlad Mihalcea
    Vlad Mihalcea over 4 years
    Of course, it's bad. Starting with Spring Boot 2, a warning is logged telling you to disable it.
  • darrengorman
    darrengorman over 4 years
    @Rafael doing anything in software development without further thinking could create problems in the future. 7.5 years after I answered this question your comment doesn't add any value.
  • Amr Ellafy
    Amr Ellafy about 4 years
    @Rafael Why it's a bad practice?
  • Rafael
    Rafael about 4 years
    @AmrEllafy -> Here it is a good explanation: stackoverflow.com/a/18498834/1261162
  • Renato Vasconcellos
    Renato Vasconcellos almost 4 years
    This is perfect for me! Thank's a lot!
  • Thomas Carlisle
    Thomas Carlisle over 3 years
    I just want to say, I found this answer and Vlad's article to be very helpful. I did resolve my issue without resorting to eager loading, OSIV or enable_lazy_load_no_trans. I am sure those things exist for a reason and there are valid use cases, but I have yet to encounter one. For me, it was as simple as making the service layer @Transactional, which is exactly what this answer's last lines say.
  • Dhwanil Patel
    Dhwanil Patel about 3 years
    Thank you so much bro. I exactly found second block :)
  • Luis Acero
    Luis Acero about 3 years
    I agree with @Rafael the only solution is the first
  • Steve
    Steve over 2 years
    I'm not sure I understand why this fixes my problem, but it did. Thank you!
  • Veerabala J
    Veerabala J over 2 years
    Could you pls tell why @Transactional solved this issue?
  • Je Suis Alrick
    Je Suis Alrick over 2 years
    @Rafael, it's bad practice to use the opinion of one person and say it is bad practice. If you pointed to a recommendation from the people at Spring fine, but in this case you pointed to a SO answer that wasn't even accepted and makes no reference to official Spring documentation.
  • Rafael
    Rafael over 2 years
    @JeSuisAlrick This entire site is based on professional advice. It is my professional advice to avoid the proposed solution and consider it as a bad practice, and I pointed to a very clear explanation of why. Anyone can of course disagree with that. If you don't want to accept that explanation because it doesn't comes from Spring, that's your decision. Others may or may not agree with you. But I must vote minus because I really think that's a misleading answer.
  • Je Suis Alrick
    Je Suis Alrick over 2 years
    @Rafael, I understand what you and the SO answer you referred to are saying. But my question is, if the transaction needs to extend to the controller for the lazy loading of related models, how is a transaction not a cross-cutting concern in this case? If a transaction does address cross-cutting concerns, then how is this approach bad practice? Isn't it subjective and based on the specific implementation of transaction management? If this answer is bad practice, what would be your alternative answer for this SO?
  • Rafael
    Rafael over 2 years
    @JeSuisAlrick "if the transaction needs to extend to the controller for the lazy loading of related models". Thats an opinionated design decision ... which IMO is wrong. You shouldn't extend the transaction boundary up to the controller. If you are refering to "Open Session In View" ... that's an anti-pattern to me. stackoverflow.com/questions/1103363/…
  • Je Suis Alrick
    Je Suis Alrick over 2 years
    @Rafael, I see what you mean--"finalise" the resulting DTO at the service layer and pass that on to the presentation layer. I'm guessing that if he was using MapStruct he'd move it to the service layer and resolve the DTO(s) using it. A sound solution.
  • Amirhosein Al
    Amirhosein Al almost 2 years
    Since I didn't want to set FetchMode to EAGER, this was my solution.