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

+3


source to share


4 answers


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.

+1


source


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

      

+4


source


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

      

0


source


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 ...

0


source







All Articles