views:

442

answers:

2

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)

+1  A: 

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

bertolami
But isn't it weird it is not documented, not even in the official commercial books? Even in forums or here in SO I found no mentions of this issue...
Ehrann Mehdan
I agree. But to understand what hibernate exactly does, I often have to put on sql logging and have a look at what is actually going on.
bertolami
+1  A: 

Didn't see any mention of this in the "Hibernate in Action" book nor the hibernate forums

ruchirhhi