474

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

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
Eugene
  • 5,353
  • 6
  • 27
  • 37

32 Answers32

250

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.

Community
  • 1
  • 1
darrengorman
  • 12,952
  • 2
  • 23
  • 24
  • 41
    Sorry, but i'd like to use lazy-load. So, I've changed the 'LinkedHashSet' type t the 'PersistentList'. Exception still occurs – Eugene Jul 31 '12 at 19:15
  • I've changed my mind. It works nice for the well-mapped tutorial project. Thanks ) – Eugene Aug 30 '12 at 19:48
  • 290
    This could be used as a workaround, but not an actual solution to the problem. What if we need to fetch lazily? – Dkyc Oct 20 '14 at 11:59
  • 1
    Is it possible to load them lazy, without that Exception? – Egidio Caprino Mar 28 '15 at 19:02
  • 15
    but in case if we want lazy then this solution will not work and most of the cases we want lazy only. – prashant thakre May 06 '15 at 17:36
  • 2
    @prashantthakre clearly it won't work if you want lazy-loading, the whole point of this answer is that it loads the objects eagerly – darrengorman May 08 '15 at 18:30
  • 1
    @darrengorman Thanks but such fix for ManyToMany to give error - 'Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags' – Jay Oct 21 '15 at 17:21
  • 157
    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. – Ced Jun 29 '16 at 18:34
  • 2
    @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. – darrengorman Jun 30 '16 at 09:17
  • 20
    @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) :). – Ced Jun 30 '16 at 15:21
  • Loading with eager may return duplicate result due to join query, To avoid this add @Fetch(FetchMode.SUBSELECT) – Ishara Samantha Nov 21 '16 at 06:43
  • In my case, the "fetch = FetchType.EAGER" was enouth. – Bruno Morais Feb 22 '17 at 13:15
  • Setting @JsonIgnore annotation do the job for me. – Heril Muratovic Jan 16 '18 at 12:34
  • 2
    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. – Henry Jan 31 '18 at 03:16
  • The solution is not correct. We need lazy loading most of the time. It will never cover all scenarios. – saran3h Feb 14 '18 at 07:24
  • 7
    How can i solve this with out changing fetch type to EAGER – Manjunath M Mar 20 '18 at 05:16
  • 2
    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... – Achilles929 Jun 06 '19 at 20:45
  • Difference between FetchType LAZY and EAGER? https://stackoverflow.com/questions/2990799/difference-between-fetchtype-lazy-and-eager-in-java-persistence-api – amarVashishth Nov 10 '19 at 22:37
  • 2
    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 Dec 13 '19 at 10:24
  • 2
    @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. – darrengorman Dec 15 '19 at 22:07
  • This is perfect for me! Thank's a lot! – Renato Vasconcellos Jun 04 '20 at 18:45
  • Wonder why this got so many likes. It removes the exception, but there is a VERY GOOD reason for why FeatchType.LAZY is the default value for @OneToMany. With this reply, you could just as well UN-invent FetchType.LAZY – Plaul Feb 12 '23 at 13:22
214

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.

gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
Boris
  • 2,413
  • 2
  • 13
  • 6
  • 5
    (1) worked for me perfectly. My case: Hibernate.initialize(registry.getVehicle().getOwner().getPerson().getAddress()); – Leonel Sanches da Silva Sep 05 '13 at 03:29
  • 7
    It seems that Hibernate.initialize doesn't work with EntityManager – marionmaiden Feb 25 '16 at 18:26
  • 9
    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. – Steve Waters Feb 16 '17 at 07:07
  • 1
    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? – Pipo Sep 13 '18 at 09:34
119

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

Community
  • 1
  • 1
sarbuLopex
  • 1,777
  • 1
  • 15
  • 22
  • 6
    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). – ben3000 Dec 16 '16 at 08:51
  • 5
    Yes it is, but I thought it could be appreciated to make it clear you could actually change the propagation param – sarbuLopex Dec 19 '16 at 11:24
  • Isn't `@Transactional` a Spring-only thing? – Campa May 30 '19 at 09:14
  • @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 – sarbuLopex May 31 '19 at 10:09
  • I'm not sure I understand why this fixes my problem, but it did. Thank you! – Steve Aug 30 '21 at 18:16
  • Putting `@Transactional` on the getter of the collection was the fix for me. It's clean and makes sense, but it had to be done in the Entity class, which I am not really a fan of but better than not having it work. – Jetto Martínez Aug 24 '22 at 19:56
  • is FetchType.EAGER a good practice in @OneToMany decorators? – fer.trecool May 30 '23 at 21:19
  • @fer.trecool no it isn't. It's really demanding in terms of performance and you can obtain the same result lazily with the solution proposed in my answer. I think the only scenario of use would be a static small dataset that is really needed at every call BUT if you used Transactional correctly you would be covered anyway so..no – sarbuLopex Jun 01 '23 at 09:18
