Friday, July 4, 2014

ONE TO ONE and Lazy VS Eager Loading

Use
1.        <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
1.        class A {  
2.            private Set bees;  
3.            public Set getBees() {  
4.                return bees;  
5.            }  
6.          
7.            public void setBees(Set bees) {  
8.                this.bees = bees;  
9.            }  
10.       
11.     }  
12.       
13.     class B {  
14.         // Not important really  
15.     }  
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
1.        class B {  
2.            private C cee;  
3.          
4.            public C getCee() {  
5.                return cee;  
6.            }  
7.          
8.            public void setCee(C cee) {  
9.                this.cee = cee;  
10.         }  
11.     }  
12.       
13.     class C {  
14.         // Not important really  
15.     }  

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 (constrained=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.


Workaround1 : - Just add annotation or entry in hdm file for
@JoinColumn for reference private Address address;.

Workaround2 :- add optional=false in OneToOne relationship
If the association is optional, Hibernate has no way to know if an address exists for a given person without issuing a query. So it can't populate the address field with a proxy, because there could be no addres referencing the person, and it can't populate it with null, because there might be an address referencing the person.
When you make the associatio mandatory (i.e. optional=false), it trusts you and assumes that an address exists, since the association is mandatory. So it directly populates the address field with a proxy, knowing that there is an address referencing the person.


Other solutions for this problem:
The simplest one is to fake one-to-many relationship. This will work because lazy loading of collection is much easier then lazy loading of single nullable property but generally this solution is very inconvenient if you use complex JPQL/HQL queries.
The other one is to use build time bytecode instrumentation. For more details please read Hibernate documentation: 19.1.7. Using lazy property fetching. Remember that in this case you have to add @LazyToOne(LazyToOneOption.NO_PROXY) annotation to one-to-one relationship to make it lazy. Setting fetch to LAZY is not enough.

The last solution is to use runtime bytecode instrumentation but it will work only for those who use Hibernate as JPA provider in full-blown JEE environment (in such case setting "hibernate.ejb.use_class_enhancer" to true should do the trick: Entity Manager Configuration) or use Hibernate with Spring configured to do runtime weaving (this might be hard to achieve on some older application servers). In this case @LazyToOne(LazyToOneOption.NO_PROXY) annotation is also required.

This will work for you.

1 comment:

  1. Honestly speaking this blog is absolutely amazing in learning the subject that is building up the knowledge of every individual and enlarging to develop the skills which can be applied in to practical one. Finally, thanking the blogger to launch more further too.

    Java Training in Chennai

    Java Course in Chennai

    ReplyDelete