CascadeType.REMOVE และ orphanRemoval ใน JPA ต่างกันอย่างไร


111

อะไรคือความแตกต่างระหว่าง

@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

และ

@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }

ตัวอย่างนี้มาจาก Java EE Tutorial แต่ฉันยังไม่เข้าใจรายละเอียด


การลบ Orphan หมายความว่าเอนทิตีที่อ้างอิงจะถูกลบออกเมื่อความสัมพันธ์กับเอนทิตี "หลัก" ถูกทำลาย
Rahul Tripathi

1
เขียนกรณีทดสอบที่อาจแสดงแนวคิด
Martin Andersson

คำตอบ:


158

จากที่นี่ : -

Cascading Remove

การทำเครื่องหมายฟิลด์อ้างอิงด้วย CascadeType.REMOVE (หรือ CascadeType.ALL ซึ่งรวมถึง REMOVE) บ่งชี้ว่าการดำเนินการลบควรเรียงซ้อนกันโดยอัตโนมัติไปยังเอนทิตีอ็อบเจ็กต์ที่อ้างถึงโดยฟิลด์นั้น (อ็อบเจ็กต์เอนทิตีหลายรายการสามารถอ้างอิงได้โดยฟิลด์คอลเลกชัน):

@Entity
class Employee {
     :
    @OneToOne(cascade=CascadeType.REMOVE)
    private Address address;
     :
}

การกำจัดเด็กกำพร้า

JPA 2 รองรับโหมดลบ cascading เพิ่มเติมและก้าวร้าวมากขึ้นซึ่งสามารถระบุได้โดยใช้องค์ประกอบ orphanRemoval ของคำอธิบายประกอบ @OneToOne และ @OneToMany:

@Entity
class Employee {
     :
    @OneToOne(orphanRemoval=true)
    private Address address;
     :
}

ความแตกต่าง: -

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

  • หากระบุ orphanRemoval = trueอินสแตนซ์ที่อยู่ที่ไม่ได้เชื่อมต่อจะถูกลบออกโดยอัตโนมัติ สิ่งนี้มีประโยชน์สำหรับการล้างวัตถุที่อ้างอิง (เช่น Address) ที่ไม่ควรมีอยู่โดยไม่มีการอ้างอิงจากวัตถุเจ้าของ (เช่นพนักงาน)
  • หากระบุเฉพาะcascade = CascadeType.REMOVEจะไม่มีการดำเนินการอัตโนมัติเนื่องจากการยกเลิกการเชื่อมต่อความสัมพันธ์ไม่ใช่การ
    ดำเนินการลบ

91

เป็นวิธีที่ง่ายต่อการเข้าใจความแตกต่างระหว่างและCascadeType.REMOVEorphanRemoval=true

สำหรับการลบเด็กกำพร้า: หากคุณเรียกใช้เอนทิตีsetOrders(null)ที่เกี่ยวข้องOrderจะถูกลบออกใน db โดยอัตโนมัติ

สำหรับการลบ cascade: หากคุณเรียกใช้เอนทิตีsetOrders(null)ที่เกี่ยวข้องOrderจะไม่ถูกลบออกใน db โดยอัตโนมัติ


3
ลบ === ลบ
อับดุล

11

สมมติว่าเรามีเอนทิตีลูกและเอนทิตีแม่ ผู้ปกครองสามารถมีลูกได้หลายคน

@Entity
class parent {
  //id and other fields
 @OneToMany (orphanRemoval = "true",cascade = CascadeType.REMOVE)
   Set<Person> myChildern;
}

orphanRemoval เป็นแนวคิดของ ORM ซึ่งจะบอกว่าเด็กกำพร้าหรือไม่ ควรลบออกจากฐานข้อมูลด้วย

เด็กเป็นเด็กกำพร้าเมื่อไม่สามารถเข้าถึงได้จากผู้ปกครอง ตัวอย่างเช่นถ้าเราลบชุดวัตถุบุคคล (ตั้งค่าเป็นชุดว่าง) หรือแทนที่ด้วยชุดใหม่ผู้ปกครองจะไม่สามารถเข้าถึงเด็กในชุดเก่าได้อีกต่อไปและเด็ก ๆ จะถูกทอดทิ้งดังนั้นเด็ก ๆ จึงถึงวาระที่จะเป็น ลบออกในฐานข้อมูลด้วย

