Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate 2nd level cache objects that are lazy=false, result in a default fetch=join, is it documented anywhere?

I experience the following apparently undocumented issue, and I want to understand if

  1. I did something wrong
  2. Did anyone encounter the same issue?
  3. Is it really not documented anywhere? or did I miss something?

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)

like image 741
Eran Medan Avatar asked Dec 28 '09 10:12

Eran Medan


People also ask

Does hibernate support second-level caching?

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).

What is query level cache in hibernate?

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.


1 Answers

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).

like image 188
bertolami Avatar answered Sep 28 '22 05:09

bertolami