Member Menu
 
 Monthly JBoss newsletter:
 
Hibernate Books
CaveatEmptor

Some explanations on lazy loading

Generality

This page try to explain how lazy loading work in Hibernate.

one-to-one

How to do it

Use

<one-to-one constrained="true" outer-join="false" class="Foo"/>

with a proxied class, Foo. This is ONLY conceptually possible for a mandatory association since we have to hit the other table to determine whether the association is null or not!

Why having this limitation ?

Think about how lazy one-to-many is implemented. Suppose you have

class A {
    private Set bees;
    public Set getBees() {
        return bees;
    }

    public void setBees(Set bees) {
        this.bees = bees;
    }

}

class B {
    // Not important really
}

What happens when Hibernate loads object of class A? It creates special Set wrapper which is not initialized yet. Then it sets this wrapper to "bees" (a.setBees(wrapper)).

Right after loading A you may call getBees() and get this set. This Set object is NEVER null. But it is not loaded from the database yet. When you perform first meaningful action on this set (size(), iterate(), etc) Hibernate loads corresponding objects from the database and initializes set. This is possible because Hibernate subclasses HashSet or something like that and override all meaningful operations to know someone needs the real data.

Now consider our class B has one-to-one association to C

class B {
    private C cee;

    public C getCee() {
        return cee;
    }

    public void setCee(C cee) {
        this.cee = cee;
    }
}

class C {
    // Not important really
}

Right after loading B, you may call getCee() to obtain C. But look, getCee() is a method of YOUR class and Hibernate has no control over it. Hibernate does not know when someone is going to call getCee(). That means Hibernate must put an appropriate value into "cee" property at the moment it loads B from database.

If proxy is enabled for C, Hibernate can put a C-proxy object which is not loaded yet, but will be loaded when someone uses it. This gives lazy loading for one-to-one.

But now imagine your B object may or may not have associated C (constrained="false"). What should getCee() return when specific B does not have C? Null. But remember, Hibernate must set correct value of "cee" at the moment it set B (because it does no know when someone will call getCee()). Proxy does not help here because proxy itself in already non-null object.

So the resume: if your B->C mapping is mandatory (constrainted=true), Hibernate will use proxy for C resulting in lazy initialization. But if you allow B without C, Hibernate just HAS TO check presence of C at the moment it loads B. But a SELECT to check presence is just inefficient because the same SELECT may not just check presence, but load entire object. So lazy loading goes away.

-- Tks to Dimas (stolen from the forum)


  NEW COMMENT

Make sure you enable proxy or lazy for referenced class 02 Jul 2004, 13:33 chlu
The one-to-one reference is only loaded lazily if the object used as the
class has a proxy or is lazy (proxy is the object itself). I didn't read
it the first time, so it didn't work out as it should.

Just use

<class name="C" lazy="true">

or

<class name="C" proxy="CProxy">

and it will do.
 
But this doesn't work with foreign keys! 15 Jul 2004, 16:25 amezick
If I set the parent (who has an identity generated Id) to have a
constrained=true to a child (who has an foreign generated Id) I get an
error because hibernate tries to save the child before it gets the id
from the parent table.
 
Use a foreign generator 30 Jul 2004, 19:05 christian
Thats because you have to use a "foreign" generator for C, which is 
needed to get the same primary key value as for B.
 
Why the outer-join setting? 08 Mar 2005, 11:41 damo9f
Why is outer-join set to false?
 
Re: Why the outer-join setting? 08 Mar 2005, 11:46 damo9f
I think I see -- outer-join=false means do not do any deep or 
pre-fetching.  I.e. outer-joing=false does not cause an inner join to be 
used; rather, it prevents any join from being used to load the related 
objects in a single query.  Pre-loading would defeat the purpose of any 
attemped lazy fetching.
 
Mapping a "one-to-zero-or-one" relationship 18 Oct 2005, 16:35 Regis Pires Magalhaes
I've been searching a way to do a lazy a "one-to-zero-or-one"
relationship in Hibernate and I finally found a way to do it using
many-to-one with unique="true". See example below:

Association: Person [1 <--> 0..1] Note

<class name="Person" table="person">
   <id name="id" column="id" type="int" unsaved-value="-1">
       <generator class="sequence">
           <param name="sequence"father_id_seq</param>
       </generator>
   </id>
   <property name="name" type="string"/>
   <many-to-one name="note" class="Note" column="id" 
                unique="true" insert="false" update="false"
                cascade="all"/>
