I faced the problem that one-to-one lazy loading doesn't work in hibernate. I've already solved it, but still don't properly understand what happens.
My code (lazy loading doesn't work here, when I pull Person - Address is also fetched):
@Entity public class Person{ @Id @SequenceGenerator(name = "person_sequence", sequenceName = "sq_person") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "person_sequence") @Column(name = "id") private long personID; @OneToOne(mappedBy="person", cascade=CascadeType.ALL, fetch = FetchType.LAZY) private Adress address; //.. getters, setters } @Entity public class Address { @Id @Column(name="id", unique=true, nullable=false) @GeneratedValue(generator="gen") @GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="person")) private long personID; @PrimaryKeyJoinColumn @OneToOne private FileInfo person; }
But: if I add optional=false
in OneToOne relationship, lazy loading works fine!
@OneToOne(mappedBy="person", cascade=CascadeType.ALL, optional = false, fetch = FetchType.LAZY) private Adress address;
Question/Entreaty: please, explain to me how optional=false
annotation helps to achieve lazy loading.
P.S. I've read posts post1 and post2, and understand why simple OneToOne can't be lazy, but I still can't grasp optional=false
magic.
1. optional=false is not working if you try to save the Personne without Adresse : "org.hibernate.PropertyValueException: not-null property references a null or transient value: " – Grégory. Nov 28, 2013 at 18:30. optional = false means that... the address is not optional.
optional=false is a runtime instruction. The primary functional thing it does is related to Lazy Loading.
1. Working with lazy associations. By default, Hibernate uses lazy select fetching for collections and lazy proxy fetching for single-valued associations. These defaults make sense for most associations in the majority of applications.
You just have to set the fetch attribute of the @OneToOne association to FetchType. LAZY. But if you model this as a bidirectional association, you will quickly recognize that Hibernate always fetches the other end of the association eagerly.
If the association is optional, Hibernate has no way of knowing 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 address referencing the person, and it can't populate it with null
, because there might be an address referencing the person.
When you make the association 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With