I experience the following apparently undocumented issue, and I want to understand if
The behavior is this Assume the following mapping
<class name="org.sample.Foo" table="foo">
...
<many-to-one name="bar" class="org.sample.Bar"/>
</class>
<class name="org.sample.Bar" table="bar" lazy="false">
...
</class>
First, as a background, Hibernate default value for the fetch attribute on a many-to-one relation should be "select", this is at least what is documented (I'll add the link here when I find it)
However, this is apparently only true if the referenced class is lazy="true"!
so apparently the above mapping is translated into this (because Bar is lazy="false"):
<class name="org.sample.Foo" table="foo">
...
<many-to-one name="bar" class="org.sample.Bar" *fetch="join" />
</class>
<class name="org.sample.Bar" table="bar" lazy="false">
...
</class>
Now why would that be an issue? instead of 2 selects, Hibernate will load the non lazy reference in a single select with its "parent" (load Foo with Bar in a single select)
this actually makes sense, since the object is not lazy, why not load it?
The answer is this: what happens if Bar is in the 2nd level cache?
<class name="org.sample.Foo" table="foo">
...
<many-to-one name="bar" class="org.sample.Bar" *fetch="join" />
</class>
<class name="org.sample.Bar" table="bar" lazy="false">
<cache usage="transactional" />
...
</class>
And the answer to that is - nothing changes!
Apparently one would assume Hibernate is smart enough to understand that objects of this type should not be loaded, but since the default fetch was changed from select to join, Hibernate doesn't have a choice (you can't join a real table with the 2nd level cache, yet)
so Hibernate does what it is told, and uses a join to fetch an object from the database where it is already in the 2nd level cache
The solution I found is to literally change the mapping to fetch="select"
Now when the second select for Bar is about to go, Hibernate understands it should not go to the database, and fetches it from the cache. and only 1 query will execute (after warmup)
A Hibernate second-level cache is one of the data caching components available in the Hibernate object-relational mapping (ORM) library. Hibernate is a popular ORM library for the Java language, and it lets you store your Java object data in a relational database management system (RDBMS).
Query Cache: Hibernate can also cache result set of a query. Hibernate Query Cache doesn't cache the state of the actual entities in the cache; it caches only identifier values and results of value type. So it should always be used in conjunction with the second-level cache.
I encountered the same issue, and found myself marking all many-to-one relations that will be cached as fetch="select"
. At the time when the query is built up, Hibernate cannot know whether the requested instance of Bar is in the second level cache or not (assuming that Foo is not in cached).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With