เกี่ยวกับปัญหาไฮเบอร์เนตที่เพื่อนร่วมงานของฉันเจอเมื่อวานนี้ มันเป็นสิ่งพื้นฐานที่สุดของการจำศีล ฉันเข้าใจว่าหลายคนประสบปัญหานี้และเป็นเรื่องปกติมาก แต่มักจะสับสนเมื่อพบปัญหา
เพื่อให้ความประทับใจลึกซึ้งยิ่งขึ้น ให้รู้ว่ามันคืออะไรและทำไมจึงเป็นเช่นนั้น
หลังจากนั้น ฉันเพียงแค่ใช้เฟรมเวิร์ก Hibernate ดั้งเดิมเพื่อทำการตรวจสอบ และเปิดคอนโซลการพิมพ์ SQL การดำเนินการ และได้ข้อสรุป:
หลักฐานก็คือในช่วงกลางของการทำธุรกรรมเดียวกัน:
1. ใช้คำสั่ง sql session.createSQLQuery(sql).executeUpdate(); เพื่อแทรก และสถานีเอาต์พุตจะพิมพ์คำสั่งการแทรก sql จากนั้นใช้คำสั่ง sql เพื่อดำเนินการ session.createSQLQuery(sql).uniqueResult() ; และคำสั่ง SQL จะถูกพิมพ์ด้วย ไม่มีปัญหาและสามารถสอบถามข้อมูลได้
2. ใช้ไฮเบอร์เนตเพื่อสรุปการดำเนินการและใช้ session.save(entity) เพื่อแทรก คอนโซลเอาต์พุตจะไม่พิมพ์คำสั่ง SQL ที่แทรกไว้ จากนั้นใช้วิธี session.get(entity,id); นอกจากนี้ยังไม่พิมพ์คำสั่งการสืบค้น SQL แต่สามารถสืบค้นข้อมูลได้ เมื่อดำเนินการคำสั่งการทำธุรกรรม คำสั่ง SQL ที่แทรกไว้จะถูกพิมพ์ออกมา
3. ใช้ session.save(entity) ของ hibernate เพื่อแทรก จากนั้นใช้คำสั่ง "HQL" เพื่อสอบถาม
4. ใช้ session.save(entity) ของ hibernate เพื่อแทรก แต่คอนโซลเอาต์พุตไม่พิมพ์คำสั่ง SQL ที่แทรกออกมา จากนั้นใช้คำสั่ง sql เพื่อดำเนินการ session.createSQLQuery(sql).uniqueResult(); และคำสั่งคำสั่ง SQL จะถูกพิมพ์ เกิดปัญหาขึ้น ไม่สามารถสอบถามข้อมูลได้ ในกรณีนี้ ให้ใช้เมธอด session.flush() ดำเนินการเมธอด flush() ก่อนที่จะทำการสืบค้น และคอนโซลเอาต์พุตจะพิมพ์คำสั่ง SQL ที่แทรกออกมา หากสอบถามอีกครั้งก็จะได้ข้อมูล
หลังจากการตรวจสอบเสร็จสิ้น ผมได้ตรวจสอบข้อมูลด้านบนแล้ว เกี่ยวกับจุดที่ 4 ปรากฏบ่อยครั้งในระหว่างกระบวนการพัฒนา ผมเชื่อว่าหลายๆ คนคงเคยเจอมัน แต่หลายๆ คนก็ยังคงสับสนอยู่ นี่เป็นเพียงความประทับใจที่ลึกซึ้งยิ่งขึ้น
จากคอนโซลการพิมพ์ SQL เราจะเห็นกระบวนการดำเนินการของวิธีการบันทึกไฮเบอร์เนตพื้นฐาน:
1. ตรวจสอบว่าอินสแตนซ์ที่จะบันทึกอยู่ในสถานะถาวรหรือไม่ และหากไม่เป็นเช่นนั้น ให้วางไว้ในแคช
2. วางแผนคำสั่ง insert sql ตามอินสแตนซ์ที่จะบันทึก โปรดทราบว่ามีการวางแผนเท่านั้นและไม่ได้ดำเนินการ
3. คำสั่งแทรกที่วางแผนไว้ก่อนหน้านี้จะถูกดำเนินการเมื่อมีการทำธุรกรรม
แทนที่ tx.commit() ด้วย session.flush ในขณะนี้ ตัวควบคุมจะพิมพ์คำสั่ง insert แต่ไม่มีการเพิ่มบันทึกใหม่ลงในฐานข้อมูล
หน้าที่หลักของวิธีฟลัชคือการล้างแคชและบังคับให้ฐานข้อมูลซิงโครไนซ์กับแคชไฮเบอร์เนตเพื่อให้แน่ใจว่าข้อมูลมีความสอดคล้องกัน การดำเนินการหลักคือส่งชุดคำสั่ง SQL ไปยังฐานข้อมูลและดำเนินการคำสั่ง SQL เหล่านี้ แต่จะไม่ส่งไปยังฐานข้อมูล วิธีการส่งจะเรียกวิธีการล้างก่อนแล้วจึงส่งธุรกรรม นี่คือเหตุผลว่าทำไมบันทึกจึงไม่ถูกแทรกลงในฐานข้อมูลเมื่อเราเพิ่งเรียกฟลัช เนื่องจากการอัพเดตฐานข้อมูลจะไม่ถูกบันทึกจนกว่าธุรกรรมจะได้รับการยืนยัน เนื่องจากเมธอด commit เรียกฟลัชโดยปริยาย โดยทั่วไปแล้วเราจะไม่เรียกเมธอดฟลัชอย่างชัดเจน
นี่คือกลไกการฟลัชของการไฮเบอร์เนต ในกระบวนการอัปเดตและบันทึกออบเจ็กต์ที่ซับซ้อนบางอย่าง จำเป็นต้องพิจารณาว่าการเปลี่ยนแปลงลำดับการดำเนินการฐานข้อมูลและการฟลัชที่ล่าช้ามีผลกระทบต่อผลลัพธ์ของโปรแกรมหรือไม่ หากมีผลกระทบจริง ๆ คุณสามารถเพิ่มฟลัชในตำแหน่งที่คุณต้องรักษาลำดับการดำเนินการเพื่อบังคับให้ไฮเบอร์เนตล้างการดำเนินการที่บันทึกไว้ในแคชลงในฐานข้อมูล สิ่งนี้อาจดูไม่สวยงาม แต่มีประสิทธิภาพมาก
คำถาม: เมธอด session.save ถูกใส่ไว้ในแคช และ SQL ไม่พบการสืบค้นฐานข้อมูลโดยตรง
หลังจากเมธอด flush() แล้ว SQL ที่ดำเนินการจะถูกพิมพ์ แต่ยังไม่ได้อยู่ในฐานข้อมูล แบบสอบถาม SQL โดยตรงสามารถค้นหาข้อมูลได้
1. ข้อมูลเอนทิตีถูกเก็บไว้ที่ไหนหลังจากฟลัช()? แคชเหมือนกับเมธอด save() ดังนั้น SQL จึงไม่สามารถรับได้
2. เนื่องจากข้อมูลไม่ได้เข้าสู่ฐานข้อมูลหลังจากวิธีล้างข้อมูล SQL จึงสามารถสอบถามได้โดยตรงโดยใช้ session.createSQLQuery(sql) เพื่อสอบถาม ควรตรวจสอบโดยตรงว่าไม่ใช่ฐานข้อมูลที่ใด