3

Lets suppose we have some entity, which has 10 fields. And let's suppose that almost all these fields have very large data. And we want to load entity (not the set of fields!) and at runtime define which fields to load. The solution I found https://stackoverflow.com/a/24710759/5057736 suggests using constructor. But in case of 10 fields and that it is necessary to define fields at runtime is not possible solution. Is there a way how to solve this problem using jpa 2.1?

Community
  • 1
  • 1
Pavel_K
  • 10,748
  • 13
  • 73
  • 186
  • Let's suppose you use JPA EntityGraphs? – Neil Stockton May 05 '16 at 11:28
  • @Neil Stockton I thought that EntityGraphs is used only for showing what Entities to load, but not what their fields. – Pavel_K May 05 '16 at 11:46
  • Nope. Defines what fields to load. http://www.datanucleus.org/products/accessplatform_5_0/jpa/entity_graphs.html It has weaknesses however, in that you can't explicitly say don't load field X – Neil Stockton May 05 '16 at 12:29
  • @Neil Stockton Thanks. Could you show an example how to use unnamed entity graph with JPQL query? I would accept your answer. – Pavel_K May 05 '16 at 14:28

3 Answers3

3

Use a JPA 2.1 EntityGraph to define the fields to be retrieved by the query. So if you have a class MyClass, and want to retrieve particular fields dynamically, something like this could suffice

EntityGraph<MyClass> eg = em.createEntityGraph(MyClass.class);
eg.addAttributeNodes("id");
eg.addAttributeNodes("name");
eg.addAttributeNodes("relation");

Query q = em.createQuery("SELECT b FROM MyClass b");
q.setHint("javax.persistence.fetchgraph", eg);
List<MyClass> results = q.getResultList();
Neil Stockton
  • 11,383
  • 3
  • 34
  • 29
  • However, all fields are loaded by default. So, how can I disable loading by default all the fields? – Pavel_K May 05 '16 at 14:42
  • That is what "fetchgraph" does different to "loadgraph", see that link in the comments ... and see JPA spec section 3.7.4 – Neil Stockton May 05 '16 at 14:45
  • If that is the case then your JPA provider is unilaterally deciding to load that field against your wishes (i.e just taking the fetchgraph as a recommendation). The JPA provider I use (DataNucleus) respects my wishes, which is really what an EntityGraph should do IMHO. – Neil Stockton May 05 '16 at 15:00
  • Upvote that question. If this topic is interesting for you. – Pavel_K May 05 '16 at 15:11
  • Upvoted it, not that its particularly interestng to me ;-) I came across way too many places where Hibernate does its own thing rather than follow JPA's intent, and have no plan of going back to it ever. Good luck ! – Neil Stockton May 05 '16 at 15:13
  • Utter nonsense; the JPA spec is very clear that fields are part of the mechanism. The JPA spec does say that "the persistence provider is permitted to fetch additional fields to those specified" ... so Hibernate can claim it "obeys the spec" ... but that becomes a useless feature for anyone using it since Hibernate thinks it knows best – Neil Stockton May 06 '16 at 04:58
  • Thank you very much for your comments and time. The question now what should I do because I need this feature. – Pavel_K May 06 '16 at 05:11
  • Neil, good news. Your code was correct. I've tested it with eclipse link and I see in logs that there are only 3 columns in sql select. However, I came across another problem - if I do getBirthDate call (when it is not loaded) instead of getting `null` the execution of the thread stops. How can it be explained? – Pavel_K May 06 '16 at 16:58
  • You access the field at what point ? when the transaction is still open ? (in which case it should load it via a new SQL call), after the transaction/EntityManager closes? in which case you may get null or exception (depends what EclipseLink behaviour is for that situation). When I do something similar with DataNucleus if within a txn then it loads that field, and if the EntityManager is closed then it throws an exception that the field is not loaded (so load it before closing the EM) – Neil Stockton May 06 '16 at 17:29
  • I did it both after closing EM and without it - I use application managed EM. However, the result is the same. No exceptions! – Pavel_K May 06 '16 at 17:35
  • so suggest that you raise a question tagged as EclipseLink and define what you're doing and maybe an EclipseLink person will answer why it locks up. Nothing should ever lock up so its a bug IMHO, but maybe there is some property they have for that. – Neil Stockton May 06 '16 at 17:39
2

The Fetch graphs are mostly targeted to fetching associations, not for individual fields.

Even if the JPA spec says that fields should be lazy by default, LAZY is just a hint for the JPA providers, which might choose to ignore it. Hibernate does not use lazy loading for fields by default. Only one-to-many and many-to-many associations are LAZY by default.

To have lazy fields, you need to enable bytecode enhancement and maybe use @LazyGroup as well.

Anyway, maybe a DTO projection query is what you needed in the first place.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
1

With a Hibernate session this can be gained through using a result transformer. Hibernate doesn't support the result transformers for JPA.

HHH-8196 Custom ResultTransformer for JPA criteria queries

You can use unwrap(Session.class) to apply a result transformer to the session.

List<Person> persons = entityManager.unwrap(Session.class).
    createQuery("select name as name from Person").
    setResultTransformer(
        Transformers.aliasToBean(Person.class)
    ).list();

Additional information about nested projections

v.ladynev
  • 19,275
  • 8
  • 46
  • 67