บทความนี้อธิบายวิธีการทำงานของแบตช์ไฮเบอร์เนต แบ่งปันสำหรับการอ้างอิงของคุณดังนี้:
การประมวลผลแบทช์ไฮเบอร์เนต
Hibernate ดำเนินการฐานข้อมูลอย่างสมบูรณ์ในวิธีที่มุ่งเน้นวัตถุ เมื่อวัตถุถาวรดำเนินการในลักษณะที่มุ่งเน้นวัตถุในโปรแกรมมันจะถูกแปลงโดยอัตโนมัติเป็นการดำเนินการบนฐานข้อมูล ตัวอย่างเช่นเมื่อการโทรของเซสชันการลบ () วิธีการลบวัตถุถาวรไฮเบอร์เนตจะรับผิดชอบในการลบบันทึกข้อมูลที่เกี่ยวข้อง เมื่อดำเนินการวิธีการตั้งค่าของวัตถุถาวร Hibernate จะแปลงเป็นวิธีการอัปเดตที่สอดคล้องกันโดยอัตโนมัติและแก้ไขระเบียนที่สอดคล้องกันของฐานข้อมูล
คำถามคือถ้า 100,000 ระเบียนจำเป็นต้องได้รับการปรับปรุงในเวลาเดียวกันคุณจำเป็นต้องโหลด 100,000 บันทึกทีละหนึ่งแล้วเรียกวิธีการตั้งค่า - นี่ไม่เพียงยุ่งยาก แต่ยังประสิทธิภาพของการเข้าถึงข้อมูลนั้นแย่มาก สำหรับสถานการณ์การประมวลผลชุดนี้ Hibernate ให้บริการโซลูชันการประมวลผลแบบแบทช์ ต่อไปนี้แนะนำวิธีการเผชิญหน้ากับสถานการณ์การประมวลผลแบทช์นี้จากสามด้าน: การแทรกชุดการอัปเดตแบทช์และการลบแบบแบทช์
1 ชุดแทรก
หากคุณต้องการแทรก 100,000 ระเบียนลงในฐานข้อมูล Hibernate อาจใช้สิ่งต่อไปนี้:
เซสชัน = sessionFactory.opensession (); ธุรกรรม tx = session.beginTransaction (); สำหรับ (int i = 0; i <100000; i ++) {ผู้ใช้ u = ผู้ใช้ใหม่ (...... ); session.save (ลูกค้า);} tx.commit (); session.close ();แต่เมื่อโปรแกรมนี้ทำงานมันจะล้มเหลวเสมอในบางจุดและ outofmemoryexception จะถูกโยนทิ้ง นี่เป็นเพราะเซสชันของ Hibernate ถือแคชระดับ 1 ที่ต้องการและอินสแตนซ์ของผู้ใช้ทั้งหมดจะถูกแคชในพื้นที่แคชระดับเซสชัน
เพื่อแก้ปัญหานี้มีแนวคิดที่ง่ายมาก: รีเฟรชข้อมูลที่แคชโดยเซสชันลงในฐานข้อมูลแทนที่จะแคชไว้ในระดับเซสชันเสมอ คุณสามารถพิจารณาออกแบบตัวสะสมซึ่งเพิ่มขึ้น 1 สำหรับผู้ใช้แต่ละคนที่บันทึกไว้ ตรวจสอบว่าข้อมูลในแคชเซสชันจะต้องถูกล้างลงในฐานข้อมูลตามค่าสะสมหรือไม่
นี่คือตัวอย่างของรหัสที่เพิ่มอินสแตนซ์ผู้ใช้ 100,000 ราย:
Private Void Testuser () โยนข้อยกเว้น {// เปิดเซสชันเซสชันเซสชัน = hibernateutil.currentsession (); // เริ่มธุรกรรมธุรกรรม tx = session.beginTransaction (); // วนซ้ำ 100 000 ครั้งและแทรก 100 000 ระเบียนสำหรับ (int i = 0; i <10,0000000; i ++) {// สร้างผู้ใช้อินสแตนซ์ผู้ใช้ U1 = ผู้ใช้ใหม่ (); u1.setName ("xxxxx" + i); u1.setage (i); U1.SetNationality ("จีน"); // cach เซสชันอินสแตนซ์ของผู้ใช้ Save (U1); // เมื่อใดก็ตามที่ตัวสะสมเป็นหลาย 20 ให้ล้างข้อมูลในเซสชันลงในฐานข้อมูลและล้างแคชเซสชันถ้า (i % 20 == 0) {session.flush (); session.clear (); tx.commit (); tx = session.beginTransaction (); }} // ส่งธุรกรรม tx.Commit (); // ปิดธุรกรรม Hibernateutil.Closesession ();}ในรหัสข้างต้นเมื่อ I%20 == 0 ข้อมูลแคชในเซสชันจะถูกเขียนลงไปยังฐานข้อมูลด้วยตนเองและการทำธุรกรรมจะถูกส่งด้วยตนเอง หากการทำธุรกรรมไม่ได้ส่งข้อมูลจะยังคงถูกแคชในสำนักงานธุรกรรม - มันไม่ได้ป้อนฐานข้อมูลซึ่งจะทำให้เกิดข้อยกเว้นของหน่วยความจำล้น
นี่คือการประมวลผลแคชระดับเซสชันและแคชรองของ SessionFactory ควรถูกปิดผ่านการกำหนดค่าต่อไปนี้
hibernate.cache.use_second_level_cache false
หมายเหตุ: นอกเหนือจากการล้างแคชระดับเซสชันด้วยตนเองแล้วยังเป็นการดีที่สุดที่จะปิดแคชทุติยภูมิ SessionFactory ระดับ มิฉะนั้นแม้ว่าแคชระดับเซสชันจะถูกล้างด้วยตนเอง แต่อาจมีการโยนข้อยกเว้นเนื่องจากยังมีแคชในระดับเซสชัน
การอัปเดต 2 ชุด
วิธีการที่อธิบายไว้ข้างต้นยังเหมาะสำหรับข้อมูลการอัปเดตแบบแบทช์ หากคุณต้องการส่งคืนข้อมูลหลายแถวคุณสามารถใช้เมธอด Scroll () เพื่อใช้ประโยชน์จากข้อได้เปรียบด้านประสิทธิภาพที่นำโดยเคอร์เซอร์ฝั่งเซิร์ฟเวอร์ นี่คือตัวอย่างโค้ดสำหรับการอัปเดตแบตช์:
Private Void Testuser () โยนข้อยกเว้น {// เปิดเซสชันเซสชันเซสชัน = hibernateutil.currentsession (); // เริ่มธุรกรรมธุรกรรม tx = session.beginTransaction (); // สอบถามบันทึกทั้งหมดในตารางผู้ใช้ scrollablebrolleresults users = session.createquery ("จากผู้ใช้") .setCachemode (cachemode.ignore) .scroll (ScrollMode.forward_only); จำนวน int = 0; // การทำธุรกรรมบันทึกทั้งหมดในตารางผู้ใช้ในขณะที่ (users.next ()) {ผู้ใช้ u = (ผู้ใช้) users.get (0); U.SetName ("ชื่อผู้ใช้ใหม่" + นับ); // เมื่อนับเป็นหลาย 20 ให้ล้างผลลัพธ์ที่อัปเดตจากเซสชันไปยังฐานข้อมูลถ้า (++ นับ % 20 == 0) {session.flush (); session.clear (); }} tx.Commit (); Hibernateutil.Closesession ();} ด้วยวิธีนี้แม้ว่าการอัปเดตแบทช์สามารถทำได้ แต่เอฟเฟกต์ก็แย่มาก ประสิทธิภาพการดำเนินการไม่สูงและจำเป็นต้องมีการสืบค้นข้อมูลก่อนจากนั้นทำการอัปเดตข้อมูล การอัปเดตนี้จะเป็นการอัปเดตแบบแถวต่อบรรทัดนั่นคือทุกครั้งที่มีการอัพเดทบันทึกแถวจะต้องดำเนินการคำสั่งการอัปเดตและประสิทธิภาพต่ำมาก
เพื่อหลีกเลี่ยงสิ่งนี้ Hibernate ให้ไวยากรณ์ HQL คล้ายกับ SQL สำหรับการอัปเดตแบตช์และการลบแบบแบทช์
3 SQL-style Batch Update/Deletion
คำสั่ง HQL ที่จัดทำโดย Hibernate ยังรองรับการอัปเดตแบทช์และลบไวยากรณ์
รูปแบบไวยากรณ์ของการอัปเดตแบทช์และคำสั่งลบมีดังนี้:
อัปเดต | ลบออกจาก? ClassName [Where Where_Conditions]
มีสี่คะแนนที่ควรค่าแก่การสังเกตเกี่ยวกับรูปแบบไวยากรณ์ข้างต้น:
●ในข้อจากคำหลักจากเป็นทางเลือก นั่นคือคุณไม่สามารถเขียนจากคำหลักได้เลย
●สามารถมีชื่อคลาสเดียวในประโยคจากและชื่อคลาสไม่สามารถมีนามแฝงได้
●คุณไม่สามารถใช้การเชื่อมต่อในคำสั่ง Batch HQL ไม่ได้ชัดเจนหรือโดยนัย แต่สามารถใช้งานย่อยได้ในประโยคที่
●ส่วนที่เป็นทางเลือกทั้งหมด
สมมติว่าคุณต้องใช้แบทช์เปลี่ยนแอตทริบิวต์ชื่อของอินสแตนซ์คลาสผู้ใช้คุณสามารถใช้ตัวอย่างโค้ดต่อไปนี้:
Private Void Testuser () โยนข้อยกเว้น {// เปิดเซสชันเซสชันเซสชัน = hibernateutil.currentsession (); // เริ่มธุรกรรมธุรกรรม tx = session.beginTransaction (); // กำหนดแบตช์อัพเดต HQL คำสั่งสตริง hqlupdate = "อัปเดตชื่อผู้ใช้ชื่อ =: newName"; // ดำเนินการอัปเดต int updateDentities = session.createquery (hqlupdate) .setstring ("newname", "ชื่อใหม่") .executeUpdate (); // กระทำการทำธุรกรรม tx.commit (); Hibernateutil.Closesession ();}ดังที่เห็นได้จากรหัสด้านบนไวยากรณ์นี้คล้ายกับไวยากรณ์ ExecuteUpdate ของ PreparedStatement ในความเป็นจริงการอัปเดตชุดนี้ของ HQL นั้นยืมโดยตรงจากคำสั่งอัปเดตของไวยากรณ์ SQL
หมายเหตุ: เมื่อใช้ไวยากรณ์การอัปเดตแบทช์นี้คุณมักจะต้องดำเนินการคำสั่งการอัปเดต SQL เพียงครั้งเดียวเพื่อทำการอัปเดตทั้งหมดที่ตรงกับบันทึกเงื่อนไข แต่อาจจำเป็นต้องดำเนินการหลายคำสั่งการอัปเดตหลายรายการเนื่องจากมีกรณีพิเศษเช่นการทำแผนที่การสืบทอดเช่นมีอินสแตนซ์บุคคลซึ่งมีอินสแตนซ์ย่อยของลูกค้า เมื่อแบตช์อัปเดตอินสแตนซ์ของบุคคลอินสแตนซ์ของลูกค้าจะต้องได้รับการอัปเดต หากคุณใช้กลยุทธ์การทำแผนที่ที่เข้าร่วมหรือสัดส่วน Subcclass การบันทึกข้อมูลบุคคลและลูกค้าจะถูกบันทึกไว้ในตารางที่แตกต่างกันดังนั้นอาจจำเป็นต้องมีคำสั่งอัปเดตหลายรายการ
ดำเนินการลบ HQL และใช้วิธี query.executeUpdate () ต่อไปนี้เป็นตัวอย่างโค้ดที่ลบระเบียนทั้งหมดด้านบนในครั้งเดียว:
Private Void Testuser () โยนข้อยกเว้น {// เปิดเซสชันเซสชันเซสชันเซสชัน = HibernateUtil.currentsession (); // เริ่มธุรกรรมธุรกรรม tx = session.beginTransaction (); // กำหนดชุดคำสั่งการลบ HQL สตริง hqlupdate = "ลบผู้ใช้"; // เรียกใช้การลบแบทช์ int updateDentities = session.createquery (hqlupdate) .ExecuteUpdate (); // กระทำการทำธุรกรรม tx.commit (); // ปิดเซสชัน hibernateutil.losesession ();}ส่งคืนค่าจำนวนเต็มโดยวิธี query.executeUpdate () ซึ่งเป็นจำนวนระเบียนที่ได้รับผลกระทบจากการดำเนินการนี้ ในความเป็นจริงการดำเนินงานพื้นฐานของไฮเบอร์เนตนั้นทำผ่าน JDBC ดังนั้นหากมีแบทช์ของการอัปเดตหรือลบการดำเนินการที่ถูกแปลงเป็นคำสั่งอัพเดตหลายรายการหรือลบคำสั่งวิธีการจะส่งคืนจำนวนระเบียนที่ได้รับผลกระทบจากคำสั่ง SQL ล่าสุด
ฉันหวังว่าคำอธิบายในบทความนี้จะเป็นประโยชน์กับการเขียนโปรแกรม Java ของทุกคนตามกรอบการทำงานของไฮเบอร์เนต