74

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.

Gandalf
  • 2,921
  • 5
  • 31
  • 44
43
@Controller
@RequestMapping(value = "/topic")
@Transactional

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

Evan Knowles
  • 7,426
  • 2
  • 37
  • 71
RuiZhi Wang
  • 455
  • 4
  • 2
  • Why did this receive a minus vote? Adding a transaction to the operation extends the session – 2dor Mar 18 '17 at 18:15
  • 11
    It is bad practice to ad @Transactional to the controller. – Rafael Dec 13 '19 at 10:27
  • @Rafael Why it's a bad practice? – Amr Ellafy Apr 30 '20 at 09:58
  • @AmrEllafy -> Here it is a good explanation: https://stackoverflow.com/a/18498834/1261162 – Rafael May 07 '20 at 19:43
  • @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. – Je Suis Alrick Oct 02 '21 at 05:04
  • 1
    @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. – Rafael Oct 06 '21 at 10:20
  • @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? – Je Suis Alrick Oct 06 '21 at 17:47
  • 1
    @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. https://stackoverflow.com/questions/1103363/why-is-hibernate-open-session-in-view-considered-a-bad-practice – Rafael Oct 06 '21 at 18:33
  • 2
    @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. – Je Suis Alrick Oct 07 '21 at 04:36
42

The best way to handle the LazyInitializationException is to join fetch upon query time, like this:

select t
from Topic t
left join fetch t.comments

You should ALWAYS avoid the following anti-patterns:

Therefore, make sure that your FetchType.LAZY associations are initialized at query time or within the original @Transactional scope using Hibernate.initialize for secondary collections.

jumping_monkey
  • 5,941
  • 2
  • 43
  • 58
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • 2
    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. – chrisinmtown Dec 12 '18 at 20:50
  • 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?) – Michiel Haisma Mar 21 '19 at 15:18
  • 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. – Vlad Mihalcea Mar 21 '19 at 17:34
  • 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? – Rafael Dec 13 '19 at 10:38
  • Of course, it's bad. Starting with Spring Boot 2, [a warning is logged telling you to disable it](https://github.com/spring-projects/spring-boot/issues/7107). – Vlad Mihalcea Dec 13 '19 at 11:41
  • 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. – Thomas Carlisle Dec 09 '20 at 18:09
34

The problem is caused by accessing an attribute with the hibernate session closed. You have not a hibernate transaction in the controller.

Possible solutions:

  1. Do all this logic, in the service layer, (with the @Transactional), not in the controller. There should be the right place to do this, it is part of the logic of the app, not in the controller (in this case, an interface to load the model). All the operations in the service layer should be transactional. i.e.: Move this line to the TopicService.findTopicByID method:

    Collection commentList = topicById.getComments();

  2. Use 'eager' instead of 'lazy'. Now you are not using 'lazy' .. it is not a real solution, if you want to use lazy, works like a temporary (very temporary) workaround.

  3. use @Transactional in the Controller. It should not be used here, you are mixing service layer with presentation, it is not a good design.
  4. use OpenSessionInViewFilter, many disadvantages reported, possible instability.

In general, the best solution is the 1.

abentan
  • 518
  • 5
  • 6
29

The reason is that when you use lazy load, the session is closed.

There are two solutions.

  1. Don't use lazy load.

    Set lazy=false in XML or Set @OneToMany(fetch = FetchType.EAGER) In annotation.

  2. Use lazy load.

    Set lazy=true in XML or Set @OneToMany(fetch = FetchType.LAZY) In annotation.

    and add OpenSessionInViewFilter filter in your web.xml

Detail See my POST.

Priyantha
  • 4,839
  • 6
  • 26
  • 46
saneryee
  • 3,239
  • 31
  • 22
  • 3
    ... and yet both solutions are not good. Suggest using EAGER can create huge problems. Using OpenSessionInViewFilter is an anti-pattern. – Rafael Dec 13 '19 at 10:26
20

In order to lazy load a collection there must be an active session. In a web app there are two ways to do this. You can use the Open Session In View pattern, where you use an interceptor to open the session at the beginning of the request and close it at the end. The risk there is that you have to have solid exception handling or you could bind up all your sessions and your app could hang.

