entitasManager.createQuery() membutuhkan banyak waktu untuk membuat kueri dan mengikat parameter. Kinerja terpengaruh

Kami menggunakan kueri kriteria Spring JPA (javax.persistence.criteria.CriteriaQuery) untuk mengambil data dari database. Kami menggunakan javax.persistence.criteria.Predicate untuk membangun predikat. Kami memiliki 1500 predikat 'ATAU' dalam satu kueri. Dan masing-masing predikat mempunyai 6 predikat 'DAN'.

SELECT (*) FROM TABLE_ABC as T1 WHERE  (t1.column1 = 'c11' AND
   t1.column2 = 'c12' AND t1.column3 = 'c13' AND t1.column4 = 'c14' AND
   t1.column5 = 'c15') 
  OR 
   (t1.column1 = 'c21' AND t1.column2 = 'c22'
   AND t1.column3 = 'c23' AND t1.column4 = 'c24' AND t1.column5 = 'c25')
   OR 
    (t1.column1 = 'c31' AND t1.column2 = 'c32'
   AND t1.column3 = 'c33' AND t1.column4 = 'c34' AND t1.column5 = 'c35').....

Sebelumnya kami menggunakan "org.hibernate.Criteria" dan menggunakan 'Konjuksi' dan 'Disjungsi' untuk membuat kueri yang sama. Pendekatan ini bekerja secara efisien. Karena "org.hibernate.Criteria" sudah tidak digunakan lagi, kami berpindah ke paket javax-criteriaquery. Kami menghadapi penurunan kinerja yang besar. Penelusuran log menunjukkan bahwa waktu yang dikonsumsi lebih banyak dalam langkah tersebut

=> entitasManager.createQuery(), Yang melakukan operasi berikut


  1. CriteriaCompiler.kompilasi
  2. KriteriaQueryImpl$1.buildCompiledQuery
  3. Kompiler Kriteria$1$1.bind

Operasi ini lebih memakan waktu.

Apakah ada solusi untuk mempercepat eksekusi ini? Apakah 'javax.persistence.criteria.CriteriaQuery' merupakan jalan ke depan?

Tolong bantu di sini!

Silakan lihat kode di bawah ini:

@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
public getData(List<DataDAO> dataReqList) {
{

    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<DataReq> criteriaQuery = builder.createQuery(DataReq.class);
    Root<DataReq> dataReqRoot = criteriaQuery.from(DataReq.class);
    Predicate[] predicateArr = new Predicate[dataReqList.size()];

    for (DataDAO dataReq : dataReqList) {

                    predicateArr[i] = builder.and(
                            builder.equal(dataReqRoot.get(TEST_S), dataReq.getS()),
                            builder.equal(dataReqRoot.get(TEST_T2), dataReq.getT2()),
                            builder.equal(dataReqRoot.get(K1), dataReq.getK1()),
                            builder.equal(dataReqRoot.get(K2), dataReq.getK2()),
                            builder.equal(dataReqRoot.get(TEST_P), dataReq.getP()),
                            builder.equal(dataReqRoot.get(TEST_T1),
                                    dataReq.getT1(),
                            builder.equal(dataReqRoot.get(TEST_I), dataReq.getI()));

                            i++;
    }

    List<Data> dataResultList = getResultList(builder, criteriaQuery, predicateArr);

}

private List<Data> getResultList(CriteriaBuilder builder,
            CriteriaQuery<DataReq> criteriaQuery, Predicate[] predicateArr) {
    criteriaQuery.where(builder.or(predicateArr));
    TypedQuery<DataReq> query = entityManager.createQuery(criteriaQuery);

    List<DataReq> dataReqList = null;
    try {

        dataReqList = query.getResultList();
    } catch(Exception e) {
    ...
    }

    return convertToData(dataReqList);

}

Kueri yang sama dengan "org.hibernate.Criteria" dan menggunakan 'Konjuksi' dan 'Disjungsi' bekerja sangat efisien dalam milidetik.


person Sreeram    schedule 27.03.2020    source sumber
comment
Hai Simon, Terima kasih atas minatnya. Kami memiliki skenario bisnis seperti itu. Kueri yang sama dengan org.hibernate.Criteria dan menggunakan 'Konjungsi' dan 'Disjungsi' bekerja cukup efisien.   -  person Sreeram    schedule 28.03.2020


Jawaban (1)


Untuk konteksnya, bergantung pada database yang Anda gunakan, ini seperti predikat IN dinamis dengan ekspresi nilai baris. Jika didukung, Anda juga dapat menulis:

WHERE (t1.column1, t1.column2, t1.column3, t1.column4, t1.column5, t1.column6) IN (
  ('c11', 'c12', 'c13', 'c14', 'c15', 'c16'),
  ('c21', 'c22', 'c23', 'c24', 'c25', 'c26'),
  ...
)

Daftar IN yang panjang seperti itu akan menjadi masalah tidak hanya di perpustakaan klien yang menghasilkan SQL dinamis, tetapi juga di sisi server. Anda menyebutkan variabel pengikat, mungkin API lama yang Anda gunakan tidak menggunakan variabel pengikat, tetapi memasukkan semua nilai ke dalam kueri. Saya telah melihat kinerjanya jauh lebih baik di Oracle untuk kumpulan parameter yang besar, jadi ini adalah salah satu kasus di mana nilai sebaris mungkin lebih baik daripada variabel pengikat.

Karena Anda menggunakan Hibernate, Anda dapat mencoba mengaktifkannya

<property name="hibernate.criteria.literal_handling_mode" value="bind"/>

Lihat HHH-9576 dan jawaban ini

Solusi yang mungkin lebih baik menggunakan array

Hal di atas (mungkin) akan membantu memulihkan kinerja sebelumnya yang Anda alami, namun bergantung pada ukuran daftar IN Anda, mungkin ada solusi yang lebih baik. Saya telah menulis blog tentang alternatif di mana Anda dapat menggunakan array alih-alih nilai pengikatan individual, jika Anda menggunakan Oracle atau PostgreSQL.

Solusi yang mungkin lebih baik menggunakan tabel sementara

Opsi lain yang sering saya lihat berhasil adalah menggunakan tabel sementara dalam bentuk (dengan asumsi Oracle):

CREATE GLOBAL TEMPORARY TABLE predicates (
  column1 VARCHAR2(100),
  column2 VARCHAR2(100), 
  column3 VARCHAR2(100), 
  column4 VARCHAR2(100), 
  column5 VARCHAR2(100), 
  column6 VARCHAR2(100)
)

Dan kemudian, sebelum menjalankan kueri Anda, masukkan semua berbagai nilai predikat ke dalam tabel itu dan kemudian gabungkan secara semi:

WHERE (t1.column1, t1.column2, t1.column3, t1.column4, t1.column5, t1.column6) IN (
  SELECT column1, column2, column3, column4, column5, column6
  FROM predicates
)

Jika Anda tidak memiliki tabel sementara, Anda dapat mencoba tabel biasa, dan menambahkan kolom transaksi_id ke dalamnya, membersihkan isinya secara manual setelah pertanyaan Anda.

person Lukas Eder    schedule 29.03.2020