ทุกคนสามารถบอกฉันได้ว่าอะไรคือข้อได้เปรียบของpersist()
vs save()
ใน Hibernate?
ทุกคนสามารถบอกฉันได้ว่าอะไรคือข้อได้เปรียบของpersist()
vs save()
ใน Hibernate?
คำตอบ:
persist()
มีการกำหนดไว้อย่างดี มันทำให้อินสแตนซ์ชั่วคราวยังคงอยู่ อย่างไรก็ตามไม่รับประกันว่าจะมีการกำหนดค่าตัวระบุให้กับอินสแตนซ์ถาวรทันทีการกำหนดอาจเกิดขึ้นในเวลาล้างข้อมูลpersist()
ข้อมูลจำเพาะไม่ได้บอกว่าซึ่งเป็นปัญหาที่ฉันมีกับ
persist()
ยังรับประกันว่าจะไม่ดำเนินการคำสั่ง INSERT ถ้ามันถูกเรียกว่าอยู่นอกขอบเขตการทำธุรกรรม สิ่งนี้มีประโยชน์ในการสนทนาที่ใช้เวลานานกับบริบทเซสชัน / การคงอยู่ที่ขยายเพิ่ม
persist()
จำเป็นต้องใช้วิธีการเช่น
save()
ไม่รับประกันเหมือนกันมันจะส่งคืนตัวระบุและถ้า INSERT จะต้องถูกดำเนินการเพื่อให้ได้ตัวระบุ (เช่น "identity" generator ไม่ใช่ "ลำดับ") INSERT นี้จะเกิดขึ้นทันทีไม่ว่าคุณจะอยู่ภายในหรือภายนอก การทำธุรกรรม สิ่งนี้ไม่ดีในการสนทนาระยะยาวที่มีบริบทเซสชัน / การคงอยู่ที่ขยายออกไป
ฉันได้ทำการวิจัยที่ดีเกี่ยวกับ save () vs persist () รวมถึงการเรียกใช้บนเครื่องท้องถิ่นของฉันหลายครั้ง คำอธิบายก่อนหน้านี้ทั้งหมดทำให้เกิดความสับสนและไม่ถูกต้อง ฉันได้เปรียบเทียบ save () และ persist () ด้านล่างหลังจากการวิจัยอย่างละเอียด
Save()
Serializable
กลับมาประเภทPersist()
generated id
ให้กับเอนทิตีที่คุณยังคงอยู่session.persist()
สำหรับวัตถุเดี่ยวจะถูกโยนPersistentObjectException
ตามที่ไม่ได้รับอนุญาตทั้งหมดเหล่านี้จะพยายาม / Hibernate v4.0.1
การทดสอบเกี่ยวกับ
Save()
หรือไม่
ฉันไม่ได้เยาะเย้ยบางการทดสอบเพื่อบันทึกความแตกต่างระหว่างและsave()
persist()
ดูเหมือนว่าทั้งสองวิธีนี้จะทำงานเหมือนกันเมื่อจัดการกับ Transient Entity แต่ต่างกันเมื่อจัดการกับ Detached Entity
สำหรับตัวอย่างด้านล่างให้นำ EmployeeVehicle เป็นเอนทิตีที่มี PK vehicleId
ซึ่งเป็นค่าที่สร้างขึ้นและvehicleName
เป็นหนึ่งในคุณสมบัติของมัน
ตัวอย่างที่ 1: การจัดการกับวัตถุชั่วคราว
Session session = factory.openSession();
session.beginTransaction();
EmployeeVehicle entity = new EmployeeVehicle();
entity.setVehicleName("Honda");
session.save(entity);
// session.persist(entity);
session.getTransaction().commit();
session.close();
ผลลัพธ์:
select nextval ('hibernate_sequence') // This is for vehicle Id generated : 36
insert into Employee_Vehicle ( Vehicle_Name, Vehicle_Id) values ( Honda, 36)
หมายเหตุผลลัพธ์จะเหมือนกันเมื่อคุณได้รับวัตถุที่คงอยู่แล้วและบันทึก
EmployeeVehicle entity = (EmployeeVehicle)session.get(EmployeeVehicle.class, 36);
entity.setVehicleName("Toyota");
session.save(entity); -------> **instead of session.update(entity);**
// session.persist(entity);
ทำซ้ำเช่นเดียวกันโดยใช้ persist(entity)
และจะส่งผลเหมือนกันกับรหัสใหม่ (เช่น 37, ฮอนด้า)
ตัวอย่างที่ 2: การจัดการกับวัตถุที่ถูกแยกออก
// Session 1
// Get the previously saved Vehicle Entity
Session session = factory.openSession();
session.beginTransaction();
EmployeeVehicle entity = (EmployeeVehicle)session.get(EmployeeVehicle.class, 36);
session.close();
// Session 2
// Here in Session 2 , vehicle entity obtained in previous session is a detached object and now we will try to save / persist it
// (i) Using Save() to persist a detached object
Session session2 = factory.openSession();
session2.beginTransaction();
entity.setVehicleName("Toyota");
session2.save(entity);
session2.getTransaction().commit();
session2.close();
ผลลัพธ์: คุณอาจคาดหวังว่ายานพาหนะที่มีรหัส: 36 ที่ได้รับในเซสชันก่อนหน้านี้จะได้รับการอัปเดตด้วยชื่อเป็น "โตโยต้า" แต่สิ่งที่เกิดขึ้นคือมีการบันทึกเอนทิตีใหม่ในฐานข้อมูลด้วยรหัสใหม่ที่สร้างขึ้นและชื่อเป็น "โตโยต้า"
select nextval ('hibernate_sequence')
insert into Employee_Vehicle ( Vehicle_Name, Vehicle_Id) values ( Toyota, 39)
การใช้การยืนยันเพื่อยืนยันการดึงเอนทิตี้ออก
// (ii) Using Persist() to persist a detached
// Session 1
Session session = factory.openSession();
session.beginTransaction();
EmployeeVehicle entity = (EmployeeVehicle)session.get(EmployeeVehicle.class, 36);
session.close();
// Session 2
// Here in Session 2 , vehicle entity obtained in previous session is a detached object and now we will try to save / persist it
// (i) Using Save() to persist a detached
Session session2 = factory.openSession();
session2.beginTransaction();
entity.setVehicleName("Toyota");
session2.persist(entity);
session2.getTransaction().commit();
session2.close();
ผลลัพธ์:
Exception being thrown : detached entity passed to persist
ดังนั้นจะเป็นการดีกว่าถ้าจะใช้ Persist () แทน Save () เนื่องจากการบันทึกจะต้องใช้อย่างระมัดระวังเมื่อจัดการกับวัตถุ Transient
หมายเหตุสำคัญ: ในตัวอย่างด้านบน pk ของเอนทิตียานพาหนะเป็นค่าที่สร้างขึ้นดังนั้นเมื่อใช้การบันทึก () เพื่อคงเอนทิตีที่แยกออกไว้ hibernate จะสร้าง id ใหม่เพื่อคงอยู่ อย่างไรก็ตามหาก pk นี้ไม่ใช่ค่าที่สร้างขึ้นกว่าจะส่งผลให้เกิดข้อยกเว้นที่ระบุว่าละเมิดคีย์
คำถามนี้มีคำตอบที่ดีเกี่ยวกับวิธีการคงอยู่ที่แตกต่างกันในไฮเบอร์เนต หากต้องการตอบคำถามของคุณโดยตรงด้วยการบันทึก () คำสั่งแทรกจะถูกดำเนินการทันทีโดยไม่คำนึงถึงสถานะของธุรกรรม มันส่งคืนคีย์ที่แทรกไว้เพื่อให้คุณสามารถทำสิ่งนี้:
long newKey = session.save(myObj);
ดังนั้นให้ใช้ save () หากคุณต้องการตัวระบุที่กำหนดให้กับอินสแตนซ์ถาวรทันที
ด้วย persist () คำสั่ง insert จะถูกดำเนินการในการทำธุรกรรมไม่จำเป็นต้องได้ทันที ซึ่งเป็นที่นิยมในกรณีส่วนใหญ่
ใช้ persist () หากคุณไม่ต้องการให้การแทรกเกิดขึ้นผิดลำดับกับการทำธุรกรรมและคุณไม่จำเป็นต้องส่งคืนคีย์การแทรก
นี่คือความแตกต่างที่สามารถช่วยให้คุณเข้าใจถึงข้อดีของการคงอยู่และวิธีการบันทึก:
วิธีการคงอยู่ () ไม่รับประกันว่าค่าตัวระบุจะถูกกำหนดให้กับสถานะถาวรทันทีการมอบหมายอาจเกิดขึ้นในเวลาล้าง
วิธีการคงอยู่ () จะไม่ดำเนินการแบบสอบถามแทรกถ้ามันถูกเรียกว่านอกขอบเขตของการทำธุรกรรม ในขณะที่วิธีการบันทึก () ส่งกลับตัวระบุเพื่อให้แบบสอบถามแทรกจะถูกดำเนินการทันทีเพื่อให้ได้ตัวระบุไม่ว่าจะอยู่ภายในหรือภายนอกธุรกรรม
วิธีการคงอยู่นั้นเรียกว่าอยู่นอกขอบเขตของธุรกรรมซึ่งมีประโยชน์ในการสนทนาที่ใช้เวลานานกับบริบทเซสชันที่ขยายเพิ่มเติม ในทางกลับกันวิธีการบันทึกไม่ดีในการสนทนาที่ใช้เวลานานกับบริบทเซสชันที่ขยาย
ความแตกต่างที่ห้าระหว่างวิธีการบันทึกและวิธีคงอยู่ใน Hibernate: JPA ได้รับการสนับสนุนอย่างต่อเนื่องขณะที่ Hibernate รองรับการบันทึกเท่านั้น
คุณสามารถดูตัวอย่างการทำงานเต็มรูปแบบจากความแตกต่างของการโพสต์ระหว่างการบันทึกและการคงอยู่ของวิธีการในไฮเบอร์เนต
save () - ตามที่ชื่อเมธอดแนะนำ, hibernate save () สามารถใช้เพื่อบันทึกเอนทิตีไปยังฐานข้อมูล เราสามารถเรียกใช้วิธีนี้นอกธุรกรรม หากเราใช้สิ่งนี้โดยไม่มีการทำธุรกรรมและเรามีการเชื่อมโยงระหว่างหน่วยงานดังนั้นเฉพาะหน่วยงานหลักที่ได้รับการบันทึกเว้นแต่ว่าเราจะทำการล้างข้อมูล
persist () - Hibernate persist จะคล้ายกับการบันทึก (ด้วยธุรกรรม) และจะเพิ่มวัตถุเอนทิตีในบริบทถาวรดังนั้นการเปลี่ยนแปลงเพิ่มเติมใด ๆ จะถูกติดตาม หากคุณสมบัติของวัตถุมีการเปลี่ยนแปลงก่อนที่จะทำธุรกรรมหรือล้างเซสชั่นก็จะถูกบันทึกลงในฐานข้อมูล นอกจากนี้เราสามารถใช้วิธี persist () ภายในขอบเขตของการทำธุรกรรมเท่านั้นดังนั้นจึงปลอดภัยและดูแลวัตถุที่เรียงซ้อนใด ๆ สุดท้ายยังคงมีอยู่ไม่ส่งคืนสิ่งใดดังนั้นเราจึงจำเป็นต้องใช้วัตถุที่ยังคงอยู่เพื่อรับค่าตัวระบุที่สร้างขึ้น
นี่คือความแตกต่าง:
ประหยัด:
ยังคงมีอยู่:
กฎพื้นฐานบอกว่า:
สำหรับเอนทิตีที่มีตัวระบุที่สร้างขึ้น:
save (): มันส่งกลับตัวระบุของกิจการทันทีนอกเหนือจากการทำให้วัตถุถาวร ดังนั้นแบบสอบถามแทรกจะถูกยิงทันที
persist (): มันจะคืนค่าวัตถุถาวร ไม่มีการบังคับให้ส่งคืนตัวบ่งชี้ทันทีดังนั้นจึงไม่รับประกันว่าเม็ดมีดจะถูกใช้งานทันที มันอาจยิงเม็ดมีดได้ทันที แต่ไม่รับประกัน ในบางกรณีแบบสอบถามอาจถูกเรียกใช้ทันทีในขณะที่ในกรณีอื่น ๆ อาจถูกเรียกใช้ในเวลาล้างเซสชัน
สำหรับเอนทิตีที่มีตัวระบุที่กำหนด:
save (): มันส่งกลับตัวระบุของกิจการทันที เนื่องจากตัวระบุถูกกำหนดให้กับเอนทิตีก่อนที่จะเรียกการบันทึกดังนั้นการแทรกจะไม่ถูกเรียกใช้ทันที มันถูกไล่ออกในเวลาที่ล้างข้อมูลเซสชัน
persist (): เหมือนกับ save มันยังแทรกไฟในเวลาล้าง
สมมติว่าเรามีนิติบุคคลที่ใช้ตัวระบุที่สร้างขึ้นดังต่อไปนี้:
@Entity
@Table(name="USER_DETAILS")
public class UserDetails {
@Id
@Column(name = "USER_ID")
@GeneratedValue(strategy=GenerationType.AUTO)
private int userId;
@Column(name = "USER_NAME")
private String userName;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
บันทึก ():
Session session = sessionFactory.openSession();
session.beginTransaction();
UserDetails user = new UserDetails();
user.setUserName("Gaurav");
session.save(user); // Query is fired immediately as this statement is executed.
session.getTransaction().commit();
session.close();
ยังคงมีอยู่ ():
Session session = sessionFactory.openSession();
session.beginTransaction();
UserDetails user = new UserDetails();
user.setUserName("Gaurav");
session.persist(user); // Query is not guaranteed to be fired immediately. It may get fired here.
session.getTransaction().commit(); // If it not executed in last statement then It is fired here.
session.close();
ตอนนี้สมมติว่าเรามีเอนทิตีเดียวกันที่กำหนดไว้ดังต่อไปนี้โดยไม่มีฟิลด์ id ที่มีคำอธิบายประกอบที่สร้างขึ้นเช่น ID จะถูกกำหนดด้วยตนเอง
@Entity
@Table(name="USER_DETAILS")
public class UserDetails {
@Id
@Column(name = "USER_ID")
private int userId;
@Column(name = "USER_NAME")
private String userName;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
สำหรับบันทึก ():
Session session = sessionFactory.openSession();
session.beginTransaction();
UserDetails user = new UserDetails();
user.setUserId(1);
user.setUserName("Gaurav");
session.save(user); // Query is not fired here since id for object being referred by user is already available. No query need to be fired to find it. Data for user now available in first level cache but not in db.
session.getTransaction().commit();// Query will be fired at this point and data for user will now also be available in DB
session.close();
เพื่อคงอยู่ ():
Session session = sessionFactory.openSession();
session.beginTransaction();
UserDetails user = new UserDetails();
user.setUserId(1);
user.setUserName("Gaurav");
session.persist(user); // Query is not fired here.Object is made persistent. Data for user now available in first level cache but not in db.
session.getTransaction().commit();// Query will be fired at this point and data for user will now also be available in DB
session.close();
กรณีข้างต้นเป็นจริงเมื่อบันทึกหรือยืนยันถูกเรียกจากภายในธุรกรรม
จุดแตกต่างอื่น ๆ ระหว่างบันทึกและคงอยู่คือ:
สามารถบันทึก () นอกธุรกรรม หากมีการใช้ตัวระบุที่กำหนดไว้แล้วเนื่องจาก id พร้อมใช้งานแล้วดังนั้นจึงไม่มีการเรียกใช้การแทรกทันที แบบสอบถามจะเริ่มทำงานเมื่อล้างเซสชันแล้วเท่านั้น
หากมีการใช้ตัวระบุที่สร้างขึ้นแล้วเนื่องจากจำเป็นต้องสร้าง id การแทรกจะเริ่มทำงานทันที แต่มันจะบันทึกเอนทิตีหลักเท่านั้น หากเอนทิตีมีเอนทิตีแบบเรียงซ้อนบางส่วนจะไม่ถูกบันทึกใน db ณ จุดนี้ พวกเขาจะถูกบันทึกเมื่อเซสชันถูกล้างข้อมูล
หากยังคงมีอยู่ () อยู่นอกการทำธุรกรรมการแทรกจะถูกใช้ก็ต่อเมื่อเซสชันถูกล้างข้อมูลไม่ว่าจะใช้ตัวระบุชนิดใด (สร้างหรือกำหนด)
หากมีการบันทึกการเรียกใช้บนวัตถุที่มีอยู่อย่างถาวรเอนทิตีจะถูกบันทึกโดยใช้แบบสอบถามแบบใช้ปรับปรุงข้อมูล
ที่จริงแล้วความแตกต่างระหว่างเมธอด hibernate save () และ persist () ขึ้นอยู่กับคลาสของตัวสร้างที่เราใช้
หากคลาสตัวสร้างของเราได้รับการกำหนดดังนั้นจะไม่มีความแตกต่างระหว่างวิธีการบันทึก () และการยืนยัน () เนื่องจากเครื่องกำเนิด 'กำหนด' หมายถึงในฐานะโปรแกรมเมอร์เราจำเป็นต้องให้ค่าคีย์หลักเพื่อบันทึกในฐานข้อมูลที่ถูกต้อง [หวังว่าคุณจะรู้แนวคิดเครื่องกำเนิดไฟฟ้านี้] ในกรณีอื่นนอกเหนือจากระดับเครื่องกำเนิดไฟฟ้าที่กำหนดให้สมมติว่าชื่อระดับเครื่องกำเนิดของเราเพิ่มขึ้น จำศีลเองจะกำหนดค่ารหัสคีย์หลักในฐานข้อมูลด้านขวา [อื่น ๆ นอกเหนือจากเครื่องกำเนิดที่ได้รับมอบหมายจำศีลใช้เฉพาะในการดูแลค่ารหัสคีย์หลักจำ] ดังนั้นในกรณีนี้ถ้าเราเรียกวิธีการบันทึก () หรือคงอยู่ () มันจะแทรกบันทึกลงในฐานข้อมูลตามปกติ
แต่ที่นี่สิ่งคือวิธีการ save () สามารถคืนค่ารหัสคีย์หลักที่สร้างขึ้นโดยจำศีลและเราสามารถดูได้โดย
ยาว s = session.save (k);
ในกรณีเดียวกันนี้ persist () จะไม่ให้ค่าใด ๆ กลับไปยังลูกค้ากลับเป็นโมฆะประเภท
persist () ยังรับประกันว่าจะไม่ดำเนินการคำสั่ง INSERT ถ้ามันถูกเรียกว่าอยู่นอกขอบเขตการทำธุรกรรม
ในขณะที่ save () INSERT จะเกิดขึ้นทันทีไม่ว่าคุณจะอยู่ภายในหรือภายนอกธุรกรรม
มันตอบอย่างสมบูรณ์บนพื้นฐานของ "กำเนิด" ประเภทใน ID ในขณะที่การจัดเก็บนิติบุคคลใด ๆ หากค่าของตัวสร้างเป็น "กำหนด" ซึ่งหมายความว่าคุณกำลังจัดหา ID จากนั้นก็ไม่ทำให้จำศีลในการบันทึกหรือคงอยู่ คุณสามารถไปกับวิธีการใด ๆ ที่คุณต้องการ หากค่าไม่ได้ "กำหนด" และคุณกำลังใช้ save () คุณจะได้รับ ID เป็นผลตอบแทนจากการดำเนินการบันทึก ()
การตรวจสอบอื่นคือถ้าคุณกำลังดำเนินการอยู่นอกวงเงินการทำธุรกรรมหรือไม่ เนื่องจากยังคงอยู่ () เป็นของ JPA ขณะที่บันทึก () สำหรับการจำศีล ดังนั้นการใช้ persist () นอกขอบเขตการทำธุรกรรมจะไม่อนุญาตให้ทำเช่นนั้นและโยนข้อยกเว้นที่เกี่ยวข้องกับ persistent ในขณะที่มีการบันทึก () ไม่มีข้อ จำกัด ดังกล่าวและหนึ่งสามารถไปกับการทำธุรกรรมฐานข้อมูลผ่านการบันทึก () นอกวงเงินการทำธุรกรรม