การวิจัยในบทความนี้ส่วนใหญ่มุ่งเน้นไปที่กลไกแคชไฮเบอร์เนตดังต่อไปนี้
student.java:
นักเรียนชั้นเรียนสาธารณะ {/*ID นักเรียน*/ID INT ส่วนตัว;/*ชื่อนักเรียน*/ชื่อสตริงส่วนตัว;/*ความสัมพันธ์ระหว่างนักเรียนและชั้นเรียน*/ชั้นเรียนส่วนตัว; // omit setter และวิธีการ getter}ชั้นเรียน. java:
ชั้นเรียนสาธารณะคลาส {/*คลาส ID*/ID INT ส่วนตัว;/*ชื่อคลาส*/ชื่อสตริงส่วนตัว;/*ความสัมพันธ์ระหว่างชั้นเรียนและนักเรียน*/ชุดส่วนตัว <Tudent> นักเรียน; // omit setter และวิธีการ getter}student.hbm.xml:
<? xml เวอร์ชัน = "1.0"?> <! Doctype hibernate-mapping สาธารณะ "-// hibernate/hibernate mapping dtd 3.0 // en" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" table = "t_student"> <id name = "id"> <generator // id> <!-แผนที่คุณสมบัติปกติ-> <property name = "name"/> <!-การแมปแบบหลายต่อหนึ่งเพิ่มคีย์ต่างประเทศลงในหลาย ๆ จุด->
classes.hbm.xml:
<? xml เวอร์ชัน = "1.0"?> <! Doctype hibernate-mapping สาธารณะ "-// hibernate/hibernate mapping dtd 3.0 // en" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" <class name = "classes" table = "t_classes" lazy = "false"> <id name = "id"> <generator/generator/> </id> <property name = "name"/> <! </set> </class> </hibernate-mapping>
ระยะเวลาการประกาศของแคชระดับแรกนั้นสั้นมากและวงจรชีวิตของเซสชันนั้นสอดคล้องกัน แคชระดับแรกเรียกว่าแคชระดับเซสชันหรือแคชระดับ แคชระดับแรกคือวัตถุแคชและไม่สามารถแคชแอตทริบิวต์
วิธีทดสอบ (ใช้การค้นหา () แบบสอบถามสองครั้งในเซสชันเดียวกัน):
/*หลังจากนำออกไปมันจะถูกวางไว้ในแคชและครั้งที่สองที่มันดึงมาจะถูกดึงโดยตรงจากแคช*/ นักเรียนนักเรียน = (นักเรียน) เซสชั่นโหลด (นักเรียน. class, 1); System.out.println ("student.name =" + student.getName ()); /*ไม่มีการออกคำสั่งค้นหาโหลดใช้แคช*/ student = (นักเรียน) เซสชันการโหลด (student.class, 1); System.out.println ("student.name =" + student.getName ());หมายเหตุ: เราจะพบว่าเมื่อเราค้นหาครั้งแรกผลลัพธ์ที่พบจะถูกวางไว้ในเซสชันแคชและแคชระดับหนึ่ง เมื่อฉันได้รับค่าในเวลาหลังโหลด () ครั้งที่สองฉันไม่ได้ออกคำสั่งเพื่อสอบถามในฐานข้อมูล แต่ดึงค่าโดยตรงจากแคช (ต้องอยู่ในเซสชันเดียวกัน)
วิธีทดสอบสอง (ในเซสชั่นเดียวกัน):
นักเรียนนักเรียน = นักเรียนใหม่ (); Student.setName ("Zhang San"); serializable id = session.save (นักเรียน); student = (นักเรียน) เซสชันโหลด (นักเรียน. คลาส, รหัส); // คำสั่ง Query จะไม่ถูกออกเนื่องจากบันทึกสนับสนุน Cache System.out.println ("student.name =" + student.getName ());หมายเหตุ: วิธีการบันทึก () ถูกเรียกและโหลด () ใช้ในการโหลดวัตถุจากนั้นแอตทริบิวต์ชื่อจะได้รับจริง แต่ไม่มีคำสั่งใด ๆ ที่จะออกจากฐานข้อมูล เพราะวิธีการบันทึก () ยังรองรับแคช
ทดสอบการเพิ่มชุดข้อมูลขนาดใหญ่:
โมฆะสาธารณะ testCache7 () {เซสชันเซสชัน = null; ลอง {session = hibernateutils.getSession (); session.beginTransaction (); สำหรับ (int i = 0; i <100; i ++) {นักเรียนนักเรียน = นักเรียนใหม่ (); Student.setName ("Zhang San" + i); เซสชั่น SAVE (นักเรียน); // อัปเดตทุก ๆ 20 รายการถ้า (i % 20 == 0) {// ล้างแคชและหลังจากโทรไปที่ฟลัชข้อมูลจะถูกบันทึกลงในฐานข้อมูลเซสชัน Flush (); // ล้างเนื้อหาที่แคชเซสชัน clear (); }} session.getTransaction (). commit (); } catch (exception e) {e.printstacktrace (); session.getTransaction (). ย้อนกลับ (); } ในที่สุด {hibernateutils.closesession (เซสชัน); - บันทึก:
1. เนื่องจากวิธีการบันทึก () รองรับแคชจึงมีปัญหา หากฉันต้องการประหยัดข้อมูล 10 ล้านชิ้นในเวลาเดียวกันมีวัตถุแคช 10 ล้านชิ้นในแคชซึ่งน่าจะทำให้เกิดการล้น ดังนั้น Hibernate ไม่รองรับการดำเนินการอัปเดตของข้อมูลขนาดใหญ่ แต่เรายังสามารถจัดการกับปัญหานี้ได้อย่างยืดหยุ่นเช่นการล้างแคชทุก ๆ 20 ชิ้นโดยใช้ลูป
2. บันทึกอัปเดต, saveorupdate, โหลด, รับ, รายการ, ซ้ำ, วิธีการล็อคจะใส่วัตถุในแคชระดับแรก แคชระดับแรกไม่สามารถควบคุมจำนวนแคชได้ดังนั้นคุณควรให้ความสนใจกับความเป็นไปได้ของหน่วยความจำที่ล้นเมื่อใช้งานข้อมูลในแบทช์ขนาดใหญ่ คุณสามารถใช้วิธีการขับไล่และล้างเพื่อล้างเนื้อหาในแคช
แคชทุติยภูมิเรียกว่าแคชระดับกระบวนการหรือแคชระดับ SessionFactory และแคชทุติยภูมิสามารถแชร์ได้โดยแคชเซสชันทั้งหมด วงจรชีวิตของแคชทุติยภูมิเหมือนกับของ SessionFactory SessionFactory สามารถจัดการแคชทุติยภูมิ หลักการของแคชรองถูกใช้เมื่อการอ่านมากกว่าการเขียนมาก แคชทุติยภูมิส่วนใหญ่ใช้ในการแคชวัตถุเอนทิตี
1. คัดลอกไฟล์ ehcahe.xml ไปยังไดเรกทอรี SRC
2. เพิ่มผู้ให้บริการผลิตภัณฑ์แคชลงในไฟล์ hibernate.cfg.xml ดังต่อไปนี้:
<property name = "hibernate.cache.provider_class"> org.hibernate.cache.ehcacheprovider </property>
3. เปิดใช้งานแคชระดับ 2 (ไม่สามารถแสดงการเริ่มต้นได้เนื่องจากเปิดใช้งานค่าเริ่มต้น) ดังนี้:
<property name = "hibernate.cache.use_second_level_cache"> true </porement>
4. ระบุคลาสเอนทิตีที่ใช้แคชระดับ 2
5. นำเข้าแพ็คเกจ jar อินเตอร์เฟสที่ใช้โดยแคช: lib/optional/ehcache/ehcache-core-2.4.3.jar
เนื้อหาของไฟล์ ehcache.xml:
<defaultCache MaxElementsInMemory = "10,000" Eternal = "False" Timetoidleseconds = "120" TimetoLiveSeconds = "120" OverflowTodisk = "true" />>
บันทึก:
1.MaxElementsInMemory แสดงถึงวัตถุที่เก็บไว้มากที่สุดในแคช
2. ภาษาเทรนด์ระบุว่ามันจะไม่มีวันหมดอายุหรือไม่ (การตั้งค่าเป็นเท็จนั้นใช้งานได้จริงมากขึ้นถ้าเป็นจริงมันจะไม่หมดอายุคุณลักษณะต่อไปนี้จะไม่มีความหมาย)
3.Timetoidlesecods ระบุว่าวัตถุไม่ได้รับการเข้าถึงนานแค่ไหนหลังจากครั้งแรกที่มีการล้าง
4. Timetolivesecods แสดงเวลาสินค้าคงคลังของวัตถุ
5. OverflowTodisk เป็นจริงซึ่งหมายความว่าจำนวนในแคชเกินจำนวนที่ระบุโดย MaxElementsInmemory และเก็บไว้ในดิสก์
ระบุเส้นทางดิสก์ที่บันทึกไว้บนล้น:
<diskstore path = "java.io.tmpdir"/>>
หมายเหตุ: เส้นทางนี้สามารถเปลี่ยนแปลงได้
วิธีการทดสอบ (หลักฐานของแคชระดับ 1 คือต้องอยู่ในเซสชั่นเดียวกันตอนนี้เราใช้แคชระดับ 2 เพื่อดูว่ามีแคชในสองเซสชันที่แตกต่างกัน):
โมฆะสาธารณะ TestCache1 () {เซสชันเซสชัน = null; ลอง {เซสชัน = hibernateutils.getSession (); session.beginTransaction (); นักเรียนนักเรียน = (นักเรียน) เซสชัน (นักเรียน. class, 1); system.out.println ("student.name =" + student.getName () {E.PrintStackTrace (); session.getTransaction (). การย้อนกลับ ();} ในที่สุด {hibernateutils.closesession (เซสชัน);} ลอง {เซสชั่น = hibernateutils.getSession () เซสชัน session.beginTransaction () สามารถแบ่งปันข้อมูลในแคชรอง ///he รองแคชเป็นระบบแคชระดับกระบวนการ. out.println ("student.name =" + student.getName ()); session.getTransaction (). commit ()} catch (Exception e) {hibernateutils.closesession (เซสชัน);}}หมายเหตุ: หากมีการกำหนดค่าแคชทุติยภูมิเราจะพบว่าแม้ว่าเซสชันแรกจะปิดและเซสชันอื่นจะเปิดใช้งานการโหลดข้อมูลมันจะไม่ออกคำสั่งเพื่อสอบถามข้อมูลในฐานข้อมูลเนื่องจากแคชรองถูกกำหนดค่าโดยเซสชันทั้งหมด
ปิดการใช้งานแคชระดับ 2 เพื่อใช้การเพิ่มชุดข้อมูลขนาดใหญ่:
โมฆะสาธารณะ testCache5 () {เซสชันเซสชัน = null; ลอง {session = hibernateutils.getSession (); session.beginTransaction (); CACHE // LINE-FIRST CACHE และการโต้ตอบแคชรองเป็นสิ่งต้องห้ามเซสชัน SESTCACHEMODE (CACHEMODE.IGNORE); สำหรับ (int i = 0; i <100; i ++) {นักเรียนนักเรียน = นักเรียนใหม่ (); Student.setName ("Zhang San" + i); เซสชั่น SAVE (นักเรียน); // อัปเดตทุก ๆ 20 รายการถ้า (i % 20 == 0) {session.flush (); // ล้างเนื้อหาแคชเซสชัน clear (); }} session.getTransaction (). commit (); } catch (exception e) {e.printstacktrace (); session.getTransaction (). ย้อนกลับ (); } ในที่สุด {hibernateutils.closesession (เซสชัน); -หมายเหตุ: session.flush () หมายถึงการล้างแคชระดับแรก แต่เราได้เริ่มต้นแคชระดับที่สองอีกครั้งและหลังจากบันทึก () มันยังช่วยประหยัดลงในแคชระดับที่สอง แต่ยังมีการล้นที่เกิดจากแคชที่มากเกินไป ดังนั้นในกรณีนี้เราควรปิดการใช้งานแคชรอง: session.setCacheMode (cachemode.ignore);
แคชแบบสอบถาม: ทั้งแคชระดับแรกและแคชแคชระดับที่สองแคชทั้งวัตถุแคชเอนทิตี แต่บางครั้งเราหวังว่าจะได้รับแอตทริบิวต์บางอย่างและไม่สามารถเข้าถึงฐานข้อมูลได้บ่อยครั้ง แต่ได้รับจากแคช ในเวลานี้เราสามารถใช้แคชแบบสอบถาม นอกจากนี้ชุดผลลัพธ์แคชแบบสอบถามสำหรับวัตถุเอนทิตีจะแคช ID วงจรชีวิตของแคชแบบสอบถามเปลี่ยนไป เมื่อตารางที่เกี่ยวข้องได้รับการแก้ไขวงจรการประกาศของแคชแบบสอบถามจะสิ้นสุดลงซึ่งไม่มีส่วนเกี่ยวข้องกับวงจรชีวิตของเซสชัน
1. แก้ไขไฟล์ hibernate.cfg.xml เพื่อเปิดใช้งานแคชแบบสอบถาม หากเท็จเป็นค่าเริ่มต้นจะไม่เปิดใช้งาน ควรตั้งค่าดังนี้:
<property name = "hibernate.cache.use_query_cache"> true </property>
2. ต้องเปิดใช้งานในโปรแกรมเช่น:
query.setCacheable (จริง)
วิธีทดสอบ:
โมฆะสาธารณะ testCache2 () {เซสชัน session = null; ลอง {เซสชัน = hibernateutils.getSession (); session.beginTransaction (); ชื่อรายการ = เซสชัน. createquery ("เลือก s.name จากนักเรียน s"). setCacheable (จริง) .List () (String) names.get (i); system.out.println (ชื่อ);} session.getTransaction (). commit ();} catch (Exception e) {E.printstackTrace (); session.getTransaction (). Rollback ();} ในที่สุด {hibernateutils.closesession (เซสชัน);} system.out.println ("---------------------------------------------------------------------------------------------------------------------------------------------------------------------- - .List (); สำหรับ (int i = 0; i <names.size (); i ++) {string name = (string) names.get (i); system.out.println (ชื่อ);} session.getTransaction () {hibernateutils.closesession (เซสชัน);}}หมายเหตุ: ในรหัสด้านบนเราปิดแคชรองเปิดแคชแบบสอบถามแล้วสอบถามคุณสมบัติปกติ เรียกใช้รหัสทดสอบและเราสามารถค้นหาว่าในเซสชันแรกการสืบค้นแรกออกคำสั่งจากนั้นปิดเซสชันแล้วสอบถามในเซสชันที่สอง เราจะพบว่าการสืบค้นในเซสชั่นที่สองไม่ได้ออกแถลงการณ์ซึ่งหมายความว่าแคชแบบสอบถามไม่มีส่วนเกี่ยวข้องกับวงจรชีวิตของเซสชัน
การกำหนดค่าแคชของ hibernate.cfg.xml:
<!-ตั้งค่าอินเทอร์เฟซการใช้งานสำหรับการระบุแคชรอง-> <ชื่อคุณสมบัติ = "hibernate.cache.region.factory_class"> org.hibernate.cache.ehcacheregionfactory </คุณสมบัติ> <!-ตั้งค่าไฟล์กำหนดค่าที่ใช้สำหรับแคชรอง-> < name = "net.sf.ehcache.configurationResourcename">/ehcache.xml </คุณสมบัติ> <!-ตั้งค่าแคชโดยใช้การสืบค้น-> <property name = "hibernate.cache.use_query_cache"> true </property> <! <mapping resource = "com/lixue/bean/student.hbm.xml"/> <!-ไฟล์การแมปทรัพยากร (นั่นคือไฟล์การแมปเอนทิตี) จะต้องได้รับการแนะนำก่อนตั้งค่าคลาสเอนทิตีโดยใช้แคชระดับ 2->
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้เกี่ยวกับการวิเคราะห์รหัสของตัวอย่างกลไกแคชไฮเบอร์เนตฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน เพื่อนที่สนใจสามารถอ้างถึงหัวข้ออื่น ๆ ที่เกี่ยวข้องในเว็บไซต์นี้ต่อไป หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น ขอบคุณเพื่อนที่ให้การสนับสนุนเว็บไซต์นี้!