เนื่องจากนี่เป็นคำถามที่พบบ่อยมากฉันจึงเขียน
บทความนี้ขึ้นมาซึ่งคำตอบนี้ขึ้นอยู่กับ
สถานะเอนทิตี
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วิธีคุณจะต้องกับ HibernateunwrapEntityManagerSession
ซึ่งแตกต่างจาก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 ฉันไม่เห็นเหตุผลใด ๆ แค่ว่ามันไม่ได้รับอนุญาต