Hello,
I am experiencing a strange
fetch="join" behavior for which I found no documentation, as well as an unexpected behavior with regards to 2nd level cache. The setup is as follows:
We have a uni-directional relationship from children to parent. In this example it is from Order to Person.
The parent class definition is fairly trivial, expect for the
lazy="false" attribute on the class tag - a limitation that currently I can not remove.
Code:
<class name="sample.Person" table="person" lazy="false">
<cache usage="read-write"/>
<id name="id" column="id" type="long" unsaved-value="-1">
<generator class="native"/>
</id>
...
<property ... />
</class>
The child class has a many-to-one association towards person, as follows:
Code:
<class name="sample.Order" table="orders" lazy="false">
<id name="id" column="id" type="long" unsaved-value="-1">
<generator class="native"/>
</id>
...
<many-to-one name="person" column="person_id" not-null="true" class="sample.Person" />
</class>
2nd level cache is enabled, using EhCache and some basic configuration from the tutorials. In addition, logging is enabled via log4j to see sql activity and cache activity.
Now, when all the Person objects are already in the cache, I load an Order object from the db, as follows:
Code:
getHibernateTemplate().load(Order.class, id);
System.out.println(order.getId() + " -> " + order.getPerson().getName());
and I got the following output:
Quote:
2009-11-05 15:08:07,093 DEBUG [main] util.SQLStatementLogger (SQLStatementLogger.java:111) -
select
order0_.id as id1_1_,
order0_.color as color1_1_,
order0_.cost as cost1_1_,
order0_.person_id as person4_1_1_,
person1_.id as id0_0_,
person1_.name as name0_0_,
person1_.age as age0_0_
from
orders order0_
inner join
person person1_
on order0_.person_id=person1_.id
where
order0_.id=?
2009-11-05 15:08:07,093 DEBUG [main] cache.EhCache (EhCache.java:74) - key: playground.model.Person#1
1 -> foo
Problem #1:As you can see from the output, hibernate is using a
join strategy to fetch, although the documentation states that
select is the default, not join. In my tests, if I remove the
lazy="false" attribute from the Person class, this behavior won't occur. However, I can't do that, so the only option that I found is to explicitly declare
fetch="select" on the many-to-one relation, as follows:
Code:
<many-to-one name="person" column="person_id" not-null="true" fetch="select" class="sample.Person" />
Problem #2By using the join, hibernate is going to the db to get the Person object, despite the fact that it's not necessary because that object is in the cache. As the output shows in the last line, hibernate indeed consult the cache and finds the object, so what was the point of the join??
When using
fetch="select" on the association as mentioned above, both problems go away. There is no join, and also, as expected, there is no call to the db for a second select, since the parent object is in the cache already.
My question is: is there a better way instead of an explicit
join="select"? and can hibernate be told to not use join if it is not necessary (if the joined-object is in the cache already)? Also, is this behavior documented anywhere?
Thanks,
Jonathan