11

I'm having a problem where JPA is trying to lazily load my data when I don't want it to. Essentially what is happening is I'm using a Service to retrieve some data, and when I go to parse that data into JSON, the JSON library is triggering hibernate to try and lazily load the data. Is there any way to stop this? I've given an example below.

// Web Controller method
public String getEmployeesByQuery(String query) {

    Gson gson = new Gson();
    List<Employee> employees = employeeService.findEmployeesByQuery(query);

    // Here is where the problem is occurring - the gson.toJSON() method is (I imagine)
    // using my getters to format the JSON output, which is triggering hibernate to
    // try and lazily load my data...
    return gson.toJSON(employees);
}

Is it possible to set JPA/hibernate to not try and lazily load the data?

UPDATE: I realize that you can use FetchType.EAGER - but what if I don't want to eager load that data? I just want to stop hibernate from trying to retrieve more data - I already have the data I want. Right now whenever I try and access a get() method hibernate will throw a "no session or session is closed" error, which makes sense because my transaction was already committed from my service.

Thanks!

Brian DiCasa
  • 9,369
  • 18
  • 65
  • 97
  • Could you tell use more about your question: do you want to completely switch from lazy loading to eager loading or do you want not to lazy load employees in this specific case and why. – gabuzo Jan 26 '11 at 15:03
  • My problem is that I don't want to lazily load the data. Sure, I could mark it for EAGER loading - but what if I don't even care about that data right now? For example, if I have an Employee who has many positions, but I don't want that data because it's too detailed for the view I want to render, how do I tell JPA to not try and fetch that data? – Brian DiCasa Jan 26 '11 at 15:34
  • Try this: http://stackoverflow.com/questions/4802887/gson-how-to-exclude-specific-fields-from-serialization-without-annotations –  Aug 24 '12 at 23:44
  • I had the same problem. The following worked for me. http://stackoverflow.com/questions/4802887/gson-how-to-exclude-specific-fields-from-serialization-without-annotations –  Aug 24 '12 at 23:47

8 Answers8

6

There are several options:

  • If you always need to load your collection eagerly, you can specify fetch = FetchType.EAGER in your mapping, as suggested in other answers.

  • Otherwise you can enable eager fetching for particular query:

    • By using JOIN FETCH clause in HQL/JPQL query:

      SELECT e FROM Employee e JOIN FETCH e.children WHERE ...
      
    • By using fetch profiles (in JPA you can access Hibernate Session via em.unwrap(Session.class))
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • The thing is I don't want that extra data. Really all I want is some basic fields from only employee (like first name, last name, phone number, etc..) and not have hibernate try and retrieve more data when I don't want it! – Brian DiCasa Jan 26 '11 at 16:15
  • @Brian: If you don't want to include these fields into JSON representation, you should instruct your JSON serializer to ignore that fields, as suggested by Ruggs. It has nothing to do with JPA and Hibernate. – axtavt Jan 26 '11 at 16:42
3

You really have two options:

  1. You can copy the data from employee to one that is not being proxied by hibernate.
  2. See if there is a way to not have the toJSON library reflect the entire object graph. I know some JSON libraries allow you to only serialize some properties of an object to JSON.

Personally I would think #1 would be easier if your library only uses reflection.

jcjr
  • 1,503
  • 24
  • 40
Ruggs
  • 1,600
  • 2
  • 16
  • 25
  • It looks like these may be the only options. But either one of them isn't really ideal. You would think JPA would specify a way to have detached entities not try and retrieve more data... – Brian DiCasa Jan 26 '11 at 16:21
  • I've used hibernate and JPA and had this problem in both. I've never found a good way to solve it. – Ruggs Jan 26 '11 at 16:37
2

As others have stated, this is not an issue with JPA/hibernate but rather with the json serialization library you are using. You should instruct gson to exclude the properties you don't want traversed.

JMM
  • 536
  • 2
  • 8
1

Yes:

@*ToMany(fetch=FetchType.EAGER)
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
1

I suggest you to make a fetched copy of the entities you want to use outside of a transaction. That way, the lazy loading will occur from within a transaction and you can pass to Gson a plain, not enhanced, POJO.

You can use Doozer to do this. It is very flexible and through a little configuration (read you'll gonna loose your hair configuring it) you can even retrieve only partially the data you want to send to Gson.

gabuzo
  • 7,378
  • 4
  • 28
  • 36
  • That's an option, but I would really like to avoid adding another library if possible. – Brian DiCasa Jan 26 '11 at 16:18
  • You can do it manually if you want to using reflexivity to find out the getters and setters but using a library will save you hours of boring-error prone development. – gabuzo Jan 26 '11 at 16:32
0

You could always change the fetch attribute to FetchType.EAGER, but it is also worth considering if you have your transactions have the right scope. Collections will be correctly loaded if they are accessed within a transaction.

willcodejavaforfood
  • 43,223
  • 17
  • 81
  • 111
0

Your problem is that you are serializing the data. We ran into the same sort of problem with Flex and JPA/Hibernate. The trick is, depending on how much you want to mangle things, either

  1. Change your data model to not chase after the data you don't want.
  2. Copy the data you do want into some sort of DTO that has no relationships to worry about.
  3. Assuming you're using Hibernate, add the Session-in-view filter....its something like that, it will keep the session open while you serialize the entire database. ;)

Option one is what we did for the first big project we did, but it ruined the data access library we had for any sort of general purpose use. Since that time we've tended more toward option two.

YMMV

mezmo
  • 2,441
  • 19
  • 22
0

The easy and straight forward thing to do is create new Data classes (something like DTO) use Hibernate.isInitialized() to check if the object is initialized by hibernate or not. I am checking gson if i can override anything. I will post it here if I find anything new.

Kumar D
  • 1,308
  • 5
  • 19
  • 43