Skip to content

LazyLoading

Guillaume Le Cousin edited this page Apr 9, 2022 · 2 revisions

When entities are linked by Relationships, you can fetch linked entities using joins in a single select query, or you can use lazy loading.

Lazy loading (also known as asynchronous loading) is the mechanism to defer loading an entity from the database until the entity is needed.

Let's consider the example where a UserAccount is linked to a Person. When using lazy loading, you will first fetch some UserAccount entities from the database, then if at some point you need the Person entity linked to a UserAccount, this entity will be loaded from the database at this time.

The main advantages of lazy loading are:

  • If linked entities are not needed, they won't be loaded from the database
  • You can fetch a first entity without its links, and do some logic with it while the linked entity is fetching from the database

The main disadvantage is if in a process you know you will always need some linked entities, fetching them asynchronously and one by one generate several requests to the database and will ends with a greater overall process time.

To enable lazy loading features, you must declare the methods you need in your entities. Here are the possible methods:

  • a method public boolean entityLoaded() { return false; } to know if the entity instance is loaded or not.
  • a method public Mono<T> loadEntity() { return null; } where T is your class, to load the entity. If the entity is already loaded, Mono.just(this)is returned.
  • methods public Mono<T> lazyGetXXX() { return null; } to get a loaded entity on a @ForeignKey or @ForeignTable field XXX, where T is the type of the field.
  • For collections, the same method can be declared with a Flux: public Flux<T> lazyGetXXX() { return null; } to get entities from a collection with @ForeignTable.

The body of those methods will be automatically generated during enhancement (when calling LcReactiveDataRelationalInitializer.init() at startup), so you can just leave it as simple as possible.

Using methods lazyGetXXX or loadEntity allow to ensure the entity or attributes are loaded. If already loaded, nothing is done, else a database request is done. That's why a Mono or Flux is returned, so you can perform your actions in a non-blocking mode even if a database request needs to be executed before.

Note that if you call several time the same method, only one database request will be generated.

In case an entity or attribute is not loaded, and you are not using those methods, here is the behavior you can expect:

  • On a @ForeignKey attribute, a class will be instantiated with the foreign key as id, all other attributes are not set. That means your foreign key attribute is not null (using classical getter) but all its attributes are null except its primary key which is pre-filled.
  • On a @ForeignTable attribute, the attribute will be null.

Note that all those methods are completely optional. If you define a method the enhancer will generate its code, else the method will just not be available.

Here is an example:

@Table
public class Book {
  @Id @GeneratedValue
  private Long id;

  @ForeignKey
  private Publisher publisher;
  
  public boolean entityLoaded() {
    return false; // correct code will be generated
  }
  
  public Mono<Book> loadEntity() {
    return null; // correct code will be generated
  }
  
  public Mono<Publisher> lazyGetPublisher() {
    return null; // correct code will be generated
  }
}

@Table
public class Publisher {
  @Id @GeneratedValue
  private Long id;
  
  @ForeignTable(joinKey = "publisher")
  private Set<Book> books;
  
  public boolean entityLoaded() {
    return false; // correct code will be generated
  }
  
  public Mono<Publisher> loadEntity() {
    return null; // correct code will be generated
  }
  
  public Flux<Book> lazyGetBooks() {
    return null; // correct code will be generated
  }
}