Spring boot Hibernate Basic 4 Memahami Lazy dan Eager Fetch dalam Spring Boot dan Hibernate

    


    Hibernate dikembangkan dengan teknik Object-Relational-Mapping (ORM). Sisi lain Java Persistence API (JPA), mempunyai 2 metod yaitu LAZY dan EAGER (javax.persistence.FetchType). Ketika model ORM yang mempunyai relasi dengan model lain akan melakukan loading / fetching terhadap objek relasi tersebut. Disinilah peran fetch lazy dan eager akan penting. Artikel ini akan membahas apa itu fetch lazy dan eager, contoh code dalam project spring boot. Serta, kapan sebaiknya digunakan 2 tipe fetch ini.

Lazy

Apa itu 

    Lazy Fetch adalah strategi pengambilan data dalam Object-Relational Mapping (ORM) yang menunda pengambilan data terkait hingga data tersebut benar-benar diperlukan. Dalam konteks ORM seperti Hibernate yang digunakan bersama dengan Spring Boot, Lazy Fetch memungkinkan entitas terkait untuk tidak dimuat dari basis data sampai akses ke entitas tersebut dilakukan.

Kapan Lazy Fetch sebaiknya digunakan

Lazy Fetch sangat berguna ketika Anda memiliki relasi yang tidak selalu diperlukan dalam setiap operasi dan hanya diakses dalam situasi tertentu. Ini membantu mengurangi beban pada basis data dan meningkatkan efisiensi aplikasi secara keseluruhan. Namun, penting untuk memahami pola akses data Anda untuk menghindari masalah kinerja yang disebabkan oleh query tambahan yang tidak diinginkan.

Model dan Repository

Book.java 
 
@Entity
@Data
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String ISBN;
    
    private String title;
    
    private Integer totalPage;
    
    private String category;
    
    @ManyToOne(fetch = FetchType.LAZY)
    private Author author;
} 

Author.java
 
@Entity
@Data
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    private String country;
} 

BookRepository
 
@Repository
public interface BookRepository extends JpaRepository<Book, Long>{
    @Query("select b from Book b left join fetch b.author where b.id = :id")
    public Book getBookFetch(Long id);
} 


Pada model Book terdapat relasi ke model Author, disitu tertulis @ManyToOne(fetch = FetchType.LAZY) menandakan pada saat process Fetch data ke Author menggunakan lazy. 

Service

BookService.java
 
@Service
public class BookService {
    @Autowired
    private AuthorRepository authorRepository;
    
    @Autowired
    private BookRepository bookRepository;
    
    public Book get(Long bookId) {
        return bookRepository.getReferenceById(bookId);
    }
    
    public void store(Book book) {
        if(book.getAuthor() != null) {
            authorRepository.save(book.getAuthor());
        }
        bookRepository.save(book);
    }
} 

LibraryService.java
 
@Service
public class LibraryService {
    @Autowired
    private BookRepository bookRepository;
    
    public Book getBook(Long bookId) {
        return bookRepository.findById(bookId).orElse(null);
    }
    
    public Book getBookFetch(Long bookId) {
        return bookRepository.getBookFetch(bookId);
    }
} 

Pada libraryService terdapat 2 method yaitu getBook dan getBookFetch. Jika kita menggunakan getBook kemudian mengakses komponen didalamnya akan menyebabkan error LazyException. Untuk mengatasinya digunakan method getBookFetch, yang pada BookRepository melakukan query left join fetch ke Author. Untuk lebih jelas bisa perhatikan contoh di bawah.

Test dan Check Query yang terbentuk 

jika kita menggunakan code ini
 
        System.out.println("========= START LAZY ==========");
        BookService bookService = applicationContext.getBean(BookService.class);
        runBook(bookService);
        LibraryService libraryService = applicationContext.getBean(LibraryService.class);
        runLibrary(libraryService);
        System.out.println("========= END LAZY =========="); 

maka result yang terbentuk 
 
========= START LAZY ==========
Hibernate: 
    insert 
    into
        author
        (country, name, id) 
    values
        (?, ?, default)
Hibernate: 
    insert 
    into
        book
        (isbn, author_id, category, title, total_page, id) 
    values
        (?, ?, ?, ?, ?, default)
Hibernate: 
    select
        b1_0.id,
        b1_0.isbn,
        b1_0.author_id,
        b1_0.category,
        b1_0.title,
        b1_0.total_page 
    from
        book b1_0 
    where
        b1_0.id=?
