3

I have a Spring application combined with Hibernate to write some services which I can consume. I do not have a view, because these are service calls from a mobile phone which sends and gets JSON results.

So I have a Controller which handles the REST call:

@Controller
@RequestMapping(value = "/test")
public class TestController {
    @Autowired
    private TestService testService;
    …

    @RequestMapping(value = "/", method = RequestMethod.PUT)
    public ResponseEntity<Map<String, Object>> testMethod(
      @RequestHeader("userid") Integer userid, …) {
        User user = testService.getUserById(userid);
        List<Phone> phones = user.getPhones();
        phones.add(…);
        …
    }
}

If I try to access the Phones, I get a org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.pojo.User.phones, no session or session was closed.

I have the two POJO classes User and Phone annotated in this way, so the User is the owner of this one-to-many relationship:

@OneToMany(cascade = { CascadeType.ALL })
@JoinColumn(name = "USER_ID")
private List<Phone> phones;

@ManyToOne
@JoinColumn(name = "USER_ID", insertable = false, updatable = false, nullable = false)
private User user;

My structure is Service -> ServiceImplementation and Dao -> DaoImplementation.

The ServiceImplementation method getUserById(userid) is annotated @Transactional.

How can I solve this problem? I know, that in this service REST call I always need to get the List of phones. I read sth. about OpenSessionInView, but it seems to be a pattern for using a view, which I do not have.

Putting a Hibernate.initialize(user.getPhones()); in front of List<Phone> phones = user.getPhones(); does not work.

I know this is a common problem, but I do not know how to solve in my case.

2 Answers 2

1

Based on this:

LazyInitializationException Within a @Transactional Method

Set up annotation driven transactions:

<tx:annotation-driven />

And then annotate your method @Transactional

As long as you get the User and access their list of phones within the same transaction, then you can use lazy fetches. i.e. Only get the list of phones if you need it in the response.

Sign up to request clarification or add additional context in comments.

3 Comments

I have <tx:annotation-driven /> in my servlet.xml together with @Transactional before testMethod and it seems to work!
This would make your controller method transactional - which is usually not so recommended but can be done if you are out of other options.
True ... a 'better' design would have those repo queries inside a service method. The answer would still be to make that method transactional.
1

Since the transaction boundary is limited to the Service layer, if you try to access the lazy loaded relation it will give you the exception you are getting because by the time you access it the transaction has ended and the persistence context that loaded user was closed. The option you have is to load it eagerly in your service layer by accessing the relation or mark your relation as eager loaded in the entity so that Hibernate automatically loads it while loading the user.

@OneToMany(cascade =  CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "USER_ID")
private List<Phone> phones;

2 Comments

Making it eager fetched seems like a brutal change to fix this one persistence requirement; you might as well just perform a query then to fetch the phones for a specific user to not be hindered by the lazy mapping in this one spot. It would become relevant to change the fetch strategy if the problem exists in multiple places. What I wonder is: what is the intended result. To persist a new phone through cascading?
Good point Gimby.But as Tim mentioned that he always requires the list. Running a seperate query can be a good option if it's all about getting the data and not creating a parent/child relation where you would want to add a phone to list and cascade the persistence operations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.