2.1 การเชื่อมโยงในระดับตาราง

เราได้เห็นวิธีที่ไฮเบอร์เนตจัดเก็บคอลเลกชันในตารางเสริม ตอนนี้ มาดูวิธีจัดระเบียบความสัมพันธ์ระหว่างตารางแบบเต็มที่เก็บคลาสเอนทิตีจริง

มีความสัมพันธ์สี่ประเภทระหว่างคลาสเอนทิตีในไฮเบอร์เนต:

  • หนึ่งต่อหนึ่ง
  • หนึ่งต่อหลาย
  • หลายต่อหนึ่ง
  • หลายต่อหลาย

และเราจะเริ่มการวิเคราะห์ด้วยตัวเลือกที่ง่ายที่สุด- หลายต่อหนึ่ง

คุณได้พบกับความสัมพันธ์ระหว่างตารางใน SQL แล้ว นี่คือลักษณะที่ปรากฏ:

รหัส ชื่อ อาชีพ เงินเดือน อายุ เข้าร่วม_วันที่
1 อีวานอฟ อีวาน โปรแกรมเมอร์ 100,000 25 2555-06-30
2 เปตรอฟ เปตรอฟ โปรแกรมเมอร์ 80000 23 2013-08-12
3 อีวานอฟ เซอร์เกย์ เทสเตอร์ 40000 สามสิบ 2014-01-01
4 ราบิโนวิช มอยชา ผู้อำนวยการ 200,000 35 2015-05-12
5 คิเรียนโก อนาสตาเซีย ผู้จัดการสำนักงาน 40000 25 2015-10-10
6 วาสก้า แมว 1,000 3 2018-11-11

ตารางพนักงาน:

ตารางนี้มีคอลัมน์ต่อไปนี้:

  • รหัส INT
  • ชื่อวาร์ชาร์
  • อาชีพ VARCHAR
  • INT เงินเดือน
  • อายุ INT
  • join_dateวันที่

และนี่คือลักษณะของตารางงานซึ่งมีงานสำหรับพนักงาน มีลักษณะดังนี้:

รหัส รหัสพนักงาน ชื่อ วันกำหนดส่ง
1 1 แก้ไขข้อบกพร่องในส่วนหน้า 2022-06-01
2 2 แก้ไขข้อบกพร่องในส่วนหลัง 2022-06-15
3 5 ซื้อกาแฟ 2022-07-01
4 5 ซื้อกาแฟ 2022-08-01
5 5 ซื้อกาแฟ 2022-09-01
6 (โมฆะ) ทำความสะอาดสำนักงาน (โมฆะ)
7 4 ใช้ชีวิตให้สนุก (โมฆะ)
8 6 ใช้ชีวิตให้สนุก (โมฆะ)

ตารางนี้มีเพียง 4 คอลัมน์:

  • id – หมายเลขงานเฉพาะ (และแถวในตาราง);
  • Employee_id – รหัสพนักงานจากตารางพนักงานที่มอบหมายงาน
  • ชื่อ – ชื่อและคำอธิบายของงาน
  • เส้นตาย - เวลาที่ต้องทำงานให้เสร็จ

เราเห็นว่าหลายแถวในตารางงานสามารถอ้างถึงรายการเดียวในตารางพนักงาน ความสัมพันธ์ระดับตารางดังกล่าวเรียกว่ากลุ่มต่อหนึ่ง

2.2 ความสัมพันธ์กับระดับคลาส Java

นอกจากการสื่อสารในระดับตารางแล้ว คุณยังสามารถจัดระเบียบการสื่อสารในระดับคลาสเอนทิตีในไฮเบอร์เนตได้อีกด้วย สิ่งนี้ทำได้ด้วยคำอธิบาย@ManyToOneประกอบ

แต่ก่อนอื่นมาสร้างสองคลาส: EmployeeและEmployeeTask :


@Entity
@Table(name="employee")
class Employee {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String name;
 
   @Column(name="occupation")
   public String occupation;
 
   @Column(name="salary")
   public Integer salary;
 
   @Column(name="join_date")
   public Date join;
}

และชั้นสองในการจัดเก็บงานของพนักงาน:


@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String description;
 
  @Column(name="employee_id")
   public Integer employeeId;
 
   @Column(name="deadline")
   public Date deadline;
}

ทั้งหมดนี้เป็นไปได้ด้วยดีกับคลาสเหล่านี้ แต่ไม่มีความสัมพันธ์ระหว่างคลาสเหล่านี้ที่จะสะท้อนข้อเท็จจริงที่ว่าฟิลด์ EmployeeId ของคลาส EmployeeTask อ้างอิงถึงฟิลด์ id ของคลาส Employee ถึงเวลาแก้ไขแล้ว

