Consider the following simplified example:
{code} @Table(name = "CONTAINER") class Container { @OneToMany(fetch = FetchType.EAGER) @JoinColumn(name = "CONTAINER_ID", referencedColumnName = "ID") @MapKey(name = "key") @Fetch(FetchMode.SUBSELECT) private Map<Longs, MappedAttribute> mappedAttributes;
@Transient private BusinessAttribute businessAttribute;
@PostLoad private void postLoad(){ this.businessAttribute = new BusinessAttribute(mappedAttributes); } }
@Table(name = "ATTRIBUTES") class MappedAttribute { ... }
class BusinessAttribute { public BusinessAttribute(List<MappedAttribute> args){} } {code}
When I call
{code} entityManager.createQuery("Select c from " + Container.class.getName() + " c where c.id in (1, 2, 3, 4)").getResultList(); {code}
Then hibernate executes the following SQL (simplified a little bit, for confidentiality)
{code} -- First select the root objects select * from CONTAINER where ID in (1,2,3,4); -- Then select all the attribute rows for each root object in individual queries select * from ATTRIBUTES where CONTAINER_ID = 1; select * from ATTRIBUTES where CONTAINER_ID = 2; select * from ATTRIBUTES where CONTAINER_ID = 3; select * from ATTRIBUTES where CONTAINER_ID = 4; {code}
This is the kind of 1+N SQL queries that I would expect to see if I had set {{FetchMode.SELECT}}, and does not seem consistent with {{FetchMode.SUBSELECT}}
If I comment out the {{@PostLoad}} annotation on {{Container.postLoad()}}, then hibernate runs the following SQL queries:
{code} -- First select the root objects select * from CONTAINER where ID in (1,2,3,4); -- The select all the attributes for all root objects at once select * from ATTRIBUTES where CONTAINER_ID in ( select ID from CONTAINER where ID in (1,2,3,4) ); {code}
Which is what I had expected it to do.
{{ The @ PostConstruct}} annotations should not disable {{FetchMode.SUBSELECT}} when PostLoad callback is indeed called AFTER all of the persistent attributes of the entities are eagerly loaded, or when but BEFORE any of the associations are loaded (even before eagerly fetches ones).
In my @PostLoad callback , I access those eagerly fetched associations ( or entity listener which are collections ) does not alter any , and that access triggers an early loading of the persistent properties of collection/associations. Since it is loaded within the parent context of a single entity , it uses a standard select . In my case, Once all of my fields are either basic the container entities have executed their @PostLoad callback , transient or hibernate tries to load the associations annotated with {{FetchType.EAGER}} , so but since the associations have already been populated for all of the container entities, there is no chance that whatever happens in nothing to do anymore and the bulk SUBSELECT is skipped.
According to the JPA 2.1 Specification / JSR-338, this behavior is incorrect:
{ {postConstruct() quote } } method would affect any association *3 . 5.3 Semantics of the Life Cycle Callback Methods for Entities* Alternately, if it [...] The PostLoad method for an entity is impossible or impractical to detect that invoked after the whole entity and all its associations are eagerly has been loaded , hibernate should provide a mechanism so that a developper can specify into the behavior of current persistence context from the listener database or after the refresh operation has been applied to hibernate (maybe via an it. { quote}
and
{ @ListenerProperties quote} *3.2.9 Load State* An entity is considered to be loaded if all attributes with FetchType.EAGER —whether explictly specified or by default— ( TRANSIENT_ONLY including relationship and other collection-valued attributes ) }} annotation, have been loaded from the database or maybe assigned by the application. Attributes with another FetchType.LAZY may or may not have been loaded. { {@Parameters quote } } annotation.
|
|