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

In this application we are developing, we noticed that a view was particularly slow. I profiled the view and noticed that there was one query executed by hibernate which took 10 seconds even if there only were two object in the database to fetch. All OneToMany and ManyToMany relations were lazy so that wasn't the problem. When inspecting the actual SQL being executed, I noticed that there were over 80 joins in the query.

Further inspecting the issue, I noticed that the problem was caused by the deep hierarchy of OneToOne and ManyToOne relations between entity classes. So, I thought, I'll just make them fetched lazy, that should solve the problem. But annotating either @OneToOne(fetch=FetchType.LAZY) or @ManyToOne(fetch=FetchType.LAZY) doesn't seem to work. Either I get an exception or then they are not actually replaced with a proxy object and thus being lazy.

Any ideas how I'll get this to work? Note that I do not use the persistence.xml to define relations or configuration details, everything is done in java code.

This question is related to java hibernate jpa lazy-loading one-to-one

The answer is


In native Hibernate XML mappings, you can accomplish this by declaring a one-to-one mapping with the constrained attribute set to true. I am not sure what the Hibernate/JPA annotation equivalent of that is, and a quick search of the doc provided no answer, but hopefully that gives you a lead to go on.


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.


The basic idea behing the XToOnes in Hibernate is that they are not lazy in most case.

One reason is that, when Hibernate have to decide to put a proxy (with the id) or a null,
it has to look into the other table anyway to join. The cost of accessing the other table in the database is significant, so it might as well fetch the data for that table at that moment (non-lazy behaviour), instead of fetching that in a later request that would require a second access to the same table.

Edited: for details, please refer to ChssPly76 's answer. This one is less accurate and detailed, it has nothing to offer. Thanks ChssPly76.


This question is quite old, but with Hibernate 5.1.10, there are some new better comfortable solution.

Lazy loading works except for the parent side of a @OneToOne association. This is because Hibernate has no other way of knowing whether to assign a null or a Proxy to this variable. More details you can find in this article

  • You can activate lazy loading bytecode enhancement
  • Or, you can just remove the parent side and use the client side with @MapsId as explained in the article above. This way, you will find that you don’t really need the parent side since the child shares the same id with the parent so you can easily fetch the child by knowing the parent id .

If the relation must not be bidirectional then an @ElementCollection might be easier than using a lazy One2Many collection.


If the child entity is used readonly, then it's possible to simply lie and set optional=false. Then ensure that every use of that mapped entity is preloaded via queries.

public class App {
  ...
  @OneToOne(mappedBy = "app", fetch = FetchType.LAZY, optional = false)
  private Attributes additional;

and

String sql = " ... FROM App a LEFT JOIN FETCH a.additional aa ...";

... maybe even persisting would work...


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>

Here's something that has been working for me (without instrumentation):

Instead of using @OneToOne on both sides, I use @OneToMany in the inverse part of the relationship (the one with mappedBy). That makes the property a collection (List in the example below), but I translate it into an item in the getter, making it transparent to the clients.

This setup works lazily, that is, the selects are only made when getPrevious() or getNext() are called - and only one select for each call.

The table structure:

CREATE TABLE `TB_ISSUE` (
    `ID`            INT(9) NOT NULL AUTO_INCREMENT,
    `NAME`          VARCHAR(255) NULL,
    `PREVIOUS`      DECIMAL(9,2) NULL
    CONSTRAINT `PK_ISSUE` PRIMARY KEY (`ID`)
);
ALTER TABLE `TB_ISSUE` ADD CONSTRAINT `FK_ISSUE_ISSUE_PREVIOUS`
                 FOREIGN KEY (`PREVIOUS`) REFERENCES `TB_ISSUE` (`ID`);

The class:

@Entity
@Table(name = "TB_ISSUE") 
public class Issue {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Integer id;

    @Column
    private String name;

    @OneToOne(fetch=FetchType.LAZY)  // one to one, as expected
    @JoinColumn(name="previous")
    private Issue previous;

    // use @OneToMany instead of @OneToOne to "fake" the lazy loading
    @OneToMany(mappedBy="previous", fetch=FetchType.LAZY)
    // notice the type isnt Issue, but a collection (that will have 0 or 1 items)
    private List<Issue> next;