2.3 คำอธิบายประกอบ @ManyToOne

ประการแรก ใน Java เราคุ้นเคยกับการดำเนินการกับวัตถุ (และการอ้างอิงวัตถุ) มากกว่ารหัสของมัน ก่อนอื่น แทนที่จะเป็นฟิลด์ EmployeeId ในคลาส EmployeeTask ให้ชี้ไปที่วัตถุประเภท Employee นี่คือลักษณะของชั้นเรียนใหม่ของเรา:


@Entity
@Table(name="task")
class EmployeeTask {
   @Column(name="id")
   public Integer id;
 
   @Column(name="name")
   public String description;
 
   @ManyToOne
   @JoinColumn(name = "employee_id")
   public Employee employee;
 
   @Column(name="deadline")
   public Date deadline;
}

ด้วยความช่วยเหลือของคำอธิบายประกอบ @ManyToOneเราได้ระบุว่าอ็อบเจ็กต์ EmployeeTask จำนวนมากสามารถอ้างถึงอ็อบเจ็กต์เดียวประเภท Employee นอกจากนี้ เมื่อใช้คำอธิบาย @JoinColumnประกอบ เราระบุว่าคอลัมน์ใดในตารางของเราที่มีการจัดเก็บ id ของวัตถุพนักงาน

2.4 ขอตัวอย่าง

และตอนนี้เรามาแสดงตัวอย่างว่า Hibernate สามารถทำงานร่วมกับคลาสที่เกี่ยวข้องดังกล่าวได้อย่างไร

สถานการณ์ที่หนึ่ง

มาเขียนแบบสอบถามเพื่อค้นหางานทั้งหมดที่ได้รับมอบหมายให้กับผู้ใช้เฉพาะราย นี่คือลักษณะของแบบสอบถามนี้ใน HQL:

from EmployeeTask where employee.name = "Ivan Ivanovich"

คุณสามารถอ้างถึงฟิลด์ของคลาสที่ขึ้นต่อกันได้โดยใช้จุด มันสะดวกสบายมาก แต่เรายังคงเขียนแบบสอบถามนี้ในรูปแบบของรหัส Java:


String hql = "from EmployeeTask where employee.name = :username";
Query<EmployeeTask> query = session.createQuery( hql, EmployeeTask.class);
query.setParameter("username", "Ivan Ivanovich");
List<EmployeeTask> resultLIst = query.list();

สถานการณ์ที่สอง

ลองเขียนแบบสอบถามที่ส่งคืนรายชื่อพนักงานที่มีงานค้างชำระ งานจะเกินกำหนดหากถึงกำหนดส่งในอดีต ต่อไปนี้คือลักษณะของแบบสอบถามใน SQL:


SELECT DISTINCT employee.*
FROM task JOIN employee ON task.employee_id = employee.id
WHERE task.deadline < CURDATE();

DISTINCTใช้เนื่องจากสามารถมีงานหลายอย่างที่กำหนดให้กับผู้ใช้คนเดียว

และตอนนี้มาเขียนแบบสอบถามเดียวกันใน HQL:

select distinct employee from EmployeeTask where deadline < CURDATE();

พนักงานในแบบสอบถามนี้เป็นเขตข้อมูลของคลาส EmployeeTask

สถานการณ์ที่สาม

มอบหมายงานที่ยังไม่ได้รับมอบหมายทั้งหมดให้กับผู้อำนวยการ แบบสอบถาม SQL จะมีลักษณะดังนี้:


UPDATE task SET employee_id = 4 WHERE employee_id IS NULL

และตอนนี้มาเขียนแบบสอบถามเดียวกันใน HQL:

update EmployeeTask set employee = :user where employee is null

คำถามสุดท้ายคือคำถามที่ยากที่สุด เราจำเป็นต้องส่ง ID ของผู้อำนวยการ แต่คลาส EmployeeTask ไม่มีฟิลด์ที่เราสามารถเขียน id ได้ แต่จะประกอบด้วยฟิลด์ Employee ซึ่งเราจำเป็นต้องกำหนดการอ้างอิงให้กับวัตถุประเภท Employee


Employee director = session.get(Employee.class, 4);
 
String hql = "update EmployeeTask set employee = :user where employee is null";
Query<EmployeeTask> query = session.createQuery(hql, EmployeeTask.class);
query.setParameter("user", director);
query.executeUpdate();