Saya kesulitan memaksa Hibernate untuk menggunakan gabungan antara dua tabel saat mengambil data. Kami memiliki tabel induk besar dengan tabel anak besar, bergabung dengan beberapa kolom (tidak ada kolom ID tunggal). Saat kami mengambil catatan induk, Hibernate dengan bersemangat mengambil anak-anak, tetapi melakukannya dengan kueri terpisah, satu untuk setiap induk. Ini tampaknya sangat lambat dibandingkan dengan hanya satu kueri dengan gabungan di dalamnya.

Tapi bagaimana Anda membuat Hibernate melakukan join?

Berikut adalah entitas (ini hanya contoh anonim):

@Entity
@Table(name = "BOOKS")
public class Book
{
    @EmbeddedId
    private BookID id;

    @Column(name = "DESCRIPTION")
    private String description;

    @Column(name = "PUBLISH_DATE")
    private Date publishDate;

    @OneToMany(mappedBy = "book", fetch = EAGER, cascade = ALL, orphanRemoval = true)
    private List<BookReview> reviews;
}

@Entity
@Table(name = "BOOK_REVIEWS")
public class BookReview
{
    @EmbeddedId
    private BookReviewID id;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "TITLE", referencedColumnName = "TITLE"),
        @JoinColumn(name = "AUTHOR_FIRST_NAME", referencedColumnName = "AUTHOR_FIRST_NAME"),
        @JoinColumn(name = "AUTHOR_LAST_NAME", referencedColumnName = "AUTHOR_LAST_NAME")
    })
    private Book book;

    @Column(name = "UPDATE_DATE")
    private Date updateDate;
}

... dan kunci yang disematkan:

@Embeddable
public class BookID
{
    @Column(name = "TITLE")
    private String title;

    @Column(name = "AUTHOR_FIRST_NAME")
    private String authorFirstName;
    
    @Column(name = "AUTHOR_LAST_NAME")
    private String authorLastName;
}

@Embeddable
public class BookReviewID extends BookID
{
    @Column(name = "USERNAME")
    private String userName;
}

Saat saya menjalankan kueri untuk semua rekaman di BOOK (menggunakan entityManager.createQuery("from Book")), itu menjalankan kueri pada tabel BUKU untuk mengambil semua rekaman, lalu memisahkan kueri untuk ulasan setiap rekaman< /em>. Daripada kueri terpisah untuk setiap kumpulan ulasan untuk setiap buku, sepertinya akan jauh lebih efisien untuk melakukan left outer join pada awalnya untuk mendapatkannya.

Apakah ini bahkan mungkin melalui anotasi? Atau haruskah saya menggunakan CriteriaBuilder atau bahasa kueri?

Saya harus mencatat bahwa menambahkan @Fetch(FetchMode.JOIN) tidak mengubah apa pun pada apa yang tampaknya dilakukan di balik selimut.

1
Depressio 27 Mei 2021, 01:46

1 menjawab

Jawaban Terbaik

Hibernasi dokumentasi sangat menyarankan untuk menggunakan fetch = FetchType.LAZY daripada fetch = FetchType.EAGER:

Jika Anda lupa JOIN FETCH semua EAGER asosiasi, Hibernate akan mengeluarkan pilihan sekunder untuk masing-masing dan setiap asosiasi yang, pada gilirannya, dapat menyebabkan masalah kueri N+1.

Untuk alasan ini, Anda harus memilih asosiasi MALAS.

Jadi, saya akan menyarankan Anda untuk memperbaiki asosiasi ini:

@OneToMany(mappedBy = "book", fetch = EAGER, cascade = ALL, orphanRemoval = true)
private List<BookReview> reviews;

Untuk ini:

@OneToMany(mappedBy = "book", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookReview> reviews;

Dan kemudian timpa kemalasan asosiasi yang bergabung dengan menggunakan join fetch di HQL seperti di bawah ini:

entityManager.createQuery(
  "select b from Book b join fetch b.reviews"
)

Adapun @Fetch(FetchMode.JOIN), seperti yang dinyatakan dalam rel dokumentasi:

Alasan mengapa kami tidak menggunakan kueri JPQL untuk mengambil beberapa entitas adalah karena strategi FetchMode.JOIN akan ditimpa oleh perintah pengambilan kueri.

...

Oleh karena itu, FetchMode.JOIN berguna ketika entitas diambil secara langsung, melalui pengenal atau id alaminya.

Selain itu, FetchMode.JOIN bertindak sebagai strategi FetchType.EAGER. Bahkan jika kita menandai asosiasi sebagai FetchType.LAZY, FetchMode.JOIN akan memuat asosiasi dengan penuh semangat.

Jadi, FetchMode.JOIN hanya akan berfungsi untuk pengambilan langsung seperti di bawah ini:

Book book = entityManager.find(Book.class, new BookID(...));
1
SternK 27 Mei 2021, 06:50