@Transactional doesn't work in @Service
I am developing REST APIs 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 service:
@Service
public class IncidencesService {
@Autowired
IncidencesDAO incidencesDAO;
@Transactional
public List<Incidence> getIncidences() {
List<Incidence> incidencias = incidencesDao.getIncidences();
return incidences;
}
}
But I am getting error org.hibernate.LazyInitializationException
:
Failed to lazily initialize role collection: com.api.blabla.Incidence.questions, no session or session closed
questions
is a @OneToMany
property Incidence
.
If I put the annotation @Transactional
in the endpoint method and not in the service, it works correctly. But my understanding is that @Transactional should be placed in the service layer.
Thank.
source to share
You are having this problem because your transaction starts and ends on your service call. Recall from the Hibernate documentation that lazy loading only works in a Hibernate session (in which case the hibernate session starts and ends with your service call). You cannot reconnect to a hibernation session (just) to initialize the collection.
Two approaches to solving this issue:
-
You can either change the fetch type to EAGER to load questions referenced inside the controller
-
Make a separate request to the service to get frequency questions from the controller and ask the response in the bean view before returning the response
Also, as a best practice suggestion, you might want to change the @Transactional annotation in the service method to @Transactional (readOnly = true) for better read-only performance.
source to share
This doesn't mean it @Transactional
doesn't work. He does what you told him to do. The service returns an instance Incidence
without initializing the collection questions
, and you are trying to access that collection outside of the service transaction (possibly when Jersey is trying to convert it to JSON).
In this situation, the service must return all the data the caller needs, which means you must initialize questions
before returning the result. So just call Hibernate.initialize(incidence.getQuestions())
in the service method for each returned instance.
@Transactional
public List<Incidence> getIncidences() {
List<Incidence> incidencias = incidencesDao.getIncidences();
for (Incidence inc : incidencias) {
Hibernate.initialize(inc.getQuestions());
}
return incidencias;
}
source to share
What can you initialize here, what-does-hibernate-initialize-do
You may need to manually initialize it. Try the following:
@Transactional
public List<Incidence> getIncidences() {
List<Incidence> incidencias = incidencesDao.getIncidences();
for (Incidence inc : incidencias) {
inc.getQuestions().size();
}
return incidencias;
}
source to share
Resolved! I had a relation Incidence -> questions
in sample LAZY type (default). However, in the endpoint resource method, I called getQuestions()
. This code was not inside a transaction, so it threw an initialization exception.
I solved this by changing the extract property @OneToMany
to EAGER .
Thank you for your responses. It was hard for you to help me if you can't see the rest of the code ...
source to share