How can I make a JPA OneToOne relation lazy How can I make a JPA OneToOne relation lazy java java

How can I make a JPA OneToOne relation lazy


First off, some clarifications to KLE's answer:

  1. Unconstrained (nullable) one-to-one association is the only one that can not be proxied without bytecode instrumentation. The reason for this is that owner entity MUST know whether association property should contain a proxy object or NULL and it can't determine that by looking at its base table's columns due to one-to-one normally being mapped via shared PK, so it has to be eagerly fetched anyway making proxy pointless. Here's a more detailed explanation.

  2. many-to-one associations (and one-to-many, obviously) do not suffer from this issue. Owner entity can easily check its own FK (and in case of one-to-many, empty collection proxy is created initially and populated on demand), so the association can be lazy.

  3. Replacing one-to-one with one-to-many is pretty much never a good idea. You can replace it with unique many-to-one but there are other (possibly better) options.

Rob H. has a valid point, however you may not be able to implement it depending on your model (e.g. if your one-to-one association is nullable).

Now, as far as original question goes:

A) @ManyToOne(fetch=FetchType.LAZY) should work just fine. Are you sure it's not being overwritten in the query itself? It's possible to specify join fetch in HQL and / or explicitly set fetch mode via Criteria API which would take precedence over class annotation. If that's not the case and you're still having problems, please post your classes, query and resulting SQL for more to-the-point conversation.

B) @OneToOne is trickier. If it's definitely not nullable, go with Rob H.'s suggestion and specify it as such:

@OneToOne(optional = false, fetch = FetchType.LAZY)

Otherwise, if you can change your database (add a foreign key column to owner table), do so and map it as "joined":

@OneToOne(fetch = FetchType.LAZY)@JoinColumn(name="other_entity_fk")public OtherEntity getOther()

and in OtherEntity:

@OneToOne(mappedBy = "other")public OwnerEntity getOwner()

If you can't do that (and can't live with eager fetching) bytecode instrumentation is your only option. I have to agree with CPerkins, however - if you have 80!!! joins due to eager OneToOne associations, you've got bigger problems then this :-)


To get lazy loading working on nullable one-to-one mappings you need to let hibernate do compile time instrumentation and add a @LazyToOne(value = LazyToOneOption.NO_PROXY) to the one-to-one relation.

Example Mapping:

@OneToOne(fetch = FetchType.LAZY)  @JoinColumn(name="other_entity_fk")@LazyToOne(value = LazyToOneOption.NO_PROXY)public OtherEntity getOther()

Example Ant Build file extension (for doing the Hibernate compile time instrumentation):

<property name="src" value="/your/src/directory"/><!-- path of the source files --> <property name="libs" value="/your/libs/directory"/><!-- path of your libraries --> <property name="destination" value="/your/build/directory"/><!-- path of your build directory --> <fileset id="applibs" dir="${libs}">   <include name="hibernate3.jar" />   <!-- include any other libraries you'll need here --> </fileset> <target name="compile">   <javac srcdir="${src}" destdir="${destination}" debug="yes">     <classpath>       <fileset refid="applibs"/>     </classpath>   </javac> </target> <target name="instrument" depends="compile">   <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">     <classpath>       <fileset refid="applibs"/>     </classpath>   </taskdef>   <instrument verbose="true">     <fileset dir="${destination}">       <!-- substitute the package where you keep your domain objs -->       <include name="/com/mycompany/domainobjects/*.class"/>     </fileset>   </instrument> </target>


Unless you are using Bytecode Enhancement, you cannot fetch lazily the parent-side @OneToOne association.

However, most often, you don't even need the parent-side association if you use @MapsId on the client side:

@Entity(name = "PostDetails")@Table(name = "post_details")public class PostDetails {     @Id    private Long id;     @Column(name = "created_on")    private Date createdOn;     @Column(name = "created_by")    private String createdBy;     @OneToOne(fetch = FetchType.LAZY)    @MapsId    private Post post;     public PostDetails() {}     public PostDetails(String createdBy) {        createdOn = new Date();        this.createdBy = createdBy;    }     //Getters and setters omitted for brevity}

With @MapsId, the id property in the child table serves as both Primary Key and Foreign Key to the parent table Primary Key.

So, if you have a reference to the parent Post entity, you can easily fetch the child entity using the parent entity identifier:

PostDetails details = entityManager.find(    PostDetails.class,    post.getId());

This way, you won't have N+1 query issues that could be caused by the mappedBy @OneToOne association on the parent side.