The other way to handle this is to collect all the data you need in your controller, close your session, and then stuff the data into your model. I personally prefer this approach, as it seems a little closer to the spirit of the MVC pattern. Also if you get an error from the database this way you can handle it a lot better than if it happens in your view renderer. Your friend in this scenario is Hibernate.initialize(myTopic.getComments()). You will also have to reattach the object to the session, since you're creating a new transaction with every request. Use session.lock(myTopic,LockMode.NONE) for that.

GMK
  • 2,890
  • 2
  • 20
  • 24
18

One of the best solutions is to add the following in your application.properties file: spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

sreekmatta
  • 189
  • 1
  • 4
  • 1
    Can you tell the OP what it does exactly, any side effects, performance impact? – PeS Aug 01 '18 at 23:38
  • 5
    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. – sreekmatta Aug 09 '18 at 01:53
  • 3
    for some, it is considered as an anti-pattern https://vladmihalcea.com/the-hibernate-enable_lazy_load_no_trans-anti-pattern/ – Uri Loya Oct 17 '19 at 06:02
12

If you are trying to have a relation between a entity and a Collection or a List of java objects (for example Long type), it would like something like this:

@ElementCollection(fetch = FetchType.EAGER)
    public List<Long> ids;
javaboygo
  • 243
  • 2
  • 11
12

I got this error after a second execution of a method to generate a JWT token.

The line user.getUsersRole().stream().forEachOrdered((ur) -> roles.add(ur.getRoleId())); generated the error.

// MyUserDetails.java

@Service
public class MyUserDetails implements UserDetailsService {

  @Override
  public UserDetails loadUserByUsername(String email) {

    /* ERROR
    /* org.hibernate.LazyInitializationException: failed to 
    /* lazily initialize a collection of role: 
    /* com.organizator.backend.model.User.usersRole, 
    /* could not initialize proxy - no Session */
    user.getUsersRole().stream().forEachOrdered((ur) ->
           roles.add(ur.getRoleId()));

In my case the @Transactional annotation solved it,

// MyUserDetails.java

import org.springframework.transaction.annotation.Transactional;

@Service
public class MyUserDetails implements UserDetailsService {

  @Override
  @Transactional // <-- added
  public UserDetails loadUserByUsername(String email) {

    /* No Error */
    user.getUsersRole().stream().forEachOrdered((ur) ->
           roles.add(ur.getRoleId()));

user9869932
  • 6,571
  • 3
  • 55
  • 49
11

Two things you should have for fetch = FetchType.LAZY.

@Transactional

and

Hibernate.initialize(topicById.getComments());
karan patel
  • 537
  • 5
  • 11
9

There are multiple solution for this Lazy Initialisation issue -

1) Change the association Fetch type from LAZY to EAGER but this is not a good practice because this will degrade the performance.

2) Use FetchType.LAZY on associated Object and also use Transactional annotation in your service layer method so that session will remain open and when you will call topicById.getComments(), child object(comments) will get loaded.

3) Also, please try to use DTO object instead of entity in controller layer. In your case, session is closed at controller layer. SO better to convert entity to DTO in service layer.

vsharma
  • 299
  • 3
  • 3
8

@Transactional annotation on controller is missing

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}
Xcoder
  • 266
  • 1
  • 5
  • 13
  • 19
    I would argue that transaction management belongs to service layer where the business logic resides. – Sõber Oct 20 '16 at 21:22
  • Transactional annotation is not missing. Controller should not have such annotation. Those annotations should be at Service level. – Rafael Dec 13 '19 at 10:31
  • 1
    Since I didn't want to set `FetchMode` to `EAGER`, this was my solution. – Amirhosein Al Jul 08 '22 at 07:34
  • There are some cases where the intial entity need to be reload in the controller and then pass it the @Transactional at the controller level is the workaround I've used, it is far better from changing to eager. JPA doesn't have proper solution for this one. This annotation is used for a specific end-point in the contrller whereas changing to EAGER affect all calls! – user2304483 Dec 17 '22 at 13:57
  • There are some cases where the intial entity need to be reload in the controller. @Transactional at the controller level is the workaround I've used, but after reading the comments I move the annoation to the service layer. What I did is reload the entity again in the service layer. So two benefits, change to EAGER is not required and annotation is in the right place. The only probelm is unnessary query is done. – user2304483 Dec 17 '22 at 14:19
7

