เนื่องจากนี่เป็นคำถามที่พบบ่อยมากฉันจึงเขียน
บทความนี้ขึ้นมาซึ่งคำตอบนี้ขึ้นอยู่กับ
สถานะเอนทิตี
JPA กำหนดสถานะเอนทิตีต่อไปนี้:
ใหม่ (ชั่วคราว)
วัตถุที่สร้างขึ้นใหม่ที่ไม่เคยเกี่ยวข้องกับ Hibernate Session
(aka Persistence Context
) และไม่ได้แมปเข้ากับแถวของตารางฐานข้อมูลใด ๆ จะถือว่าอยู่ในสถานะใหม่ (ชั่วคราว)
ในการที่จะคงอยู่เราต้องเรียกEntityManager#persist
วิธีการอย่างชัดเจนหรือใช้กลไกการคงอยู่ของสกรรมกริยา
ถาวร (จัดการ)
เอนทิตีแบบถาวรนั้นเชื่อมโยงกับแถวตารางฐานข้อมูลและมีการจัดการโดย Persistence Context ที่กำลังทำงานอยู่ การเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นกับเอนทิตีดังกล่าวจะถูกตรวจพบและแพร่กระจายไปยังฐานข้อมูล (ในระหว่างช่วงเวลาล้างเซสชัน)
ด้วย Hibernate เราไม่จำเป็นต้องเรียกใช้คำสั่ง INSERT / UPDATE / DELETE อีกต่อไป Hibernate ใช้รูปแบบการทำงานการเขียนหลังการทำธุรกรรมและการเปลี่ยนแปลงจะถูกทำข้อมูลให้ตรงกันในช่วงเวลาที่รับผิดชอบอย่างมากในช่วงเวลาSession
ล้างข้อมูลปัจจุบัน
สันโดษ
เมื่อบริบทการคงอยู่ที่รันอยู่ในปัจจุบันถูกปิดเอนทิตีที่ได้รับการจัดการก่อนหน้านี้ทั้งหมดจะถูกแยกออก การเปลี่ยนแปลงที่สำเร็จจะไม่ถูกติดตามอีกต่อไปและจะไม่มีการซิงโครไนซ์ฐานข้อมูลอัตโนมัติเกิดขึ้น
การเปลี่ยนสถานะเอนทิตี
คุณสามารถเปลี่ยนสถานะเอนทิตีได้โดยใช้วิธีการต่าง ๆ ที่กำหนดโดยEntityManager
อินเตอร์เฟส
เพื่อให้เข้าใจถึงการเปลี่ยนสถานะของเอนทิตี JPA ได้ดีขึ้นให้พิจารณาแผนภาพต่อไปนี้:
เมื่อใช้ JPA เพื่อเชื่อมโยงเอนทิตีที่EntityManager
ดึงกลับเข้ากับแอคทีฟคุณสามารถใช้การดำเนินการผสาน
เมื่อใช้ Native Hibernate API นอกเหนือจากmerge
คุณสามารถแนบเอนทิตีที่แยกออกไปยัง Hibernate ที่ใช้งานอยู่โดยใช้วิธีการอัพเดตดังที่แสดงในแผนภาพต่อไปนี้:
การผสานเอนทิตีที่แยกออกมา
การผสานจะคัดลอกสถานะเอนทิตีที่ดึงออกมา (ต้นทาง) ไปยังอินสแตนซ์เอนทิตีที่ได้รับการจัดการ (ปลายทาง)
พิจารณาว่าเรายังคงยืนยันBook
เอนทิตีต่อไปนี้และขณะนี้เอนทิตีถูกดึงออกมาตามEntityManager
ที่เคยใช้เพื่อยืนยันเอนทิตีที่ถูกปิด:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
ในขณะที่เอนทิตีอยู่ในสถานะแยกออกเราปรับเปลี่ยนดังต่อไปนี้:
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
ตอนนี้เราต้องการเผยแพร่การเปลี่ยนแปลงไปยังฐานข้อมูลเพื่อให้เราสามารถเรียกmerge
วิธีการ:
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
และไฮเบอร์เนตจะทำการประมวลผลคำสั่ง SQL ต่อไปนี้:
SELECT
b.id,
b.author AS author2_0_,
b.isbn AS isbn3_0_,
b.title AS title4_0_
FROM
book b
WHERE
b.id = 1
-- Merging the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
หากเอนทิตีที่ผสานไม่เทียบเท่าในปัจจุบันEntityManager
สแนปชอตเอนทิตีสดจะถูกดึงมาจากฐานข้อมูล
เมื่อมีเอนทิตีที่จัดการแล้ว JPA จะคัดลอกสถานะของเอนทิตีที่ดึงออกมาไปยังสถานะที่ถูกจัดการอยู่ในปัจจุบันและในระหว่างบริบทการคงอยู่flush
การอัปเดตจะถูกสร้างขึ้นหากกลไกการตรวจสอบสกปรกพบว่า
ดังนั้นเมื่อใช้merge
อินสแตนซ์ออบเจ็กต์ที่ดึงออกมาจะยังคงถูกแยกออกแม้หลังจากการดำเนินการผสาน
ติดตั้งเอนทิตีซ้ำอีกครั้ง
ไฮเบอร์เนต แต่ไม่ใช่ JPA รองรับการติดตั้งupdate
ซ้ำผ่านวิธีการ
Hibernate Session
สามารถเชื่อมโยงวัตถุเอนทิตี้เดียวสำหรับแถวฐานข้อมูลที่กำหนด นี่เป็นเพราะบริบทการมีอยู่ทำหน้าที่เป็นแคชในหน่วยความจำ (แคชระดับแรก) และมีเพียงหนึ่งค่า (เอนทิตี) ที่เชื่อมโยงกับคีย์ที่กำหนด (ประเภทเอนทิตีและตัวระบุฐานข้อมูล)
สามารถแนบเอนทิตีได้ต่อเมื่อไม่มีวัตถุ JVM อื่น ๆ (จับคู่แถวฐานข้อมูลเดียวกัน) ที่เชื่อมโยงกับ Hibernate ปัจจุบันSession
แล้ว
พิจารณาว่าเราได้ยืนยันBook
เอนทิตีและเราได้ทำการแก้ไขเมื่อBook
เอนทิตีอยู่ในสถานะแยกออก:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
เราสามารถแนบเอนทิตี้ที่ดึงกลับได้เช่นนี้:
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
และไฮเบอร์เนตจะดำเนินการคำสั่ง SQL ต่อไปนี้:
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
update
วิธีคุณจะต้องกับ Hibernateunwrap
EntityManager
Session
ซึ่งแตกต่างจากmerge
เอนทิตีที่แยกออกมานั้นจะถูกเชื่อมโยงใหม่กับบริบทการคงอยู่ปัจจุบันและการอัปเดตจะถูกกำหนดเวลาระหว่างล้างข้อมูลว่าเอนทิตีได้ปรับเปลี่ยนหรือไม่
เพื่อป้องกันสิ่งนี้คุณสามารถใช้@SelectBeforeUpdate
คำอธิบายประกอบแบบไฮเบอร์เนตซึ่งจะทริกเกอร์คำสั่ง SELECT ที่ดึงข้อมูลสถานะการโหลดซึ่งจะถูกใช้โดยกลไกการตรวจสอบที่สกปรก
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
ระวัง NonUniqueObjectException
ปัญหาหนึ่งที่สามารถเกิดขึ้นได้update
คือถ้าบริบทการมีอยู่มีการอ้างอิงเอนทิตีที่มี id เดียวกันและเป็นประเภทเดียวกันในตัวอย่างต่อไปนี้:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
try {
doInJPA(entityManager -> {
Book book = entityManager.find(
Book.class,
_book.getId()
);
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(_book);
});
} catch (NonUniqueObjectException e) {
LOGGER.error(
"The Persistence Context cannot hold " +
"two representations of the same entity",
e
);
}
ตอนนี้เมื่อดำเนินการกรณีทดสอบข้างต้น Hibernate จะส่ง a NonUniqueObjectException
เนื่องจากกรณีที่สองEntityManager
มีBook
เอนทิตีที่มีตัวระบุเดียวกับที่เราส่งไปupdate
แล้ว
org.hibernate.NonUniqueObjectException:
A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)
ข้อสรุป
ต้องmerge
ใช้วิธีการนี้หากคุณใช้การล็อคในแง่ดีเนื่องจากจะช่วยป้องกันการอัพเดทที่สูญหาย สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับหัวข้อนี้ตรวจสอบบทความนี้
update
เป็นสิ่งที่ดีสำหรับการปรับปรุงชุดเท่าที่จะสามารถป้องกันไม่ให้เลือกคำสั่งเพิ่มเติมที่เกิดจากmerge
การดำเนินงานจึงช่วยลดเวลาในการดำเนินการปรับปรุงชุด
refresh()
กับเอนทิตี้เดี่ยวหรือไม่? เมื่อมองดูข้อมูลจำเพาะ 2.0 ฉันไม่เห็นเหตุผลใด ๆ แค่ว่ามันไม่ได้รับอนุญาต