Lazy loading in Hibernate is a feature which allows Hibernate to load the dependent objects lazily to improve performance of Java application. By default Hibernate does eager or aggressive loading for single valued associations (many to one and one-to-one ) and lazy loading for collection-valued associations (one to many or many-to-many). If you set the loading to lazy=false or FetchType.EAGER then object will be eagerly loader, means when an object is loaded from database all its relationship and associated objects are also loaded from Database e.g. when an Author entity object is initialized then all the books written by this author will also be loaded into memory.
This can significantly impact performance of Java application especially if Book table is large and contains millions of books from thousands of authors and if there is no immediate need of those books.
That's its best to avoid EAGER loading with collections. You can also instruct Hibernate to lazy fetch child object by setting lazy=true in the respective hibernate mapping file of parent class, where you have defined the relationship.
This one is also a common Hibernate interview question, in case you are preparing for Java interview.
Lazy fetching in Hibernate
The lazy=true will ensure that the associated objects are not loaded unless they are explicitly invoked e.g. when a client called the getBooks() method. In this case, the Hibernate issues a fresh database call to load the related objects e.g. Books.
This is good because child objects are loaded only when you need them. But in some cases, where you want to load the related objects when the parent is loaded then just make lazy=false and Hibernate will load the associated object when the parent object is loaded from object.
Let's see an example of lazy loading in Hibernate
Lazy loading example in Hibernate
Suppose you have an AUTHOR table and a BOOK table. There is one-to-many relationship exists between AUTHOR and BOOK because one Author can write multiple books.
We have two Java classes Author and Book to map with those tables and we want to instruct Hibernate to load only Authors and not books when we call get(auhtor_id, Author.class).
Since Hibernate supports Lazy loading by default for collection-valued associations or in one to many relationships, you don't need to do anything, hibernate will not load books when you access author, but if someone has set eager loading then its a problem.
In our case, Author is parent class and Books is child or related or associated class, as seen from following class definition.
public class Author{
private Set books = new HashSet(); // contains set of associated objects
public Author(){
// no argument constructor
}
public void setBooks(Set books){
this.books = books;
}
public Set getBooks(){
return books;
}
}
In the Author.hbm.xml file we have
<set name="books" inverse="true" cascade="delete" lazy="false">
<key column="author_id" />
<one-to-many class="Book"/>
</set>
In the above configuration books are eagerly loaded when Author is loaded.
If lazy="false" : - when you load the Author object that time child object Book is also loaded and set to the Author object by calling setBooks() method. If you call author.getBook() then loaded data returns. No fresh database call is made and no time is lost.
If lazy="true" :- This the default configuration. If you don't mention then hibernate consider lazy=true.
When you load the Author object that time child object Book is not loaded. You need an extra database call to load all the associated books.
If you call author.getBooks() then that time database query fires and return results. Fresh database call is required to load the related book objects.
You can write the same code using Annotations also as shown below:
import jakarta.persistence.*;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import java.util.Set;
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// other fields...
@OneToMany(mappedBy = "author", fetch = FetchType.EAGER)
@Cascade(CascadeType.DELETE)
private Set<Book> books;
public Set getBooks(){
return books;
}
}
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "author_id")
private Author author;
// other fields, getters and setters...
}
In recent versions of Hibernate (6.x and later), we also recommend to use the Jakarta Persistence API annotations (jakarta.persistence.*) instead of the older javax.persistence.* annotations.
Also, the @Cascade annotation is a Hibernate-specific annotation, not a standard JPA annotation. If you want to use standard JPA, you can replace it with @OneToMany(cascade = CascadeType.REMOVE), but be aware that this might have slightly different behavior in some edge cases.
And, if you want to learn more about Hibernate, here is a nice diagram which shows Hibernate architecture:
That's all about lazy loading in Hibernate. Lazy loading allows Hibernate to load the related object late when they are actually needed, this saves memory and time most of the time.
Since Hibernate by default eagerly load related objects, you need to set lazy=false in your hibernate mapping file of parent class or fetch = FetchType.EAGER if you are using annotation to represent the relationship in recent Hibernate version.
I personally recommend to use lazy loading for collections to avoid performance issues, especially when dealing with large datasets. However, the best strategy depends on your specific use case and access patterns.
It's also worth to remember that while these settings provide a hint to Hibernate, the actual behavior can be influenced by other factors such as the specific queries you're using or the presence of join fetching in your JPQL/HQL queries.
No comments:
Post a Comment