I found out that declaring @PersistenceContext as EXTENDED also solves this problem:

@PersistenceContext(type = PersistenceContextType.EXTENDED)
ElOjcar
  • 301
  • 2
  • 4
  • 12
Elcin ABD
  • 127
  • 1
  • 2
  • 3
    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 http://stackoverflow.com/questions/2547817/what-is-the-difference-between-transaction-scoped-persistence-context-and-extend – kiedysktos Jan 27 '17 at 14:13
  • Dangerous. This is not the correct answer. There are others above much more accurate and safe. – Rafael Dec 13 '19 at 10:34
7

The problem is caused because the code is accessing a lazy JPA relation when the "connection" to the database is closed (persistence context is the correct name in terms of Hibernate/JPA).

A simple way of solving it in Spring Boot is by defining a service layer and using the @Transactional annotation. This annotation in a method creates a transaction that propagates into the repository layer and keeps open the persistence context until the method finish. If you access the collection inside the transactional method Hibernate/JPA will fetch the data from the database.

In your case, you just need to annotate with @Transactional the method findTopicByID(id) in your TopicService and force the fetch of the collection in that method (for instance, by asking its size):

    @Transactional(readOnly = true)
    public Topic findTopicById(Long id) {
        Topic topic = TopicRepository.findById(id).orElse(null);
        topic.getComments().size();
        return topic;
    }
  • annotating a service method with `@Transactional` is not a good practice. It just blocks the connection to your db. This will cause your app to become unresponsive when the method calls increase. – Niamatullah Bakhshi Jul 18 '22 at 03:57
  • The use of `@Transctional` annotation in services is documented in multiple tutorials like https://vladmihalcea.com/spring-transactional-annotation/ or in the official Spring documentation: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction-declarative-attransactional-settings – Domingo Gallardo Jul 19 '22 at 06:54
  • We had used this on the methods that called our database (repository). After an increase in the number of method calls, our app started to hang. This was because this annotation blocks the current database connection. We had 30 Hikaripool database connections. All would exceed. Guess what! we removed these annotations from our service layer methods and added them to the repository. If you want to replicate the issue, you could try to configure something above. We used spring boot by the way. – Niamatullah Bakhshi Jul 19 '22 at 07:21
  • Thanks for sharing your experience, it is very useful. I'll investigate your solution. – Domingo Gallardo Jul 20 '22 at 08:14
6

your list is lazy loading, so the list wasn't loaded. call to get on the list is not enough. use in Hibernate.initialize in order to init the list. If dosnt work run on the list element and call Hibernate.initialize for each . this need to be before you return from the transaction scope. look at this post.
search for -

Node n = // .. get the node
Hibernate.initialize(n); // initializes 'parent' similar to getParent.
Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session 
Priyantha
  • 4,839
  • 6
  • 26
  • 46
Avihai Marchiano
  • 3,837
  • 3
  • 38
  • 55
6

To solve the problem in my case it was just missing this line

<tx:annotation-driven transaction-manager="myTxManager" />

in the application-context file.

The @Transactional annotation over a method was not taken into account.

Hope the answer will help someone

Madhav Datt
  • 1,065
  • 2
  • 10
  • 25
Mario Biasi
  • 61
  • 1
  • 1
5

it was the problem i recently faced which i solved with using

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

more detailed decription here and this saved my day.

tolgayilmaz
  • 3,987
  • 2
  • 19
  • 19
5

This is an old question but the below information may help people looking for an answer to this.

@VladMihalcea 's answer is useful. You must not rely on FetchType.EAGER , instead you should load the comments into the Topic entity when required.

If you are not explicitly defining your queries so that you could specify a join fetch, then using @NamedEntityGraph and @EntityGraph you could override the FetchType.LAZY (@OneToMany associations use LAZY by default) at runtime and load the comments at the same time as the Topic only when required. Which means that you restrict loading the comments to only those methods (queries) which really require that. An entity graph as JPA defines it:

An entity graph can be used with the find method or as a query hint to override or augment FetchType semantics.

You could use it based on the JPA example here. Alternatively, if you use Spring Data JPA, then you could use it based on the example provided by Spring.

Balu
  • 522
  • 6
  • 16
4

By using the hibernate @Transactional annotation, if you get an object from the database with lazy fetched attributes, you can simply get these by fetching these attributes like this :

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

Here, in an Hibernate proxy-managed transaction, the fact of calling ticket.getSales() do another query to fetch sales because you explicitly asked it.

Alex
  • 4,599
  • 4
  • 22
  • 42
