Let's start with a short example:
@Entity public class Person { private Animal animal; @OneToOne(optional = false) public Animal getAnimal() { return animal; } public void setAnimals(Animal animal) { this.animal = animal; } }
@Entity public class Animal { private Person owner; @OneToOne(fetch = FetchType.LAZY, optional = true, mappedBy = "animal") public Person getOwner() { return owner; } public void setOwner(Person owner) { this.owner = owner; } }
Many developers expect that owner property of the Animal entity will not be loaded until it is accessed for the first time. This is true but only for relationships where the proxy object for the other end of the relationship can be used. In our example it won't work.
There are at least three well known 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.
But what if you don't want to modify the structure of your entities and don't want or cannot use bytecode instumentalization (there are some issues related to this).
There is one more undocumented solution. It requires some modifications in the entities code but thanks to this building and deployment process can remain untouched.
The idea is to fool Hibernate that the entity class which we want to use has been already instrumented. To do this your entity class has to implement FieldHandled or InterceptFieldEnabled interface.
@Entity public class Animal implements FieldHandled { private Person owner; private FieldHandler fieldHandler; @OneToOne(fetch = FetchType.LAZY, optional = true, mappedBy = "animal") @LazyToOne(LazyToOneOption.NO_PROXY) public Person getOwner() { if (fieldHandler != null) { return (Person) fieldHandler.readObject(this, "owner", owner); } return owner; } public void setOwner(Person owner) { if (fieldHandler != null) { this.owner = fieldHandler.writeObject(this, "owner", this.owner, owner); return; } this.owner = owner; } public FieldHandler getFieldHandler() { return fieldHandler; } public void setFieldHandler(FieldHandler fieldHandler) { this.fieldHandler = fieldHandler; } }
If you are using javassist as bytecode provider (default from Hibernate version 3.3.0.CR2) implement org.hibernate.bytecode.javassist.FieldHandled and if you are using CGLib implement net.sf.cglib.transform.impl.InterceptFieldEnabled.
Getters and setters for non-lazy properties requires no changes.
The last thing worth mentioning here is that Hibernate does not support one by one lazy properties loading. This means that if your entity class has more then one lazy properties all of them are going to be loaded during the first access to any of them (I know that this is stupid but this is how it is currently implemented).