Hibernate ได้ดำเนินการจำแนกและบูรณาการและพบว่าไฮเบอร์เนตแบ่งออกเป็นสามส่วน: วัตถุหลักการทำแผนที่และ HQL สามส่วนเหล่านี้มักใช้กันมากที่สุดในกระบวนการพัฒนา บทความก่อนหน้านี้พูดคุยเกี่ยวกับวิธีการแปลงระหว่างวัตถุหลักและวัตถุ ถัดไปหารือเกี่ยวกับวิธีการใช้การทำแผนที่ของไฮเบอร์เนต
ฟังก์ชั่นที่สำคัญของไฮเบอร์เนตคือการทำแผนที่ซึ่งสามารถแปลงระหว่างโมเดลวัตถุและแบบจำลองเชิงสัมพันธ์ ได้รับการสนับสนุนโดยแนวคิดการเขียนโปรแกรมเชิงวัตถุ นักพัฒนาที่ใช้โปรแกรมการทำแผนที่จำเป็นต้องใส่ใจเกี่ยวกับการเขียนโค้ดในโมเดลวัตถุเท่านั้น การแมประหว่างวัตถุและฐานข้อมูลเชิงสัมพันธ์มักจะถูกกำหนดโดยเอกสาร XML เอกสารการทำแผนที่นี้ได้รับการออกแบบให้อ่านได้และสามารถแก้ไขได้ด้วยตนเอง ฉันสรุปความสัมพันธ์การแมปนี้ดังที่แสดงในรูปต่อไปนี้:
การแมปถูกกำหนดผ่าน XML จัดการโดยใช้เซสชันที่สร้างโดย Hibernate และในที่สุดเซสชันจะใช้ JTA เพื่อส่งการเปลี่ยนแปลงไปยังฐานข้อมูล เซสชันสามารถเข้าใจได้ว่าเป็นผู้จัดการการคงอยู่ซึ่งจัดการวัตถุในเลเยอร์การคงอยู่ มันถูกสร้างขึ้นโดย SessionFactory เมื่อการเขียนโปรแกรมด้วยไฮเบอร์เนตคุณต้องเชื่อมต่อกับฐานข้อมูลก่อนดังนั้นคุณต้องตรวจสอบการกำหนดค่าของการเชื่อมต่อฐานข้อมูลใน XML ก่อนที่จะสร้างเซสชัน factory ตามการกำหนดค่าของเอกสาร (ซึ่งสามารถเข้าใจได้ว่าเป็นกระจกฐานข้อมูล) จากนั้นสร้างเซสชัน ในที่สุดเซสชันจะส่งการเปลี่ยนแปลงไปยังฐานข้อมูลอย่างสม่ำเสมอซึ่งจะเสร็จสิ้นการดำเนินการทั้งหมด
กระบวนการใช้งาน
1. สร้างไฟล์การแมปและไฟล์การแมปจะถูกต่อท้ายด้วย. hbm.xml ซึ่งระบุว่าเป็นไฟล์การแมปไฮเบอร์เนต
2. ลงทะเบียนคลาสเอนทิตีในไฟล์การแมปและเพิ่มคุณสมบัติของคลาสเอนทิตีลงในคลาสการแมป เมื่อเพิ่มคุณสมบัติคุณต้องระบุสองค่า: ID และคุณสมบัติ ID ระบุว่าเป็นตัวระบุเพียงตัวเดียวของเอนทิตีและคุณสมบัติระบุว่าเป็นคอลัมน์ฟิลด์ของตาราง
3. ส่งการแก้ไขและซิงโครไนซ์ข้อมูล
หมายเหตุ: นักพัฒนาที่ได้พัฒนาข้อมูล XML ไปยังฐานข้อมูลในไม่ช้าจะเข้าใจว่าการแมปนี้เป็นกระบวนการของการอัปเดตแบทช์และการสร้างแบทช์และการแมปก็ไม่มีข้อยกเว้น ไฮเบอร์เนตกำหนดชุดของมาตรฐานการทำแผนที่ที่สามารถแปลงตามมาตรฐาน การใช้งานภายในยังคงตายดังนั้นจึงค่อนข้างยืดหยุ่นและใช้งานง่าย
กระบวนการทำแผนที่คลาสเอนทิตีอย่างง่าย:
1. รหัสคุณสมบัติของคลาสเอนทิตี USER1:
แพ็คเกจ com.hibernate; นำเข้า java.util.date; ผู้ใช้ระดับสาธารณะ 1 {รหัสสตริงส่วนตัว; ชื่อสตริงส่วนตัว; รหัสผ่านสตริงส่วนตัว วันที่ส่วนตัว createTime; วันที่เอกชนหมดอายุ; สตริงสาธารณะ getId () {return id; } สตริงสาธารณะ getName () {ชื่อคืน; } โมฆะสาธารณะ setName (ชื่อสตริง) {this.name = name; } สตริงสาธารณะ getPassword () {ส่งคืนรหัสผ่าน; } โมฆะสาธารณะ setPassword (รหัสผ่านสตริง) {this.password = รหัสผ่าน; } วันที่สาธารณะ getCreateTime () {return createTime; } โมฆะสาธารณะ setCreateTime (วันที่ createTime) {this.createTime = createTime; } วันที่สาธารณะ getExpireTime () {ส่งคืนหมดอายุ; } โมฆะสาธารณะ setExpireTime (วันที่วันหมดอายุ) {this.expireTime = ExpireTime; -
2. การใช้รหัสภายในของ user1.hbm.xml ของไฟล์การแม็พ user1.java:
การตั้งค่าที่สามารถตั้งค่าในฐานข้อมูลพื้นฐานมีให้ในไฮเบอร์เนต คุณสามารถใช้แอตทริบิวต์ฉลากเพื่อตั้งค่าความสัมพันธ์การแมปเฉพาะ
คลาส-> ตารางใช้แท็กคลาส, คุณสมบัติที่ใช้กันทั่วไป:
(1) ชื่อ: คลาสเอนทิตีแผนที่ค่าของมันจะต้องตั้งค่าเป็นชื่อของคลาสเอนทิตีที่ต้องแปลงเป็นตาราง ในระหว่างการซิงโครไนซ์คลาสเอนทิตีที่สอดคล้องกันจะพบตามแอตทริบิวต์นี้
(2) ตาราง: แมปชื่อตารางฐานข้อมูล หากชื่อของตารางที่จะแมปจะแตกต่างจากชื่อคลาสเอนทิตีให้ใช้คุณสมบัตินี้เพื่อระบุตารางที่แมป หากไม่มีอยู่ตารางจะถูกสร้างขึ้นตามค่าของคุณสมบัติ
ตรวจสอบโครงสร้างตารางที่สร้างขึ้นโดยการกำหนดค่าในรูปด้านบนดังที่แสดงด้านล่าง:
ชื่อตารางถูกเปลี่ยนเป็น t_user1; ฟิลด์ ID ถูกเปลี่ยนเป็น user_id และความยาวของฟิลด์คือ 32 บิต; คุณสมบัติ createTime ถูกแมปกับฟิลด์ฐานข้อมูล create_time และได้รับการแก้ไขถึงประเภทวันที่
คุณสมบัติ -> ฟิลด์ใช้ ID หรือแท็กคุณสมบัติคุณสมบัติที่ใช้กันทั่วไป:
(1) ชื่อ: ฟังก์ชั่นคล้ายกับชื่อของแท็กคลาสและค่ากำหนดชื่อแอตทริบิวต์การแมปของคลาสเอนทิตี
(2) คอลัมน์: คล้ายกับตารางของแท็กคลาสเอนทิตีระบุชื่อคอลัมน์ของตารางการแมปและจะถูกสร้างขึ้นหากไม่มีอยู่;
(3) พิมพ์: ระบุประเภทข้อมูลที่แมปไปยังฟิลด์ในฐานข้อมูลและดูเอกสารตามต้องการ
(4) เครื่องกำเนิดไฟฟ้าซึ่งเป็นตัวเลือกใช้เพื่อสร้างตัวระบุที่ไม่ซ้ำกันสำหรับคลาสถาวร
<id name = "id" type = "long" คอลัมน์ = "cat_id"> <generator> <param name = "table"> uid_table </param> <param name = "คอลัมน์"> next_hi_value_column
เครื่องกำเนิดไฟฟ้าทั้งหมดใช้อินเตอร์เฟส org.hibernate.id.identifiergenerator นี่เป็นอินเทอร์เฟซที่ง่ายมาก แอปพลิเคชันบางตัวสามารถเลือกที่จะให้การใช้งานเฉพาะของตนเอง แน่นอน Hibernate มีการใช้งานในตัวมากมาย นี่คือประเภทที่ใช้กันทั่วไป:
(1) ข้อมูลประจำตัว: ตัวระบุที่ส่งคืนนั้นมีความยาวประเภทสั้นหรือ int คล้ายกับฟิลด์การผสมพันธุ์ของฐานข้อมูล
(2) ลำดับ: ใช้ลำดับใน DB2, PostgreSQL, Oracle, SAP DB, McKOI และเครื่องกำเนิดไฟฟ้าใน Interbase ตัวระบุที่ส่งคืนนั้นมีความยาว, สั้น, สั้นหรือ int ในฐานข้อมูลทั้งหมดแทนที่จะเพิ่มขึ้นด้วยตนเองในตารางเดียวคุณต้องระบุว่าต้องเพิ่มการเพิ่มขึ้นของตัวเองในตารางเดียว
(3) UUID: ใช้อัลกอริทึม UUID 128 บิตเพื่อสร้างตัวระบุประเภทสตริงซึ่งไม่ซ้ำกันในเครือข่าย (ใช้ที่อยู่ IP) UUID ถูกเข้ารหัสเป็นสตริงของตัวเลขเลขฐานสิบหก 32 บิต คล้ายกับหมายเลขซีเรียลที่สร้างโดย. NET
(4) ดั้งเดิม: เลือกหนึ่งในตัวตนลำดับหรือ HILO ตามความสามารถของฐานข้อมูลพื้นฐาน ในวิธีที่ยืดหยุ่นประเภทตัวตนที่ใช้จะถูกกำหนดตามฐานข้อมูลที่ใช้ MySQL จะเลือก Identity และ Oracle จะเลือกลำดับ
(5) กำหนด: สร้างรหัสประจำตัวด้วยตนเองสำหรับคลาสเอนทิตี นี่คือนโยบายการสร้างเริ่มต้นเมื่อไม่ได้ระบุองค์ประกอบ <senerator>
(6) ต่างประเทศ: ใช้ตัวระบุวัตถุที่เกี่ยวข้องอื่น มักจะใช้ร่วมกับ <หนึ่งต่อหนึ่ง>
นักพัฒนามักจะใช้กับวิธีการกำหนดค่าด้วยตนเองเพื่อเขียนคุณสมบัติการกำหนดค่าตามคำแนะนำเอกสาร นี่เป็นแบบดั้งเดิมมาก ผู้เริ่มต้นแนะนำให้ใช้วิธีการกำหนดค่าด้วยตนเองเพื่อช่วยคิด นอกจากนี้ยังมีเครื่องมือของบุคคลที่สามจำนวนมากที่ใช้วิธีการภาพเพื่อกำหนดค่าและสร้างเอกสารการกำหนดค่า XML ซึ่งปรับปรุงประสิทธิภาพการพัฒนา เครื่องมือที่คล้ายกันเช่น Xdoclet, Middlegen และ Andormda
การแมปแบบเชื่อมโยงมีมากมายต่อหนึ่ง
การทำแผนที่ขั้นพื้นฐานของไฮเบอร์เนตถูกกล่าวถึงข้างต้น คลาสเอนทิตีสอดคล้องกับตารางและใช้การแมปแท็ก <class> ในไฟล์การแมปไฮเบอร์เนตที่สอดคล้องกัน และคุณสมบัติปกติในคลาสเอนทิตีสอดคล้องกับฟิลด์ตารางและแมปโดยใช้แท็ก <porement> นอกจากนี้เมื่อสร้างคลาสเอนทิตีคุณควรให้ความสนใจกับ: ตัวสร้างเริ่มต้นที่ไม่มีพารามิเตอร์ควรนำไปใช้ในคลาสเอนทิตีและควรให้ป้ายกำกับ ขอแนะนำให้ไม่ใช้ขั้นสุดท้ายเพื่อแก้ไขคลาสเอนทิตีและสร้างวิธีการ getter และ setter สำหรับคลาสเอนทิตี ในที่สุดมีการแนะนำกลยุทธ์การสร้างคีย์หลักหลัก ๆ และขั้นตอนต่อไปคือการหารือเกี่ยวกับการทำแผนที่แบบหลายต่อหนึ่ง
การทำแผนที่ความสัมพันธ์แบบหลายต่อหนึ่งนี้สะท้อนให้เห็นในโมเดลวัตถุ มันเป็นความสัมพันธ์ในการรวม ผู้ใช้เป็นส่วนหนึ่งของกลุ่ม มีผู้ใช้ในกลุ่ม วงจรชีวิตของพวกเขาแตกต่างกันและสามารถสะท้อนได้ในรูปต่อไปนี้:
แล้วการทำแผนที่ความสัมพันธ์แบบหลายต่อหนึ่งนี้ตั้งอยู่ในไฮเบอร์เนตอย่างไร? ต่อไปนี้จะแนะนำสองวิธี: ใช้แท็ก <หลายต่อหนึ่งถึงหนึ่ง> เพื่อทำแผนที่โดยตรงหรือใช้ Cascade <หลายต่อหนึ่ง> เพื่อแก้ไขตาราง
1. การแมปโดยตรงแบบหลายต่อหนึ่ง <br /> สามารถเข้าใจได้จากความหมายตามตัวอักษรว่ามันหมายถึงความสัมพันธ์แบบหลายต่อหนึ่ง หลายคนอ้างถึงจุดจบมากขึ้นและอีกคนหนึ่งอ้างถึงจุดจบน้อยลง เมื่อใช้มันแท็กมักจะใช้ใน HBM ของจุดสิ้นสุดมากขึ้นและแอตทริบิวต์ชื่อของ <หลายต่อหนึ่ง> ถูกตั้งค่าเป็นแอตทริบิวต์ของปลายด้านหนึ่งของหนึ่งในคลาสที่สอดคล้องกันของไฟล์การแม็พเช่น: <หลายมหลาย to-name = "กลุ่ม" คอลัมน์ = "groupId"> < แท็กนี้จะถูกเพิ่มใน user.hbm.xml ซึ่งสอดคล้องกับหลาย ๆ ค่าชื่อในแท็กคือกลุ่มเพื่อทำแผนที่หนึ่งและจะมีแอตทริบิวต์ที่เรียกว่ากลุ่มใน user.java จากนั้นมาดูคลาสรหัสเฉพาะที่ใช้การใช้งาน
(1) รหัสคลาส user.java ซึ่งมีคุณสมบัติที่เรียกว่ากลุ่มซึ่งจะใช้เป็นค่าชื่อของปลายด้านหนึ่งของ <หลายต่อหนึ่ง>
ผู้ใช้ระดับสาธารณะ {ชื่อสตริงส่วนตัว; สตริงสาธารณะ getName () {ชื่อคืน; } โมฆะสาธารณะ setName (ชื่อสตริง) {this.name = name; } กลุ่มส่วนตัว; กลุ่มสาธารณะ getGroup () {กลุ่มส่งคืน; } โมฆะสาธารณะ setGroup (กลุ่มกลุ่ม) {this.group = กลุ่ม; -(2) ค่าของชื่อ <หลายต่อหนึ่ง> ใน user.hbm.xml คือค่าคุณสมบัติของด้านเดียวใน user.java มันจะสร้างคอลัมน์ใหม่ในฐานข้อมูลซึ่งสามารถเข้าใจได้ว่าเป็นคีย์ต่างประเทศของตารางผู้ใช้
<? xml เวอร์ชัน = "1.0"?> <! Doctype hibernate-mapping สาธารณะ "-// hibernate/hibernate mapping dtd 3.0 // en" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" 3.4.0.cr1-> <hibernate-mapping> <class name = "com.hibernate.user" table = "user"> <id name = "id" type = "java.lang.long"> <column name = "id" /> <generator /> < /id> <! มันจะสร้างคอลัมน์ในตารางโดยอัตโนมัติดังนั้นคอลัมน์จะถูกเปลี่ยนชื่อโดยใช้คอลัมน์-> <หลายชื่อชื่อ = "กลุ่ม" คอลัมน์ = "groupId"> </หลาย to-to-one> </class> </hibernate-mapping>
(3) ทดสอบความสัมพันธ์การแมปข้างต้นเขียนวัตถุผู้ใช้สองรายการลงในตารางชื่อผู้ใช้ 1 และผู้ใช้ 2 ตั้งชื่อมันจางซานและ li si ใช้เซสชันเพื่อบันทึกวัตถุเขียนข้อมูลลงในฐานข้อมูลรหัสมีดังนี้:
โมฆะสาธารณะ TestSave1 () {เซสชันเซสชัน = null; ลอง {session = getSession.getSession (); session.beginTransaction (); กลุ่มกลุ่ม = กลุ่มใหม่ (); group.setName ("โหนดพลังงาน"); ผู้ใช้ผู้ใช้ 1 = ผู้ใช้ใหม่ (); user1.setName ("จางซาน"); user1.setGroup (กลุ่ม); ผู้ใช้ผู้ใช้ 2 = ผู้ใช้ใหม่ (); user2.setName ("Li Si"); user2.setGroup (กลุ่ม); session.save (user1); session.save (user2); // ข้อผิดพลาด transientObjectException จะถูกรายงาน // ข้อผิดพลาดเกิดขึ้นเมื่อทำความสะอาดแคช TransientObjectException // เนื่องจากกลุ่มเป็นสถานะชั่วคราวมันไม่ได้ถูกลงโทษและไม่มีข้อมูลที่ตรงกันในฐานข้อมูล // เมื่อผู้ใช้เป็นสถานะถาวร session.getTransaction (). commit (); } catch (exception e) {e.printstacktrace (); session.getTransaction (). ย้อนกลับ (); } ในที่สุด {getSession.losesession (เซสชัน); -อย่างไรก็ตามเมื่อใช้รหัสข้างต้นจะมีการรายงาน transientobjectException เมื่อดำเนินการเขียน นี่เป็นเพราะเมื่อบันทึกวัตถุผู้ใช้จะค้นหาวัตถุกลุ่มในหน่วยความจำตามกลุ่มที่เพิ่มใน <หลายต่อหนึ่ง> อย่างไรก็ตามในรหัสข้างต้นวัตถุกลุ่มอยู่ในสถานะชั่วคราวและไม่ได้รับการจัดการโดยเซสชันซึ่งหมายความว่าไม่สามารถพบวัตถุเซสชันและวัตถุผู้ใช้จะเข้าสู่สถานะถาวรดังนั้นข้อผิดพลาดนี้จะถูกรายงาน รหัสที่ถูกต้องมีดังนี้:
โมฆะสาธารณะ testSave2 () {เซสชันเซสชัน = null; ลอง {session = getSession.getSession (); session.beginTransaction (); กลุ่มกลุ่ม = กลุ่มใหม่ (); group.setName ("โหนดพลังงาน"); เซสชัน SAVE (กลุ่ม); // ตั้งค่าวัตถุกลุ่มที่นี่เป็นผู้ใช้วัตถุแบบถาวรผู้ใช้ 1 = ผู้ใช้ใหม่ (); user1.setName ("จางซาน"); user1.setGroup (กลุ่ม); ผู้ใช้ผู้ใช้ 2 = ผู้ใช้ใหม่ (); user2.setName ("Li Si"); user2.setGroup (กลุ่ม); session.save (user1); session.save (user2); // ข้อมูลสามารถบันทึกได้อย่างถูกต้อง // เนื่องจากกลุ่มและผู้ใช้เป็นวัตถุในสถานะถาวร // วัตถุที่เกี่ยวข้องสามารถพบได้ในเซสชั่นเมื่อไฮเบอร์เนตทำความสะอาดเซสชันแคช GetTransaction (). commit (); } catch (exception e) {e.printstacktrace (); session.getTransaction (). ย้อนกลับ (); } ในที่สุด {getSession.losesession (เซสชัน); - 2. การทำแผนที่ cascading
นอกเหนือจากการแปลงทั้งวัตถุกลุ่มและวัตถุผู้ใช้เป็นวัตถุถาวรที่กล่าวถึงข้างต้นคุณยังสามารถใช้แอตทริบิวต์การแมปเรียงซ้อนคาสเคดเพิ่มแอตทริบิวต์ Cascade ในแอตทริบิวต์ <หลายต่อหนึ่ง> และคัดลอกเพื่อบันทึกการอัปเดต คุณสามารถเขียนไปยังฐานข้อมูลเมื่อวัตถุกลุ่มไม่อยู่ในสถานะถาวร ด้วยวิธีนี้คุณจะต้องตั้งค่าแอตทริบิวต์กลุ่มของวัตถุผู้ใช้ทั้งสองเป็นวัตถุกลุ่มเดียวกันเพื่อให้ได้ความสัมพันธ์การทำแผนที่แบบหลายต่อหนึ่ง ในเวลานี้เนื้อหาที่เกี่ยวข้องใน user.hbm.xml เป็นรหัสต่อไปนี้:
<? xml เวอร์ชัน = "1.0"?> <! Doctype hibernate-mapping สาธารณะ "-// hibernate/hibernate mapping dtd 3.0 // en" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" 3.4.0.cr1-> <hibernate-mapping> <class name = "com.hibernate.user" table = "user"> <id name = "id" type = "java.lang.long"> <column name = "id" /> <generator /> < /id> <! Cascade = "Save-update"> </หลายต่อหนึ่ง> </class> </hibernate-mapping>
หมายเหตุ: หลังจากตั้งค่า Cascade เพื่อบันทึกการอัปเดตแล้วสามารถทำการดัดแปลงแบบเรียงซ้อนการเพิ่มและการลบไปยังฐานข้อมูล แต่การดำเนินการคิวรีคิวรีเฉพาะไม่สามารถทำได้
วิธีการทดสอบไฟล์การทดสอบที่สอดคล้องกันมีดังนี้:
// CASCADE CASCADE โมฆะสาธารณะทดสอบ 3 () {เซสชันเซสชัน = null; ลอง {session = getSession.getSession (); session.beginTransaction (); กลุ่มกลุ่ม = กลุ่มใหม่ (); group.setName ("โหนดพลังงาน"); ผู้ใช้ผู้ใช้ 1 = ผู้ใช้ใหม่ (); user1.setName ("จางซาน"); user1.setGroup (กลุ่ม); ผู้ใช้ผู้ใช้ 2 = ผู้ใช้ใหม่ (); user2.setName ("Li Si"); user2.setGroup (กลุ่ม); session.save (user1); session.save (user2); // transientObjectException ไม่ได้ถูกโยนลง // เนื่องจากมีการใช้ cascade // hibernate จะบันทึกกลุ่มวัตถุที่เกี่ยวข้องของผู้ใช้ // กลุ่มและผู้ใช้เป็นวัตถุทั้งสองในเซสชันสถานะถาวร getTransaction (). commit (); } catch (exception e) {e.printstacktrace (); session.getTransaction (). ย้อนกลับ (); } ในที่สุด {getSession.losesession (เซสชัน); - 3. การระเหิดเปรียบเทียบ
ทั้งสองวิธียังใช้วิธีการทำแผนที่แบบหลายต่อหนึ่งและผลลัพธ์ก็เหมือนกัน แต่ก็แตกต่างกันมากในการใช้งาน ไม่ว่าจะเป็นประเภทแรกหรือสองใช้ <หลายต่อหนึ่ง> เพื่อเพิ่มแท็กไปยังไฟล์การแมปที่ปลายหลาย ๆ ด้านและกำหนดแอตทริบิวต์ชื่อของแท็กให้กับค่าแอตทริบิวต์ของปลายด้านหนึ่งของคลาสที่ลงทะเบียนโดยไฟล์การแมป ความแตกต่างคือความสัมพันธ์การแมปโดยตรงไม่ได้ใช้คุณลักษณะของฟิลด์ไฮเบอร์เนตซึ่งมีความยืดหยุ่นในการใช้งานมากขึ้น ไม่เพียง แต่รองรับการเพิ่มการลบและการปรับเปลี่ยน แต่ยังอนุญาตให้ทำการสืบค้น การดัดแปลงคาสเคดที่สองใช้วิธีการที่จัดทำโดยไฮเบอร์เนต วิธีนี้รองรับการเพิ่มการลบและการแก้ไขเท่านั้นและไม่สนับสนุนการสืบค้น