CascadeType.REMOVE เป็นแนวคิดระดับฐานข้อมูลและจะบอกว่าพาเรนต์ถูกลบออกเรกคอร์ดที่เกี่ยวข้องทั้งหมดในตารางลูกควรถูกลบออก


คำตอบนี้ดูมีเหตุผลมากกว่าคำตอบอื่น ๆ ในชุดข้อความนี้
Jignesh M. Khatri

2

ความแตกต่างในทางปฏิบัติอยู่ที่ว่าคุณพยายามอัปเดตข้อมูล (PATCH) หรือแทนที่ข้อมูลทั้งหมด (PUT)

สมมติว่าคุณลบการcustomerใช้กว่าcascade=REMOVEจะเป็นการลบคำสั่งซื้อของลูกค้าที่ดูเหมือนตั้งใจและมีประโยชน์

@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

ตอนนี้สมมติว่าคุณอัปเดต a customerโดยorphanRemoval="true"จะลบคำสั่งซื้อก่อนหน้าทั้งหมดและแทนที่ด้วยคำสั่งซื้อที่ให้มา ( PUTในแง่ของREST API)

@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }

หากไม่มีorphanRemovalคำสั่งซื้อเก่าจะถูกเก็บไว้ ( PATCHในแง่ของREST API)


2

เนื่องจากคำถามนี้เป็นคำถามที่พบบ่อยมากคำตอบนี้จึงมาจากบทความที่ฉันเขียนในบล็อกของฉัน

CascadeType ลบ

CascadeType.REMOVEกลยุทธ์ซึ่งคุณสามารถกำหนดค่าอย่างชัดเจน:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.REMOVE
)
private List<PostComment> comments = new ArrayList<>();

หรือสืบทอดโดยปริยายจากCascadeType.ALLกลยุทธ์:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL
)
private List<PostComment> comments = new ArrayList<>();

อนุญาตให้คุณเผยแพร่การremoveดำเนินการจากเอนทิตีแม่ไปยังเอนทิตีลูก

ดังนั้นหากเราดึงPostเอนทิตีหลักพร้อมกับcommentsคอลเลกชันและลบpostเอนทิตี:

Post post = entityManager.createQuery("""
    select p
    from Post p
    join fetch p.comments
    where p.id = :id
    """, Post.class)
.setParameter("id", postId)
.getSingleResult();

entityManager.remove(post);

ไฮเบอร์เนตจะเรียกใช้คำสั่งลบสามคำสั่ง:

DELETE FROM post_comment 
WHERE id = 2

DELETE FROM post_comment 
WHERE id = 3

DELETE FROM post 
WHERE id = 1

PostCommentหน่วยงานที่เด็กถูกลบเนื่องจากCascadeType.REMOVEกลยุทธ์ซึ่งทำหน้าที่เป็นถ้าเราได้นำหน่วยงานของเด็กได้เป็นอย่างดี

กลยุทธ์การกำจัดเด็กกำพร้า

กลยุทธ์การกำจัดเด็กกำพร้าซึ่งต้องตั้งค่าผ่านorphanRemovalแอตทริบิวต์:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

อนุญาตให้คุณลบแถวตารางลูกเมื่อลบเอนทิตีลูกออกจากคอลเลกชัน

ดังนั้นหากเราโหลดPostเอนทิตีพร้อมกับcommentsคอลเล็กชันและลบรายการแรกPostCommentออกจากcommentsคอลเล็กชัน:

Post post = entityManager.createQuery("""
    select p
    from Post p
    join fetch p.comments c
    where p.id = :id
    order by p.id, c.id
    """, Post.class)
.setParameter("id", postId)
.getSingleResult();

post.remove(post.getComments().get(0));

ไฮเบอร์เนตจะดำเนินการคำสั่ง DELETE สำหรับpost_commentแถวตารางที่เกี่ยวข้อง:

DELETE FROM post_comment 
WHERE id = 2

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับหัวข้อนี้โปรดอ่านบทความนี้เช่นกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.