Each row of the table Person
(having name
, firstname
and age
) shall be read.
EntityManager em = emf.createEntityManager();
Session s = (Session) em.getDelegate();
Criteria criteria = s.createCriteria(Person.class);
criteria.setFetchMode("age", FetchMode.SELECT);
But the SQL shows
Hibernate:
select
person0_.name,
person0_.firstname,
person0_.age
from
SCOPE.PERSON person0_
How to let the age be lazy ONLY for the Criteria??
Setting the FetchMode of the "age" property on a criteria has no effect because the fetching strategy at this point is for associated objects only but not for properties. See section 20.1. Fetching strategies of the hibernate docs.
The only way for lazy loading of a property is the
@Basic
annotation set toFetchType.LAZY
. See here, or if you use .hbm.xml files for mapping uselazy=true
, see this section of the hibernate docs.Lazy loading of properties also use buildtime bytecode instumentation (hibernate is changing the entity classes after compilation to allow lazy loading of properties). Read 20.1.8. Using lazy property fetching
An other possible solution (except for all the other solutions) to your problem is to make a simpler Person class and use a constructor query like:
You could even use your existing Person class, just extend it with an appropriate constructor, but I would prefer explicitness.
But all the solutions presented here do not implement a lazy loading of the
age
attribute. The only way to do this is the@Basic
annotation, or you have to implement your own lazy loading.Your reasoning is valid (in general; we can however argue about the specific example of the
age
field), but unfortunately there is no straight-forward solution for this. Actually, Hibernate has the concept of fetch profiles, but it is currently very limited (you can override the default fetch plan/strategy only with the join-style fetch profiles).So, the possible workaround to your issue could be as follows.
1) Move
age
to a separate entity and associate thePerson
entity with it with a lazy one-to-one relationship:2) Define a fetch profile which overrides the default one:
3) Enable this profile for each session in the application:
Depending on the framework you use, there should be an easy hook/interceptor which you will use to enable the profile for each session (transaction) that is craeted. For example, an approach in Spring could be to override AbstractPlatformTransactionManager.doBegin of the transaction manager in use.
This way the
personAge
will be eagerly loaded in all the sessions in the application, unless the fetch profile is explicitly disabled.4) Disable the fetch profile in the session in which you use the desired Criteria query:
This way the default fetch plan/strategy is used (specified in the entity mappings), which is the lazy loading of the
PersonAge
.If your age is an object like the PersonAge of @Dragan you could associate the fecth mode with the criteria rather than the entity like you do.
So, I think you have three options:
For Projection you could use ResultTransformer to
I think you could create a PersonProxy on your own that triggers a query for retrieve the age but this is kind of awful.
You can simply define a new entity
SimplePerson
mapped to the samepersons
database table which contains only the following attributes:This way, when selecting a
SimplePerson
with both Criteria and HQL, the age column will not be retrieved.Another alternative is to use lazy loading for basic attributes, but mapping multiple subentities to the same database table is much more flexible.
I think that lazy mode only makes sense with associations. If you are accessing a plain table it will load all the fields.
If you want the
age
field not to appear in the SQL and so not being loaded into memory then use projections: