81

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.

3
  • Hey @Volodymyr, I have the same problem with you. I'm trying to separate a BLOB column from an entity. Parent entity has child entity. Child entity contains binary column. Parent and child are the same table so I use @OneToOne relationship. Although I used LAZY fetchType but it seems doesn't work. When I put optional=false, it works. Any explanation will be appreciated really. Commented Oct 21, 2015 at 8:35
  • @Emerald214 sorry, that was 2 years ago. Currently I'm writting JS Mobile, and can't help you Commented Oct 21, 2015 at 8:38
  • OneToOne optional = false doesn't work with CascadeType.PERSIST see: hibernate.atlassian.net/browse/HHH-9670 Commented Jul 5, 2017 at 6:51

2 Answers 2

104

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.

Sign up to request clarification or add additional context in comments.

9 Comments

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: "
optional = false means that... the address is not optional. So it's mandatory. So setting it to null throws an exception. That's quite expected.
It's possible except that it won't actually lazy-load the association. It's also possible by using LazyToOne(NO_PROXY) and instrumenting the byte-code at build time, IIRC, but I've had bad experiences with that. You'd better use a dedicated join column if the association is optional.
Yes, I've tested LazyToOne(NO_PROXY) and instrumenting the byte code at build-time but have bad experience like you ( With another lib HibernateJackson ). I've finally decide to not map the association and managed the table separately in differents DAO.
optional = false, does not work for me, it still fetches those entity eagerly. @OneToOne(fetch = FetchType.LAZY, mappedBy = "fundSeries", optional = false) private FundSeriesDetailEntity fundSeriesDetail;
|
13

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.