4

To get rid of lazy initialization exception you should not call for lazy collection when you operate with detached object.

From my opinion, best approach is to use DTO, and not entity. In this case you can explicitly set fields which you want to use. As usual it's enough. No need to worry that something like jackson ObjectMapper, or hashCode generated by Lombok will call your methods implicitly.

For some specific cases you can use @EntityGrpaph annotation, which allow you to make eager load even if you have fetchType=lazy in your entity.

degr
  • 1,559
  • 1
  • 19
  • 37
2

For those working with Criteria, I found that

criteria.setFetchMode("lazily_fetched_member", FetchMode.EAGER);

did everything I needed had done.

Initial fetch mode for collections is set to FetchMode.LAZY to provide performance, but when I need the data, I just add that line and enjoy the fully populated objects.

velis
  • 8,747
  • 4
  • 44
  • 64
2

In my case following code was a problem:

entityManager.detach(topicById);
topicById.getComments() // exception thrown

Because it detached from the database and Hibernate no longer retrieved list from the field when it was needed. So I initialize it before detaching:

Hibernate.initialize(topicById.getComments());
entityManager.detach(topicById);
topicById.getComments() // works like a charm
kiedysktos
  • 3,910
  • 7
  • 31
  • 40
2

In my case, I had the mapping b/w A and B like

A has

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

in the DAO layer, the method needs to be annotated with @Transactional if you haven't annotated the mapping with Fetch Type - Eager

Farhan stands with Palestine
  • 13,890
  • 13
  • 58
  • 105
2

Not the best solution, but for those who are facing LazyInitializationException especially on Serialization this will help. Here you will check lazily initialized properties and setting null to those. For that create the below class

public class RepositoryUtil {
    public static final boolean isCollectionInitialized(Collection<?> collection) {
        if (collection instanceof PersistentCollection)
            return ((PersistentCollection) collection).wasInitialized();
        else 
            return true;
    }   
}

Inside your Entity class which you are having lazily initialized properties add a method like shown below. Add all your lazily loading properties inside this method.

public void checkLazyIntialzation() {
    if (!RepositoryUtil.isCollectionInitialized(yourlazyproperty)) {
        yourlazyproperty= null;
    }

Call this checkLazyIntialzation() method after on all the places where you are loading data.

 YourEntity obj= entityManager.find(YourEntity.class,1L);
  obj.checkLazyIntialzation();
Mohammed Javad
  • 629
  • 8
  • 18
1

The reason is you are trying to get the commentList on your controller after closing the session inside the service.

topicById.getComments();

Above will load the commentList only if your hibernate session is active, which I guess you closed in your service.

So, you have to get the commentList before closing the session.

aditya lath
  • 410
  • 7
  • 6
  • 2
    Yes, this is the problem statement, You should also provide an answer in an `Answer` – Sarz Apr 11 '18 at 05:36
1

The collection comments in your model class Topic is lazily loaded, which is the default behaviour if you don't annotate it with fetch = FetchType.EAGER specifically.

It is mostly likely that your findTopicByID service is using a stateless Hibernate session. A stateless session does not have the first level cache, i.e., no persistence context. Later on when you try to iterate comments, Hibernate will throw an exception.

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

The solution can be:

  1. Annotate comments with fetch = FetchType.EAGER

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)   
    private Collection<Comment> comments = new LinkedHashSet<Comment>();
    
  2. If you still would like comments to be lazily loaded, use Hibernate's stateful sessions, so that you'll be able to fetch comments later on demand.

Yuci
  • 27,235
  • 10
  • 114
  • 113
0

Hi All posting quite late hope it helps others, Thanking in advance to @GMK for this post Hibernate.initialize(object)

when Lazy="true"

Set<myObject> set=null;
hibernateSession.open
set=hibernateSession.getMyObjects();
hibernateSession.close();

now if i access 'set' after closing session it throws exception.

My solution :

Set<myObject> set=new HashSet<myObject>();
hibernateSession.open
set.addAll(hibernateSession.getMyObjects());
hibernateSession.close();

now i can access 'set' even after closing Hibernate Session.

vic
  • 29
  • 9
0

Yet another way to do the thing, you can use TransactionTemplate to wrap around the lazy fetch. Like

Collection<Comment> commentList = this.transactionTemplate.execute
(status -> topicById.getComments());
aristotll
  • 8,694
  • 6
  • 33
  • 53
-2

In my Spring-Boot project spring.jpa.open-in-view was set to false in application.properties. Setting it to true solved the problem.