1

I'm implementing a RESTful service using Spring Framework v3.2.4 and JPA + Hibernate 4. I'm returning resources in JSON format (using Jackson Mapper), but now I'm stuck with this exception:

Could not write JSON: failed to lazily initialize a collection of role: it.teck.service.model.Canvas.params, could not initialize proxy - no Session (through reference chain: it.teck.service.model.Canvas["params"]);

I have a "many to many" relationship between Canvas and Param entities and I need to serialize also params list when a canvas is requested to the service.

In my classes I have:

@Entity
public class Canvass {

    @ManyToMany
    @JoinTable(name = "canvas_params", joinColumns = { @JoinColumn(name = "id_canvas", referencedColumnName = "id_canvas") }, inverseJoinColumns = { @JoinColumn(name = "id_param", referencedColumnName = "id_param") })
        private List<Param> params;

    // ...
}

And:

@Entity
public class Param {

    @ManyToMany(mappedBy = "params")
    private List<Canvas> canvasList;

    // ...
}

In several posts and SO answers is suggested to exclude ManyToMany fields from serialization, to break the serialization loop, but I need to serialize params linked to my canvas entities. So, what should I do?

BenMorel
  • 30,280
  • 40
  • 163
  • 285
davioooh
  • 20,249
  • 33
  • 132
  • 224

5 Answers5

4

Because of the Hibernate lazy loading at the serialization time you end up with Hibernate proxy collections. At this time session it's already closed or not in scope. You can write a custom converter that has a JPA entity manager in scope (look for OpenEntityManagerInViewFilter examples) or if the performance is not an issue(the collections are fairly small) and you always need fully populated objects, you can specify a eager fetching strategy on the mapping as:

@ManyToMany(fetch = FetchType.EAGER) 
@JoinTable(name = "canvas_params", joinColumns = { @JoinColumn(name = "id_canvas", referencedColumnName = "id_canvas") }, inverseJoinColumns = { @JoinColumn(name = "id_param", referencedColumnName = "id_param") })
    private List<Param> params;
Ion Cojocaru
  • 2,513
  • 13
  • 16
  • I finally solved setting EAGER fetch type. It's very strage: I previously set this parameter but I got an "out of memory" error. Now it seems to work. Thank you. – davioooh Nov 08 '13 at 11:14
  • 1
    Setting the fetch type to EAGER in the mapping is the easy way to fix that kind of issues, but most of the time it is the wrong way. It can also have huge impact on other part of the application where you are loading Canvas. It will load your session with unneeded entities (Param). Hibernate can spend a lot of time doing dirty checking on flush if your session is huge. A simple cpu sampling will show it (lot of time spent in dirtyCheck method). – Thierry Nov 08 '13 at 13:37
  • If he serializes the objects immediately on retrieval without any intermediate changes than the benefits of lazy loading are fading. On the opposite if there is some processing/validation and rendering full objects only on demands than you are spot on – Ion Cojocaru Nov 08 '13 at 13:58
4

This happens because Jackson is trying to access a property of your bean that is managed by Hibernate outside of the session. So the property is lazily loaded and when you try to access it outside the session, hibernate will not be able to fetch it from DB.

You have three options:

  1. You do like Ion says, you set FetchType.EAGER on the entity. The downside is that everytime that you fetch one entity this way, it will fetch all the Canvases and Params linked to it. And you may not want that, because it may slow down your app.
  2. You serialize it when still in the session at DAO level or Service level. This is the cleanest and most proper way
  3. Finally, if (while still in the session) you do something like params.size() or canvasList.size(), this will automatically trigger the collection fetch from db. This is a bit of a hack, but it works and you don't have to modify your DAO signature or the fetch strategy for the entity.

There is another option, for Spring, that consists in making the controller method where the serialization is happening @Transactional, this will keep the session open! But it will make the method transactional so be careful with unwanted consequences.

gotch4
  • 12,473
  • 27
  • 100
  • 161
0

Your issue do not seems to be linked to a serialization loop, but to serialization that is done outside to the transaction that retrieved the parent object.

To fix this, can you do the serialization inside this tx ?

You have several other options, described in hibernate "lazily init exception" threads like : failed to lazily initialize a collection of role

Community
  • 1
  • 1
Thierry
  • 4,700
  • 29
  • 38
0

The other answers supplied do cover the cause of the problem. For the solution I advise you look into something like Hexagonal Architecture or something like that.

Essentially you want a repository layer where you get the data you are going to use, by forcing yourself to map from the Entity data objects into Domain data objects you will in fact circumnavigate this issue. The reason is that you are separating out your concerns. You are loading all the data you will use (not extra data you won't use) in a single place and then you are performing any logic in a different place. This will have the effect of getting all your database stuff done first and release those resources so that you can then do your logic at your leisure.

A good link to use for this is: http://alistair.cockburn.us/Hexagonal+architecture

Even if your implementation isn't strict, applying some of the disciplines it talks about will help massively.

Hope this helps.

kipper_t
  • 347
  • 3
  • 12
0

You can try as below to change the fetch strategy at run time to overcome this problem.

User user = (User) session.createCriteria(User.class)
    .setFetchMode("permissions", FetchMode.JOIN)
    .add( Restrictions.idEq(userId) )
    .uniqueResult();

There are few more option in "FetchMode".

udit khare
  • 354
  • 2
  • 10