    public Integer getId() { return id; }
    public String getName() { return name; }

    public Issue getPrevious() { return previous; }
    // in the getter, transform the collection into an Issue for the clients
    public Issue getNext() { return next.isEmpty() ? null : next.get(0); }

}

Most efficient mapping of a one-to-one association You can avoid all these problems and get rid of the foreign key column by using the same primary key value for both associated entities. You can do that by annotating the owning side of the association with @MapsId.

@Entity
public class Book {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @OneToOne(mappedBy = "book", fetch = FetchType.LAZY, optional = false)
    private Manuscript manuscript;
 
    ...
}


@Entity
public class Manuscript {
 
    @Id
    private Long id;
 
    @OneToOne
    @MapsId
    @JoinColumn(name = "id")
    private Book book;
 
    ...
}


Book b = em.find(Book.class, 100L);
Manuscript m = em.find(Manuscript.class, b.getId());

More Detail click on this url


For Kotlin devs: To allow Hibernate to inherit from the @Entity types that you want to be lazy-loadable they have to be inheritable/open, which they in Kotlin by default are not. To work around this issue we can make use of the all-open compiler plugin and instruct it to also handle the JPA annotations by adding this to our build.gradle:

allOpen {
   annotation("javax.persistence.Entity")
   annotation("javax.persistence.MappedSuperclass")
   annotation("javax.persistence.Embeddable")
}

If you are using Kotlin and Spring like me, you are most probably also using the kotlin-jpa/no-args and kotlin-spring/all-open compiler plugins already. However, you will still need to add the above lines, as that combination of plugins neither makes such classes open.

Read the great article of Léo Millon for further explanations.


As already perfectly explained by ChssPly76, Hibernate's proxies don't help with unconstrained (nullable) one-to-one associations, BUT there is a trick explained here to avoid to set up instrumentation. The idea is to fool Hibernate that the entity class which we want to use has been already instrumented: you instrument it manually in the source code. It's easy! I've implemented it with CGLib as bytecode provider and it works (ensure that you configure lazy="no-proxy" and fetch="select", not "join", in your HBM).

I think this is a good alternative to real (I mean automatic) instrumentation when you have just one one-to-one nullable relation that you want to make lazy. The main drawback is that the solution depends on the bytecode provider you are using, so comment your class accurately because you could have to change the bytecode provider in the future; of course, you are also modifying your model bean for a technical reason and this is not fine.


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to hibernate

Hibernate Error executing DDL via JDBC Statement How does spring.jpa.hibernate.ddl-auto property exactly work in Spring? Error creating bean with name 'entityManagerFactory' defined in class path resource : Invocation of init method failed JPA Hibernate Persistence exception [PersistenceUnit: default] Unable to build Hibernate SessionFactory Disable all Database related auto configuration in Spring Boot Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] HikariCP - connection is not available Hibernate-sequence doesn't exist How to find distinct rows with field in list using JPA and Spring? Spring Data JPA and Exists query

Examples related to jpa

No converter found capable of converting from type to type How does spring.jpa.hibernate.ddl-auto property exactly work in Spring? Deserialize Java 8 LocalDateTime with JacksonMapper Error creating bean with name 'entityManagerFactory' defined in class path resource : Invocation of init method failed How to beautifully update a JPA entity in Spring Data? JPA Hibernate Persistence exception [PersistenceUnit: default] Unable to build Hibernate SessionFactory How to return a custom object from a Spring Data JPA GROUP BY query How to find distinct rows with field in list using JPA and Spring? What is this spring.jpa.open-in-view=true property in Spring Boot? Spring Data JPA and Exists query

Examples related to lazy-loading

How to fix org.hibernate.LazyInitializationException - could not initialize proxy - no Session Hibernate: best practice to pull all lazy collections How to Lazy Load div background images Doctrine 2 ArrayCollection filter method How to load images dynamically (or lazily) when users scrolls them into view Entity framework linq query Include() multiple children entities How to convert a Hibernate proxy to a real entity object What is lazy loading in Hibernate? How can I make a JPA OneToOne relation lazy JQuery to load Javascript file dynamically

Examples related to one-to-one

How to Create a real one-to-one relationship in SQL Server How can I make a JPA OneToOne relation lazy