Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 418 Vote(s) - 3.48 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to solve the “failed to lazily initialize a collection of role” Hibernate exception

#1
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
Reply

#2
If you know that you'll want to see all `Comment`s 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](

[To see links please register here]

) if you want to know more.
Reply

#3
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][1] pattern, where you use an [interceptor][2] 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][3](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.


[1]:

[To see links please register here]

[2]:

[To see links please register here]

[3]:

[To see links please register here]

Reply

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

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

more detailed decription [here][1] and this saved my day.


[1]:

[To see links please register here]

Reply

#5
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.
Reply

#6
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;
Reply

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

@PersistenceContext(type = PersistenceContextType.EXTENDED)
Reply

#8
For those working with [Criteria][1], 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.

[1]:

[To see links please register here]

Reply

#9
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
Reply

#10
@Transactional annotation on controller is missing

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



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through