บทความนี้อธิบายเทคโนโลยีการโหลดแฝงไฮเบอร์เนต แบ่งปันสำหรับการอ้างอิงของคุณดังนี้:
การโหลดขี้เกียจของ Hibernae เป็นเทคนิคที่พบได้บ่อยมาก แอตทริบิวต์การรวบรวมของเอนทิตีจะล่าช้าโดยค่าเริ่มต้นและเอนทิตีที่เกี่ยวข้องกับเอนทิตีจะล่าช้าโดยค่าเริ่มต้น Hibernate ใช้การโหลดล่าช้านี้เพื่อลดค่าใช้จ่ายหน่วยความจำของระบบดังนั้นจึงมั่นใจได้ว่าประสิทธิภาพการทำงานของไฮเบอร์เนต
ก่อนอื่นให้วิเคราะห์“ ความลับ” ของการโหลดล่าช้าไฮเบอร์เนต
การโหลดอสังหาริมทรัพย์ขี้เกียจ
เมื่อไฮเบอร์เนตเริ่มต้นเอนทิตีถาวรจากฐานข้อมูลคุณลักษณะการรวบรวมของเอนทิตีนั้นจะเริ่มต้นด้วยคลาสถาวรหรือไม่? หากแอตทริบิวต์คอลเลกชันมีบันทึก 100,000 หรือหลายล้านรายการการรวบรวมข้อมูลของคุณลักษณะการรวบรวมทั้งหมดในขณะที่การเริ่มต้นเอนทิตีถาวรจะส่งผลให้ประสิทธิภาพลดลงอย่างรวดเร็ว เป็นไปได้ทั้งหมดที่ระบบจำเป็นต้องใช้บางบันทึกในแอตทริบิวต์การรวบรวมของคลาสถาวรและไม่ใช่แอตทริบิวต์การรวบรวมทั้งหมดเลย ด้วยวิธีนี้ไม่จำเป็นต้องโหลดแอตทริบิวต์การรวบรวมทั้งหมดในครั้งเดียว
แนะนำกลยุทธ์การโหลดขี้เกียจโดยทั่วไปสำหรับคุณสมบัติการรวบรวม การโหลดล่าช้าที่เรียกว่าการโหลดข้อมูลที่เกี่ยวข้องจากฐานข้อมูลเมื่อระบบต้องการใช้แอตทริบิวต์การรวบรวม
ตัวอย่างเช่นคลาสบุคคลต่อไปนี้ถือแอตทริบิวต์การรวบรวมและองค์ประกอบในแอตทริบิวต์การรวบรวมมีที่อยู่ประเภทและรหัสตัวอย่างของคลาสบุคคลมีดังนี้:
รายการ 1. person.java
บุคคลชั้นเรียนสาธารณะ {// ระบุแอ็ตทริบิวต์ ID จำนวนเต็มส่วนตัว; // แอตทริบิวต์ชื่อบุคคลชื่อสตริงส่วนตัว; // ให้คุณลักษณะอายุของบุคคลอายุ int ส่วนตัว; // ใช้ชุดเพื่อบันทึกแอตทริบิวต์การรวบรวมส่วนตัวชุด <dront> addresses = new hashset <dront> (); // เมธอด setter และ getter ของแต่ละแอตทริบิวต์ถูกละเว้นด้านล่าง ... }เพื่อให้ไฮเบอร์เนตจัดการคุณสมบัติการรวบรวมของคลาสถาวรโปรแกรมจะให้ไฟล์การแมปต่อไปนี้สำหรับคลาสถาวร:
รายชื่อ 2. person.hbm.xml
<? xml เวอร์ชัน = "1.0" การเข้ารหัส = "gbk"?> <! doctype hibernate-mapping สาธารณะ "-// hibernate/hibernate mapping dtd 3.0 // en" "http://www.hibernate.org/dtd/hibernate-mapping-3 package = "org.crazyit.app.domain"> <!-การแม็พบุคคลที่มีอยู่ในระดับการคงอยู่-> <class name = "person" table = "person_inf"> <!-การแมปคุณสมบัติการระบุคุณสมบัติรหัส-> <id name = "id" คอลัมน์ = "person_id"> <! name = "age" type = "int"/> <!-แอตทริบิวต์การรวบรวมแผนที่-> <set name = "addresses" table = "person_address" lazy = "true"> <!-ระบุคอลัมน์คีย์ต่างประเทศที่เกี่ยวข้อง-> <คีย์คอลัมน์ = "person_id"/> <คอมโพสิต-element> <! name = "zip"/> </composite-element> </et> </class> </hibernate-mapping>
จากรหัสด้านบนที่แมปไฟล์เราจะเห็นว่าคลาสที่อยู่ในแอตทริบิวต์การรวบรวมของบุคคลเป็นเพียง pojo ปกติ คลาสที่อยู่มีสองแอตทริบิวต์: รายละเอียดและซิป เนื่องจากรหัสคลาสที่อยู่นั้นง่ายมากรหัสสำหรับคลาสนี้จึงไม่ได้รับที่นี่อีกต่อไป
รหัสในองค์ประกอบ <set .../> ในไฟล์การแมปด้านบนระบุ lazy = "true" (สำหรับ <set .../> องค์ประกอบ, lazy = "true" เป็นค่าเริ่มต้น) ซึ่งระบุว่า hibernate จะชะลอการโหลดวัตถุที่อยู่ในแอตทริบิวต์การรวบรวม
ตัวอย่างเช่นโหลดเอนทิตีบุคคลด้วย ID 1 โดยทำตามรหัสต่อไปนี้:
เซสชั่นเซสชัน = sf.getCurrentsession (); ธุรกรรม tx = เซสชัน. beginTransaction (); บุคคล p = (บุคคล) เซสชัน. get (person.class, 1); // <1> system.out.println (p.getName ());
รหัสข้างต้นเพียงต้องการเข้าถึงเอนทิตีบุคคลด้วย ID 1 และไม่ต้องการเข้าถึงวัตถุที่อยู่ที่เกี่ยวข้องกับนิติบุคคลนี้ มีสองสถานการณ์ในเวลานี้:
1. หากการโหลดไม่ล่าช้า Hibernate จะคว้าวัตถุที่อยู่ที่เกี่ยวข้องกับเอนทิตีบุคคลทันทีเมื่อโหลดบันทึกข้อมูลที่สอดคล้องกับเอนทิตีบุคคล
2. หากใช้การโหลดขี้เกียจไฮเบอร์เนตจะโหลดบันทึกข้อมูลที่สอดคล้องกับเอนทิตีของบุคคลเท่านั้น
เห็นได้ชัดว่าวิธีที่สองไม่เพียง แต่ลดการโต้ตอบกับฐานข้อมูล แต่ยังหลีกเลี่ยงค่าใช้จ่ายหน่วยความจำที่เกิดจากการโหลดเอนทิตีที่อยู่ - นี่คือเหตุผลที่ไฮเบอร์เนตเปิดใช้งานการโหลดขี้เกียจตามค่าเริ่มต้น
คำถามตอนนี้คือการโหลดขี้เกียจใช้อย่างไร? Hibernate ที่อยู่มูลค่าทรัพย์สินของเอนทิตีบุคคลคืออะไรเมื่อโหลดเอนทิตีบุคคล?
เพื่อแก้ปัญหานี้เราตั้งค่าเบรกพอยต์ที่รหัส <1> และดีบักใน Eclipse ในเวลานี้เราจะเห็นว่าหน้าต่างคอนโซล Eclipse มีเอาต์พุตดังแสดงในรูปที่ 1:
รูปที่ 1. เอาต์พุตคอนโซลสำหรับคุณสมบัติการรวบรวมการโหลดขี้เกียจ
ดังที่แสดงในผลลัพธ์ในรูปที่ 1 ไฮเบอร์เนตจะคว้าข้อมูลจากตารางข้อมูลที่สอดคล้องกับเอนทิตีของบุคคลเท่านั้นและไม่ได้คว้าข้อมูลจากตารางข้อมูลที่สอดคล้องกับวัตถุที่อยู่ นี่คือการโหลดขี้เกียจ
แล้วคุณสมบัติที่อยู่ของนิติบุคคลคืออะไร? ในเวลานี้คุณสามารถเห็นผลลัพธ์ที่แสดงในรูปที่ 2 จากหน้าต่างตัวแปรของ eclipse:
รูปที่ 2. ค่าแอตทริบิวต์การรวบรวมขี้เกียจ
จากเนื้อหาในกล่องในรูปที่ 2 จะเห็นได้ว่าคุณสมบัติที่อยู่ไม่ใช่คลาสการใช้งานที่คุ้นเคยเช่น HashSet และ TreeSet แต่เป็นคลาสการใช้งานที่ยังคงมีอยู่ซึ่งเป็นคลาสการใช้งานที่จัดทำโดย Hibernate สำหรับอินเทอร์เฟซที่กำหนด
วัตถุการรวบรวม PresententSet ไม่ได้จับข้อมูลของตารางข้อมูลพื้นฐานดังนั้นจึงเป็นไปไม่ได้ที่จะเริ่มต้นวัตถุที่อยู่ในการรวบรวม อย่างไรก็ตามคอลเลกชัน PresententSet มีแอตทริบิวต์เซสชันซึ่งเป็นเซสชันไฮเบอร์เนต เมื่อโปรแกรมจำเป็นต้องเข้าถึงองค์ประกอบการรวบรวม PresententSet เซ็ตเซ็ตจะใช้แอตทริบิวต์เซสชันนี้เพื่อคว้าบันทึกข้อมูลที่สอดคล้องกับวัตถุที่อยู่จริง
แล้วคุณจะคว้าข้อมูลข้อมูลที่สอดคล้องกับเอนทิตีที่อยู่เหล่านั้นอย่างไร นี่ไม่ใช่เรื่องยากสำหรับการคงอยู่เพราะยังมีแอตทริบิวต์ของเจ้าของในคอลเลกชัน PresentIntentSet ซึ่งระบุว่านิติบุคคลที่วัตถุที่อยู่เป็นของ Hibernate จะค้นหาข้อมูลจากตารางข้อมูลที่สอดคล้องกับที่อยู่ที่สอดคล้องกับตารางข้อมูล
ตัวอย่างเช่นเราคลิกบรรทัดที่อยู่ในหน้าต่างที่แสดงในรูปที่ 2 ซึ่งหมายความว่าเราบอก Eclipse ให้ดีบักและส่งออกแอตทริบิวต์ที่อยู่ นี่คือการเข้าถึงแอตทริบิวต์ที่อยู่ ในเวลานี้คุณสามารถดูคำสั่ง SQL ต่อไปนี้ในหน้าต่างคอนโซล Eclipse:
เลือกที่อยู่ 0_.person_id เป็น person1_0_0_, addresses0_.detail เป็นรายละเอียด 0_, ที่อยู่ 0_.zip เป็น zip0_from person_address address0_where addresses0_.person_id =?
นี่คือคอลเล็กชัน PresententSet และคำสั่ง SQL ที่จับบันทึกที่อยู่เฉพาะตามแอตทริบิวต์เจ้าของ ในเวลานี้คุณสามารถเห็นเอาต์พุตที่แสดงในรูปที่ 3 จากหน้าต่างตัวแปรของ eclipse:
รูปที่ 3. ค่าแอตทริบิวต์การรวบรวมที่โหลด
ดังที่เห็นได้จากรูปที่ 3 แอตทริบิวต์ที่อยู่ในเวลานี้ได้รับการเริ่มต้นและชุดมีวัตถุที่อยู่ 2 รายการซึ่งเป็นวัตถุที่อยู่สองรายการที่เกี่ยวข้องกับเอนทิตีบุคคล
จากการแนะนำข้างต้นเราจะเห็นว่ากุญแจสำคัญในการชะลอการโหลดแอตทริบิวต์ที่ตั้งไว้ของไฮเบอร์เนตอยู่ในคลาสการใช้งานแบบคงที่ ในระหว่างการโหลดขี้เกียจคอลเลกชัน PresententSet ไม่ได้มีองค์ประกอบใด ๆ อย่างไรก็ตาม PresententSet จะมีเซสชันไฮเบอร์เนตซึ่งสามารถมั่นใจได้ว่าเมื่อโปรแกรมต้องการเข้าถึงการรวบรวมข้อมูลบันทึกข้อมูลจะถูกโหลด "ทันที" และโหลดองค์ประกอบการรวบรวม
เช่นเดียวกับคลาสการใช้งานแบบคงที่ Hibernate ยังให้บริการ PersistentList, PersistentMap, PresententStedmentMap, StervantStentSentset และคลาสการใช้งานอื่น ๆ
ผู้อ่านที่คุ้นเคยกับแอตทริบิวต์คอลเลกชันไฮเบอร์เนตควรจำไว้ว่า: ไฮเบอร์เนตต้องการให้ประกาศแอตทริบิวต์การรวบรวมสามารถใช้กับอินเทอร์เฟซเช่นชุด, รายการ, แผนที่, SortedSet, SortedMap และอื่น ๆ เหตุผลก็คือไฮเบอร์เนตจำเป็นต้องชะลอการโหลดแอตทริบิวต์การรวบรวมและการโหลดล่าช้าของไฮเบอร์เนตขึ้นอยู่กับการคงอยู่, รายการถาวร, การถาวร, การต่อเนื่อง, การตอบโต้ map และ stantentsentedset เพื่อให้เสร็จสมบูรณ์ - นั่นคือการพัฒนาที่มีการพัฒนา
Hibernate ใช้การโหลดขี้เกียจสำหรับแอตทริบิวต์การรวบรวมโดยค่าเริ่มต้น ในบางกรณีพิเศษตั้งค่าแอตทริบิวต์ lazy = "false" สำหรับองค์ประกอบเช่น <set .../>, <list .../>, <แผนที่ .../> เพื่อยกเลิกการโหลดขี้เกียจ
การหน่วงเวลาการโหลดเอนทิตีที่เกี่ยวข้อง
โดยค่าเริ่มต้นไฮเบอร์เนตจะใช้การโหลดขี้เกียจเพื่อโหลดเอนทิตีที่เกี่ยวข้อง ไม่ว่าจะเป็นสมาคมแบบหนึ่งถึงหลายคนการเชื่อมโยงแบบหนึ่งต่อหนึ่งหรือสมาคมหลายถึงหลายคนไฮเบอร์เนตจะใช้การโหลดขี้เกียจตามค่าเริ่มต้น
สำหรับหน่วยงานที่เกี่ยวข้องพวกเขาสามารถแบ่งออกเป็นสองกรณี:
1. เมื่อเอนทิตีที่เกี่ยวข้องมีหลายเอนทิตี (รวมถึงแบบหนึ่งถึงหลายต่อหลายคน): ในเวลานี้เอนทิตีที่เกี่ยวข้องจะมีอยู่ในรูปแบบของคอลเลกชันและไฮเบอร์เนตจะใช้ชุดคงที่, persistentList, persistentMap นี่คือสถานการณ์ที่แนะนำก่อนหน้านี้
2. เมื่อเอนทิตีที่เกี่ยวข้องเป็นเอนทิตีเดียว (รวมถึงหนึ่งต่อหนึ่งและหลายต่อหนึ่ง): เมื่อไฮเบอร์เนตโหลดเอนทิตีเอนทิตีที่เกี่ยวข้องล่าช้าจะเป็นวัตถุพร็อกซีที่สร้างขึ้นแบบไดนามิก
เมื่อเอนทิตีที่เกี่ยวข้องเป็นเอนทิตีเดียวนั่นคือเมื่อเอนทิตีที่เกี่ยวข้องถูกแมปโดยใช้ <หลายต่อหนึ่ง .../> หรือ <หนึ่งต่อหนึ่ง .../> องค์ประกอบทั้งสองนี้สามารถระบุการโหลดขี้เกียจผ่านแอตทริบิวต์ขี้เกียจ
ตัวอย่างต่อไปนี้ยังแมปคลาสที่อยู่กับคลาสถาวร ในเวลานี้คลาสที่อยู่ยังกลายเป็นคลาสเอนทิตีและนิติบุคคลและนิติบุคคลที่อยู่ในรูปแบบการเชื่อมโยงแบบสองทางหนึ่งถึงหลายทาง รหัสไฟล์การแมปในเวลานี้มีดังนี้:
รายชื่อ 3. person.hbm.xml
<? xml version = "1.0" การเข้ารหัส = "gbk"?> <!-ระบุข้อมูล DTD สำหรับไฮเบอร์เนต-> <! Doctype hibernate-mapping สาธารณะ "-// hibernate/hibernate mapping dtd dtd 3.0 // en "" http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd "> <hibernate-mapping package =" org.crazyit.app.domain "> <! column = "person_id"> <!-กำหนดนโยบายตัวสร้างคีย์หลัก-> <generator/> <!-ใช้เพื่อแมปแอตทริบิวต์ทั่วไป-> <ชื่อคุณสมบัติ = "ชื่อ" type = "สตริง"/> <ชื่อคุณสมบัติ = "อายุ" ประเภท = "int"/> <! inverse = "true"> <!-ระบุคอลัมน์คีย์ต่างประเทศที่เกี่ยวข้อง-> <คีย์คอลัมน์ = "person_id"/> <!-ใช้เพื่อทำแผนที่กับแอตทริบิวต์คลาสที่เกี่ยวข้อง-> <แบบหนึ่งต่อหลาย/> </set> </class> <! ระบุนโยบายเครื่องกำเนิดคีย์หลัก-> <generator/> </id> <!-แผนที่รายละเอียดแอตทริบิวต์ปกติ-> <property name = "รายละเอียด"/> <!-แผนที่แอตทริบิวต์ปกติ zip-> <ชื่อคุณสมบัติ = "zip"/> <! not-null = "true"/> </class> </hibernate-mapping>
ถัดไปโปรแกรมจะโหลดเอนทิตีบุคคลด้วย ID 1 ผ่านตัวอย่างโค้ดต่อไปนี้:
// เปิดเซสชันเซสชันการขึ้นอยู่กับบริบท = sf.getCurrentsession (); ธุรกรรม tx = session.beginTransaction (); ที่อยู่ = (ที่อยู่) เซสชัน (address.class, 1); // <1> system.out.println (address.getDetail ());
เพื่อที่จะเห็นการประมวลผลเอนทิตีที่เกี่ยวข้องของไฮเบอร์เนตเมื่อโหลดเอนทิตีที่อยู่เราตั้งค่าเบรกพอยต์ที่รหัส <1> และแก้ไขข้อบกพร่องใน Eclipse ในเวลานี้เราจะเห็นว่าหน้าต่างคอนโซล Eclipse ส่งออกคำสั่ง SQL ต่อไปนี้:
เลือก address0_.address_id เป็น address1_1_0_, address0_.detail เป็น detail1_0_, address0_.zip เป็น zip1_0_, address0_.person_id เป็น person4_1_0_ จากที่อยู่
ไม่ยากที่จะเห็นจากคำสั่ง SQL นี้ว่าไฮเบอร์เนตโหลดตารางข้อมูลที่สอดคล้องกับเอนทิตีที่อยู่เพื่อรวบรวมข้อมูล แต่ไม่ได้รวบรวมข้อมูลบันทึกจากตารางข้อมูลที่สอดคล้องกับเอนทิตีของบุคคลซึ่งก็คือการโหลดขี้เกียจมีบทบาท
จากหน้าต่างตัวแปรของ eclipse ดูเอาต์พุตที่แสดงในรูปที่ 4:
รูปที่ 4. เอนทิตีโหลดล่าช้า
มันสามารถเห็นได้อย่างชัดเจนจากรูปที่ 4 ว่านิติบุคคลที่เกี่ยวข้องกับเอนทิตีที่อยู่ไม่ใช่วัตถุบุคคล แต่เป็นอินสแตนซ์ของบุคคล _ $$ _ Javassist_0 คลาส คลาสนี้เป็นคลาสพร็อกซีที่สร้างขึ้นแบบไดนามิกโดยไฮเบอร์เนตโดยใช้โครงการ Javassist เมื่อไฮเบอร์เนตล่าช้าในการโหลดเอนทิตีที่เกี่ยวข้อง Javassist จะถูกใช้เพื่อสร้างวัตถุพร็อกซีแบบไดนามิกและวัตถุพร็อกซีนี้จะรับผิดชอบในการพร็อกซ์ "ยังไม่โหลด"
ตราบใดที่แอปพลิเคชันจำเป็นต้องใช้เอนทิตีที่เกี่ยวข้องซึ่งเป็น "ยังไม่โหลด" บุคคล _ $$ _ Javassist_0 วัตถุพร็อกซีจะรับผิดชอบในการโหลดเอนทิตีที่เกี่ยวข้องจริงและส่งคืนเอนทิตีที่เกี่ยวข้องจริง - นี่เป็นรูปแบบพร็อกซีทั่วไป
คลิกแอตทริบิวต์บุคคลในหน้าต่างตัวแปรที่แสดงในรูปที่ 4 (นั่นคือบังคับให้แอตทริบิวต์บุคคลที่ใช้ในโหมดดีบั๊ก) จากนั้นคุณจะเห็นเอาต์พุต SQL คำสั่งต่อไปนี้ในหน้าต่างคอนโซลของ eclipse:
เลือก person0_.person_id เป็น person1_0_0_, person0_.name เป็น name0_0_, person0_.age เป็น AGE0_0_ จาก person_inf person0_where person0_.person_id =?
คำสั่ง SQL ข้างต้นเป็นคำสั่งที่จับเอนทิตีที่เกี่ยวข้องของ "การโหลดล่าช้า" ในเวลานี้คุณสามารถเห็นผลลัพธ์ที่แสดงในรูปที่ 5 ของเอาต์พุตหน้าต่างตัวแปร:
รูปที่ 5. เอนทิตีที่โหลด
Hibernate ใช้โหมด "โหลดล่าช้า" เพื่อจัดการเอนทิตีที่เกี่ยวข้อง ในความเป็นจริงเมื่อโหลดเอนทิตีหลักมันไม่ได้คว้าข้อมูลที่เกี่ยวข้องของเอนทิตีที่เกี่ยวข้อง แต่เพียงสร้างวัตถุแบบไดนามิกเป็นพร็อกซีของเอนทิตีที่เกี่ยวข้อง เมื่อแอปพลิเคชันจำเป็นต้องใช้เอนทิตีที่เกี่ยวข้องจริงๆวัตถุพร็อกซีจะรับผิดชอบในการคว้าบันทึกจากฐานข้อมูลพื้นฐานและเริ่มต้นเอนทิตีที่เกี่ยวข้องจริง
ในการโหลดล่าช้าไฮเบอร์เนตสิ่งที่โปรแกรมไคลเอนต์เริ่มได้รับคือวัตถุพร็อกซีที่สร้างขึ้นแบบไดนามิกในขณะที่เอนทิตีจริงจะถูกมอบหมายให้กับวัตถุพร็อกซีสำหรับการจัดการ - นี่คือรูปแบบพร็อกซีทั่วไป
โหมดตัวแทน
โหมดพร็อกซีเป็นโหมดการออกแบบที่มีแอปพลิเคชันที่กว้างมาก เมื่อรหัสลูกค้าต้องการเรียกวัตถุลูกค้าจริง ๆ ไม่สนใจว่าจะได้รับวัตถุอย่างถูกต้องหรือไม่ มันต้องการเพียงวัตถุที่สามารถให้ฟังก์ชั่น ในเวลานี้เราสามารถส่งคืนพร็อกซี (พร็อกซี) ของวัตถุ
ในวิธีการออกแบบนี้ระบบจะให้วัตถุกับวัตถุพร็อกซีและวัตถุพร็อกซีควบคุมการอ้างอิงไปยังวัตถุแหล่งที่มา พร็อกซีเป็นวัตถุ Java ที่ทำหน้าที่ในนามของวัตถุ Java อื่น ในบางกรณีรหัสลูกค้าไม่ต้องการหรือไม่สามารถเรียก Callee โดยตรงและวัตถุพร็อกซีสามารถทำหน้าที่เป็นตัวกลางระหว่างลูกค้าและวัตถุเป้าหมาย
สำหรับลูกค้ามันไม่สามารถแยกแยะความแตกต่างระหว่างวัตถุพร็อกซีและวัตถุจริงและไม่จำเป็นต้องแยกแยะความแตกต่างระหว่างวัตถุพร็อกซีและวัตถุจริง รหัสลูกค้าไม่ทราบวัตถุพร็อกซีจริง รหัสไคลเอ็นต์นั้นมุ่งเน้นอินเทอร์เฟซและจะถืออินเทอร์เฟซของวัตถุพร็อกซีเท่านั้น
ในระยะสั้นตราบใดที่รหัสไคลเอนต์ไม่สามารถหรือไม่ต้องการเข้าถึงวัตถุที่เรียกโดยตรง - มีเหตุผลหลายประการสำหรับสถานการณ์นี้เช่นการสร้างวัตถุที่มีค่าใช้จ่ายสูงของระบบสูงหรือวัตถุที่เรียกว่าอยู่ในโฮสต์ระยะไกลหรือฟังก์ชั่นของวัตถุเป้าหมายไม่เพียงพอที่จะตอบสนองความต้องการ ...
ต่อไปนี้แสดงให้เห็นถึงโหมดพร็อกซีอย่างง่าย โปรแกรมแรกให้อินเทอร์เฟซรูปภาพซึ่งแสดงถึงอินเทอร์เฟซที่ใช้โดยวัตถุภาพขนาดใหญ่ รหัสอินเตอร์เฟสมีดังนี้:
รายการ 3. image.java
ภาพอินเตอร์เฟสสาธารณะ {void show ();}อินเทอร์เฟซนี้ให้คลาสการใช้งานที่จำลองวัตถุภาพขนาดใหญ่และตัวสร้างของคลาสการใช้งานใช้เมธอด thead.sleep () เพื่อหยุด 3s ด้านล่างนี้เป็นรหัสโปรแกรมสำหรับ bigimage
รายการ 4. bigimage.java
// ใช้ bigimage นี้เพื่อจำลองภาพสาธารณะภาพขนาดใหญ่ Bigimage ใช้ภาพ {สาธารณะ bigimage () {ลอง {// โปรแกรมหยุดชั่วคราวระบบการจำลองโหมด 3S ระบบการจำลองค่าใช้จ่าย Thread.sleep (3000); System.out.println ("การโหลดภาพสำเร็จ ... ");} catch (interruptedexception ex) {ex.printstacktrace ();}} // ใช้วิธีการแสดง () ในภาพโมฆะสาธารณะแสดง () {system.out.println ("วาดภาพขนาดใหญ่จริง");รหัสโปรแกรมข้างต้นหยุด 3s ซึ่งบ่งชี้ว่าต้องใช้เวลา 3 วินาทีเหนือศีรษะเพื่อสร้างวัตถุ bigimage - โปรแกรมใช้ความล่าช้านี้เพื่อจำลองค่าใช้จ่ายของระบบที่เกิดจากการโหลดภาพนี้ หากไม่ได้ใช้โหมดพร็อกซีระบบจะสร้างความล่าช้า 3S เมื่อสร้าง bigimage ในโปรแกรม เพื่อหลีกเลี่ยงความล่าช้านี้โปรแกรมจะจัดทำวัตถุพร็อกซีสำหรับวัตถุ BigImage และคลาสพร็อกซีของคลาส Bigimage มีดังนี้
รายการ 5. imageproxy.java
คลาสสาธารณะ ImageProxy ใช้ภาพ {// รวมอินสแตนซ์ภาพเป็นภาพพร็อกซีออบเจ็กต์ภาพส่วนตัวภาพ; // ใช้สิ่งที่เป็นนามธรรมเพื่อเริ่มต้นวัตถุพร็อกซีวัตถุสาธารณะ imageProxy (ภาพภาพ) {this.image = image;}/*** rewrite การแสดง () show () {// สร้างวัตถุพร็อกซีเฉพาะในกรณี (image == null) {image = new bigimage (); ime image.show ();}}คลาสพร็อกซี ImageProxy ด้านบนใช้วิธีการแสดง () เดียวกันกับ BigImage ซึ่งช่วยให้รหัสไคลเอนต์ใช้วัตถุพร็อกซีเป็น bigimage หลังจากได้รับวัตถุพร็อกซี
ตรรกะการควบคุมจะถูกเพิ่มลงในวิธีการแสดง () ของคลาส imageProxy ตรรกะการควบคุมนี้ใช้เพื่อควบคุมว่าวัตถุพร็อกซี bigimage จะถูกสร้างขึ้นเฉพาะเมื่อระบบเรียกการแสดง () ของภาพ โปรแกรมต่อไปนี้จำเป็นต้องใช้วัตถุ BigImage แต่โปรแกรมไม่ได้ส่งคืนอินสแตนซ์ BigImage โดยตรง แต่ก่อนอื่นจะส่งคืนวัตถุพร็อกซี BigImage ดังที่แสดงในโปรแกรมต่อไปนี้
รายการ 6. bigimagetest.java
คลาสสาธารณะ bigimagetest {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {long start = system.currentTimeMillis (); // โปรแกรมส่งคืนวัตถุภาพซึ่งเป็นเพียงวัตถุพร็อกซีของภาพ bigimage = imageProxy (null); system.out.println ( เริ่มต้น)); // โปรแกรมจะสร้างวัตถุพร็อกซีจริง ๆ เมื่อวิธีการแสดง () ของพร็อกซีรูปภาพถูกเรียกจริง image.show ();}}โปรแกรมข้างต้นเริ่มต้นภาพได้อย่างรวดเร็วเนื่องจากโปรแกรมไม่ได้สร้างวัตถุ BigImage แต่เพียงแค่ได้รับวัตถุพร็อกซี ImageProxy - จนกว่าโปรแกรมจะเรียกวิธีการ Image.Show () โปรแกรมจะต้องเรียกวิธีการแสดง () ของวัตถุ BigImage และโปรแกรมสร้างวัตถุ bigimage ในเวลานี้ เรียกใช้โปรแกรมด้านบนและดูผลลัพธ์ที่แสดงในรูปที่ 6
รูปที่ 6. ปรับปรุงประสิทธิภาพโดยใช้โหมดพร็อกซี
เมื่อเห็นผลลัพธ์การทำงานที่แสดงในรูปที่ 6 ผู้อ่านควรจะยอมรับได้ว่าการใช้โหมดพร็อกซีช่วยปรับปรุงประสิทธิภาพของระบบในการรับวัตถุภาพ แต่ผู้อ่านบางคนอาจถามคำถาม: เมื่อโปรแกรมเรียกวิธีการแสดง () ของวัตถุ ImageProxy มันก็ต้องสร้างวัตถุ bigimage แต่ค่าใช้จ่ายของระบบไม่ได้ลดลงจริงหรือ? เป็นเพียงว่าระบบนี้มีความล่าช้าหรือไม่?
เราสามารถตอบคำถามนี้ได้จากสองมุมมองต่อไปนี้:
การชะลอการสร้าง bigimage จนกว่าจะจำเป็นจริง ๆ สามารถมั่นใจได้ว่าการทำงานที่ราบรื่นของโปรแกรมก่อนหน้านี้และลดเวลาการอยู่รอดของ bigimage ในหน่วยความจำช่วยประหยัดค่าใช้จ่ายของระบบจากมุมมองแมโคร
ในบางกรณีอาจจะไม่เรียกวิธีการแสดง () ของวัตถุ ImageProxy - หมายความว่าระบบไม่จำเป็นต้องสร้างวัตถุ bigimage เลย ในกรณีนี้การใช้โหมดพร็อกซีสามารถปรับปรุงประสิทธิภาพการทำงานของระบบได้อย่างมีนัยสำคัญ
Hibernate ที่คล้ายกันโดยสิ้นเชิงยังใช้โหมดพร็อกซีเพื่อ "ล่าช้า" เวลาในการโหลดเอนทิตีที่เกี่ยวข้อง หากโปรแกรมไม่จำเป็นต้องเข้าถึงเอนทิตีที่เกี่ยวข้องโปรแกรมจะไม่รวบรวมข้อมูลเอนทิตีที่เกี่ยวข้อง สิ่งนี้สามารถบันทึกค่าใช้จ่ายของหน่วยความจำของระบบและลดเวลาเมื่อไฮเบอร์เนตโหลดเอนทิตี
สรุป
Hibernate Lazy Load เป็นแอปพลิเคชันของโหมดพร็อกซี ในปีที่ผ่านมาเรามักจะใช้โหมดพร็อกซีเพื่อลดค่าใช้จ่ายหน่วยความจำระบบและปรับปรุงประสิทธิภาพของแอปพลิเคชัน Hibernate ใช้ประโยชน์จากข้อได้เปรียบของโหมดพร็อกซีนี้และรวม Javassist หรือ CGLIB เพื่อสร้างวัตถุพร็อกซีแบบไดนามิกซึ่งเพิ่มความยืดหยุ่นให้กับโหมดพร็อกซี Hibernate ให้ชื่อใหม่นี้: ขี้เกียจกำลังโหลด ไม่ว่าในกรณีใดการวิเคราะห์และทำความเข้าใจการใช้เฟรมเวิร์กโอเพนซอร์ซเหล่านี้อย่างเต็มที่จะได้รับประโยชน์จากข้อได้เปรียบของแบบจำลองการออกแบบคลาสสิก
ฉันหวังว่าคำอธิบายในบทความนี้จะเป็นประโยชน์กับการเขียนโปรแกรม Java ของทุกคนตามกรอบการทำงานของไฮเบอร์เนต