Reputation: 26054
I'm developing a REST API with Jersey, Spring and Hibernate. I have this endpoint:
@Path("incidences")
public class IncidencesResource {
@InjectParam
IncidencesService incidencesService;
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Incidence> getIncidences(
@QueryParam("lastupdate") String lastUpdate) {
return incidencesService.getIncidences();
}
and the service:
@Service
public class IncidencesService {
@Autowired
IncidencesDAO incidencesDAO;
@Transactional
public List<Incidence> getIncidences() {
List<Incidence> incidencias = incidencesDao.getIncidences();
return incidences;
}
}
But I get a org.hibernate.LazyInitializationException
error:
Failed to lazily initialize a collection of role: com.api.blabla.Incidence.questions, no session or session was closed
questions
is a @OneToMany
property of Incidence
.
If I put @Transactional
annotation in the endpoint method, instead of in the service, it works properly. But I understand that @Transactional has to be placed at service level.
Thanks.
Upvotes: 4
Views: 1767
Reputation: 330
You are having this problem because your transaction begins and ends at your service call. Recall from Hibernate documentation that lazy loading only works within a Hibernate Session (in this case, the hibernate session begins and ends with your service call). You can't reconnect to the hibernate session (simply) to initialize the collection.
Two approach to solve this:
You can either change the fetch type to EAGER to eagerly load the questions referenced inside the controller later
Make a separate query to the service to get the questions for the incidence from the controller and set the response in the view bean before returning the response
Additionally as suggestion from a better practise perspective you may want to change the @Transactional annotation in the service method to @Transactional(readOnly = true) for a better performance of the read only call.
Upvotes: 1
Reputation: 26054
Solved! I had Incidence -> questions
relationship in LAZY fetch type (default). However, in the endpoint resource method I was calling getQuestions()
. This code wasn't inside the transaction, so it throwed initialization exception.
I have solved it by changing fetch property of @OneToMany
to EAGER.
Thanks for your answers. It was difficult for you to help me if you can't see the rest of the code...
Upvotes: 0
Reputation: 106
What initialize does can you see here what-does-hibernate-initialize-do
Maybe you have to manually initialize it. Please try this:
@Transactional
public List<Incidence> getIncidences() {
List<Incidence> incidencias = incidencesDao.getIncidences();
for (Incidence inc : incidencias) {
inc.getQuestions().size();
}
return incidencias;
}
Upvotes: 0
Reputation: 24423
This does not mean @Transactional
is not working. It is doing what you told it to do. The service returns an instance of Incidence
without initialized collection of questions
, and you are trying to access that collection outside of service's transaction (probably when Jersey tries to convert it to JSON).
In this situation, the service must return all the data needed by the caller, which means you have to initialize questions
before returning the result. So, just call Hibernate.initialize(incidence.getQuestions())
in service method for every instance you return.
@Transactional
public List<Incidence> getIncidences() {
List<Incidence> incidencias = incidencesDao.getIncidences();
for (Incidence inc : incidencias) {
Hibernate.initialize(inc.getQuestions());
}
return incidencias;
}
Upvotes: 4