whoops could not initialize proxy [com.sufyan97.learn_hibernate.book.Author#1] - no Session
Hibernate: 
    select
        b1_0.id,
        b1_0.isbn,
        a1_0.id,
        a1_0.country,
        a1_0.name,
        b1_0.category,
        b1_0.title,
        b1_0.total_page 
    from
        book b1_0 
    left join
        author a1_0 
            on a1_0.id=b1_0.author_id 
    where
        b1_0.id=?
J. K. Rowling
========= END LAZY ========== 

Pembahasan Hasil

  • Jika diperhatikan query yang terbentuk setelah insert hanya melakukan query pada tabel utama saja. Sehingga, memori yang digunakan jauh lebih hemat dan tidak terlalu membebani database.
  • Ketika object relasi lazy diambil komponennya akan melempar error LazyInitializationException
  • Untuk mengakalinya bisa digunakan query left join fetch seperti yang terbentuk pada method getBookFetch.

Eager

Apa itu 

    Eager Fetch adalah strategi pengambilan data dalam Object-Relational Mapping (ORM) di mana semua data yang terkait diambil sekaligus, bahkan jika data tersebut tidak segera diperlukan. Dalam konteks Hibernate dan Java Persistence API (JPA) yang digunakan bersama dengan Spring Boot, Eager Fetch memungkinkan entitas terkait untuk dimuat langsung saat entitas utama diambil dari basis data. Hal ini sangat berlawanan dengan Lazy Fetch yang tidak mengambil data relasinya.

Kapan Eager Fetch sebaiknya digunakan

    Eager Fetch sangat berguna ketika Anda sering membutuhkan semua relasi saat mengakses entitas utama. Ini memastikan bahwa semua data yang diperlukan tersedia segera, mengurangi kebutuhan untuk query tambahan. Namun, ini harus digunakan dengan hati-hati untuk menghindari overfetching dan konsumsi memori yang berlebihan. Selain itu jika kita sangat "malas" berurusan dengan LazyInitializationException bisa gunakan cara ini. Selain itu query yang digunakan jauh lebih ringkas dibandingkan lazy fetch. Jika join data yang digunakan kecil juga lebih optimal dengan menggunakan Eager fetch.

Model dan Repository 

Buku.java
 
@Entity
@Data
public class Buku {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String ISBN;
    
    private String title;
    
    private Integer totalPage;
    
    private String category;
    
    @ManyToOne(fetch = FetchType.EAGER)
    private Penulis penulis;
} 

Penulis.java
 
@Entity
@Data
public class Penulis {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    private String country;
} 

BukuRepository.java
 
@Repository
public interface BukuRepository extends JpaRepository<Buku, Long>{
    @Query("select b from Buku b left join fetch b.penulis where b.id = :id")
    public Buku getBukuFetch(Long id);
} 

Service

BukuService.java
 
@Service
public class BukuService {
    @Autowired
    public BukuRepository bukuRepository;
    
    @Autowired
    public PenulisRepository penulisRepository;
    
    public Buku get(Long bookId) {
        return bukuRepository.getReferenceById(bookId);
    }
    
    public void store(Buku buku) {
        if(buku.getPenulis() != null) {
            penulisRepository.save(buku.getPenulis());
        }
        bukuRepository.save(buku);
    }
} 

PerpustakaanService.java
 
@Service
public class PerpustakaanService {
    @Autowired
    public BukuRepository bukuRepository;
    
    public Buku getBook(Long bookId) {
        return bukuRepository.findById(bookId).orElse(null);
    }
    
    public Buku getBukuFetch(Long bookId) {
        return bukuRepository.getBukuFetch(bookId);
    }
} 

    Pada libraryService terdapat 2 method yaitu getBook dan getBookFetch. Jika kita menggunakan getBook kemudian mengakses komponen didalamnya akan menyebabkan error LazyException. Untuk mengatasinya digunakan method getBookFetch, yang pada BookRepository melakukan query left join fetch ke Author. Untuk lebih jelas bisa perhatikan contoh di bawah.

Test dan Check Query yang terbentuk 

Jika digunakan baris kode ini untuk melakukan test 
 
System.out.println("========= START EAGER ==========");
BukuService bukuService = applicationContext.getBean(BukuService.class);
runBuku(bukuService);
PerpustakaanService perpustakaanService = applicationContext.getBean(PerpustakaanService.class);
runPerpustakaan(perpustakaanService);
System.out.println("========= END EAGER =========="); 

Maka akan menghasilkan
 
========= START EAGER ==========
Hibernate: 
    insert 
    into
        penulis
        (country, name, id) 
    values
        (?, ?, default)
Hibernate: 
    insert 
    into
        buku
        (isbn, category, penulis_id, title, total_page, id) 
    values
        (?, ?, ?, ?, ?, default)
Hibernate: 
    select
        b1_0.id,
        b1_0.isbn,
        b1_0.category,
        p1_0.id,
        p1_0.country,
        p1_0.name,
        b1_0.title,
        b1_0.total_page 
    from
        buku b1_0 
    left join
        penulis p1_0 
            on p1_0.id=b1_0.penulis_id 
    where
        b1_0.id=?
Hibernate: 
    select
        b1_0.id,
        b1_0.isbn,
        b1_0.category,
        p1_0.id,
        p1_0.country,
        p1_0.name,
        b1_0.title,
        b1_0.total_page 
    from
        buku b1_0 
    left join
        penulis p1_0 
            on p1_0.id=b1_0.penulis_id 
    where
        b1_0.id=?
J. K. Rowling
========= END EAGER ========== 

Pembahasan hasil Eager

  • Jika diperhatikan hasil query yang dihasilkan getBukuFetch dan getBook menghasilkan query yang sama.
  • Ketika komponen object Penulis diambil tidak menyebabkan LazyInitializationException.

Sumber

Posting Komentar untuk "Spring boot Hibernate Basic 4 Memahami Lazy dan Eager Fetch dalam Spring Boot dan Hibernate"