</class>

<class name="Note" table="note">
   <id name="id" column="id" type="int" unsaved-value="-1" >
       <generator class="foreign">
           <param name="property">owner</param>
       </generator>
   </id> 
   <property name="note" type="string"/>
   <one-to-one name="owner" class="Person" constrained="true"/>
</class>


Observe that column "id" is used twice in Person mapping.I've been
searching a way to do a lazy a "one-to-zero-or-one" relationship in
Hibernate and I finally found a way to do it using many-to-one with
unique="true". See example below:

Association: Person [1 <--> 0..1] Note

<class name="Person" table="person">
   <id name="id" column="id" type="int" unsaved-value="-1">
       <generator class="sequence">
           <param name="sequence"father_id_seq</param>
       </generator>
   </id>
   <property name="name" type="string"/>
   <many-to-one name="note" class="Note" column="id" 
                unique="true" insert="false" update="false"
                cascade="all"/>
</class>

<class name="Note" table="note">
   <id name="id" column="id" type="int" unsaved-value="-1" >
       <generator class="foreign">
           <param name="property">owner</param>
       </generator>
   </id> 
   <property name="note" type="string"/>
   <one-to-one name="owner" class="Person" constrained="true"/>
</class>


Observe that column "id" is used twice in Person mapping.
 
a join on the id would solve the problem 06 Sep 2006, 13:51 dsurber
It would be straight forward to add a join on the id column of the
foreign table. That would permit Hibernate to discover whether an
optional one-to-one object was present and have enough info to create
the proxy. I have a one-to-one where the foreign table is very large and
holds a bunch of infrequently used information. Most parent records do
not need a detail record as they do not have any of the information it
stores. Parent records are cached and heavily used. It would be a waste
to fill up memory  or table space with mostly unused detail records.

If Hibernate would generate an outer join on just the id of the detail
table when retreiving a parent, it could determine at minimal cost
whether there was a detail record or not and so know whether to create a
proxy or not. While going ahead and retrieving the whole detail record
at the same time might be faster in terms of SQL execution, it is still
the wrong thing when the detail record will most likely not be accessed
and is just using up a lot of resources. In fact it was created in the
first place to avoid using up these resources.

Douglas
 
Association: Person [1 <--> 0..1] Note does not work 17 Sep 2006, 01:43 pearl8
My 0.02รง

The eg. by Regis Pires Magalhaes

Association: Person [1 <--> 0..1] Note

is not really a [1 <--> 0..1] but more of a 1 <----> 1

I find that for a Person if you DO NOT create a note, the save goes 
through fine, but when you try to Load the Person.getNote() then:

1. a proxy is created
2. person.getNote() evaluates to not null even if there are no notes for 
a person
3. person.getNote().getNoteDetails() or some such method bombs with an 
Exception:org.hibernate.ObjectNotFoundException: No row with the given 
identifier exists

In my experiments, rather, if this is modeled as a many-to-one from 
Person to Note with a NOTE_ID being stored in the Person Table, then 
that approach is much more conducive to a unidirectional Person [1 <--> 
0..1] Note

Something like this in Person:

        <many-to-one name="note" class="Note" column="NOTE_ID" 
unique="true" cascade="all"/>   

With this in Note:

  <class name="Note" table="NOTE">

	<id name="id" column="NOTE_ID" type="long">
	 <generator class="increment"></generator>
	</id>
 
emnulating constrained="true" in JPA 08 Aug 2007, 17:08 mojavelinux
It doesn't appear to be possible to emulate constrained="true" in JPA.
This feature requires that you use the Hibernate mappings.
 
But a SELECT to check presence is just inefficient because the s 11 Sep 2007, 16:35 wren337
> But a SELECT to check presence is just inefficient because the same
> SELECT may not just check presence, but load entire object. So lazy
> loading goes away.

This is hugely wrong.  Imagine a table with a FK and a 20M blob of data
in each row.  Lazy loading the blob when displaying a list of rows for
example is useful, even if you have to select the PK column to find out
the rows exist.  Lazy loading has more uses than avoiding the SQL
execution.  In this case avoiding the memory overhead of creating
perhaps hundreds of M of data in memory when it's not needed.
 
© Copyright 2006, Red Hat Middleware, LLC. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc. [Privacy Policy]