EntityManager.createQuery() ใช้เวลามากในการสร้างแบบสอบถามและเชื่อมโยงพารามิเตอร์ ประสิทธิภาพการทำงานได้รับผลกระทบ

เรากำลังใช้แบบสอบถามเกณฑ์ Spring JPA ( javax.persistence.criteria.CriteriaQuery) เพื่อดึงข้อมูลจากฐานข้อมูล เราใช้ javax.persistence.criteria.Predicate เพื่อสร้างภาคแสดง เรามีภาคแสดง 'หรือ' 1,500 รายการในแบบสอบถามเดียว และแต่ละภาคแสดงจะมี 6 ภาคแสดง 'AND'

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').....

ก่อนหน้านี้เราใช้ "org.hibernate.Criteria" และใช้ 'Conjuction' และ 'Disjunction' เพื่อสร้างแบบสอบถามเดียวกัน แนวทางนี้ทำงานได้อย่างมีประสิทธิภาพ เนื่องจาก "org.hibernate.Criteria" ถูกตัดราคาแล้ว เราจึงย้ายไปยังแพ็คเกจ javax-criteriaquery เรากำลังเผชิญกับความเสื่อมถอยครั้งใหญ่ในด้านประสิทธิภาพ การเจาะลึกบันทึกบ่งชี้ว่ามีการใช้เวลามากขึ้นในขั้นตอนนั้น

=> enterityManager.createQuery() ซึ่งดำเนินการดังต่อไปนี้


  1. เกณฑ์Compiler.compile
  2. CriteriaQueryImpl$1.buildCompiledQuery
  3. เกณฑ์คอมไพเลอร์$1$1.ผูก

การดำเนินการเหล่านี้ใช้เวลานานกว่า

มีวิธีแก้ไขใดที่ทำให้การดำเนินการเหล่านี้เร็วขึ้นหรือไม่? 'javax.persistence.criteria.CriteriaQuery' เป็นหนทางข้างหน้าหรือไม่

โปรดช่วยที่นี่!

โปรดดูรหัสด้านล่าง:

@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);

}

ข้อความค้นหาเดียวกันกับ "org.hibernate.Criteria" และการใช้ 'Conjuction' และ 'Disjunction' จะทำงานได้อย่างมีประสิทธิภาพมากในหน่วยมิลลิวินาที


comment
สวัสดีไซมอน ขอบคุณสำหรับความสนใจ เรามีสถานการณ์ทางธุรกิจเช่นนี้ แบบสอบถามเดียวกันกับ org.hibernate.Criteria และการใช้ 'Conjunction' และ 'Disjunction' ทำงานได้อย่างมีประสิทธิภาพมาก   -  person Sreeram    schedule 28.03.2020


คำตอบ (1)


สำหรับบริบท ขึ้นอยู่กับฐานข้อมูลที่คุณใช้ นี่เป็นเหมือนเพรดิเคต IN แบบไดนามิกที่มีนิพจน์ค่าแถว หากรองรับ คุณยังสามารถเขียน:

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'),
  ...
)

รายการ IN ที่ยาวเช่นนี้จะกลายเป็นปัญหาไม่เพียงแต่ในไลบรารีไคลเอนต์ที่สร้าง SQL แบบไดนามิก แต่ยังรวมถึงฝั่งเซิร์ฟเวอร์ด้วย คุณพูดถึงตัวแปรการผูก บางที API เก่าที่คุณใช้อาจไม่ได้ใช้ตัวแปรการผูก แต่กลับอินไลน์ค่าทั้งหมดลงในแบบสอบถาม ฉันเห็นว่าทำงานได้ดีกว่ามากใน Oracle สำหรับชุดพารามิเตอร์ขนาดใหญ่ ดังนั้นนี่เป็นหนึ่งในกรณีที่ ค่าในบรรทัดอาจดีกว่าการผูกตัวแปร

เนื่องจากคุณใช้ Hibernate คุณจึงสามารถลองเปิดใช้งานได้

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

ดู HHH-9576 และ คำตอบนี้

วิธีแก้ปัญหาที่ดียิ่งขึ้นโดยใช้อาร์เรย์

ข้อมูลข้างต้นอาจ (อาจ) ช่วยฟื้นฟูประสิทธิภาพการทำงานก่อนหน้านี้ที่คุณพบ แต่อาจมีวิธีแก้ปัญหาที่ดีกว่านี้ ทั้งนี้ขึ้นอยู่กับขนาดรายการ IN ของคุณ ฉันได้บล็อกเกี่ยวกับทางเลือกอื่นที่คุณสามารถใช้ อาร์เรย์แทนค่าการเชื่อมโยงแต่ละรายการ ในกรณีที่คุณใช้ Oracle หรือ PostgreSQL

ทางออกที่ดียิ่งขึ้นโดยใช้ตารางชั่วคราว

อีกทางเลือกหนึ่งที่ฉันเห็นการทำงานบ่อยมากคือการใช้ตารางชั่วคราวของแบบฟอร์ม (สมมติว่า Oracle):

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

จากนั้น ก่อนที่จะเรียกใช้แบบสอบถาม ให้แทรกค่าเพรดิเคตต่างๆ ทั้งหมดลงในตารางนั้นเป็นกลุ่ม จากนั้นจึงรวมกึ่งเข้าด้วยกัน:

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

หากคุณไม่มีตารางชั่วคราว คุณสามารถลองใช้ตารางธรรมดาแทน และเพิ่มคอลัมน์ Transaction_id ลงไป เพื่อล้างเนื้อหาด้วยตนเองหลังจากการสืบค้นของคุณ

person Lukas Eder    schedule 29.03.2020