جلسة Hibernate هي الواجهة بين تطبيق Java وإطار العمل Hibernate. اليوم سننظر في أهم الطرق في الجلسة لحفظ البيانات وتحديثها في الجداول – save، saveOrUpdate، persist، update و merge.
جلسة Hibernate
حفظ جلسة Hibernate
كما يوحي اسم الطريقة، يمكن استخدام حفظ هايبرنيت() لحفظ الكيان في قاعدة البيانات. يمكننا استدعاء هذه الطريقة خارج عملية التحويل، وهذا هو السبب في عدم إعجابي بهذه الطريقة لحفظ البيانات. إذا استخدمنا هذا بدون عملية تحويل وكان هناك تدرج بين الكيانات، فإن الكيان الأساسي فقط يتم حفظه ما لم نقم بتفريغ الجلسة. لأغراض الاختبار لدينا كيانان هما – Employee
و Address
.
package com.journaldev.hibernate.model;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Cascade;
@Entity
@Table(name = "EMPLOYEE")
@Access(value=AccessType.FIELD)
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private long id;
@Column(name = "emp_name")
private String name;
@Column(name = "emp_salary")
private double salary;
@OneToOne(mappedBy = "employee")
@Cascade(value = org.hibernate.annotations.CascadeType.ALL)
private Address address;
//طرق الحصول والتعيين
@Override
public String toString() {
return "Id= " + id + ", Name= " + name + ", Salary= " + salary
+ ", {Address= " + address + "}";
}
}
package com.journaldev.hibernate.model;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
@Entity
@Table(name = "ADDRESS")
@Access(value=AccessType.FIELD)
public class Address {
@Id
@Column(name = "emp_id", unique = true, nullable = false)
@GeneratedValue(generator = "gen")
@GenericGenerator(name = "gen", strategy = "foreign", parameters = { @Parameter(name = "property", value = "employee") })
private long id;
@Column(name = "address_line1")
private String addressLine1;
@Column(name = "zipcode")
private String zipcode;
@Column(name = "city")
private String city;
@OneToOne
@PrimaryKeyJoinColumn
private Employee employee;
//طرق الحصول والتعيين
@Override
public String toString() {
return "AddressLine1= " + addressLine1 + ", City=" + city
+ ", Zipcode=" + zipcode;
}
}
فيما يلي برنامج Hibernate بسيط حيث نقوم باستدعاء save()
في حالات مختلفة.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Address;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateSaveExample {
public static void main(String[] args) {
// العمل التحضيري
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
//حفظ المثال - بدون معاملة
Session session = sessionFactory.openSession();
Employee emp = getTestEmployee();
long id = (Long) session.save(emp);
System.out.println("1. Employee save called without transaction, id="+id);
session.flush(); //address will not get saved without this
System.out.println("*****");
//حفظ المثال - مع معاملة
Transaction tx1 = session.beginTransaction();
Session session1 = sessionFactory.openSession();
Employee emp1 = getTestEmployee();
long id1 = (Long) session1.save(emp1);
System.out.println("2. Employee save called with transaction, id="+id1);
System.out.println("3. Before committing save transaction");
tx1.commit();
System.out.println("4. After committing save transaction");
System.out.println("*****");
//حفظ المثال - صف موجود في الجدول
Session session6 = sessionFactory.openSession();
Transaction tx6 = session6.beginTransaction();
Employee emp6 = (Employee) session6.load(Employee.class, new Long(20));
//تحديث بعض البيانات
System.out.println("Employee Details="+emp6);
emp6.setName("New Name");
emp6.getAddress().setCity("New City");
long id6 = (Long) session6.save(emp6);
emp6.setName("New Name1"); // will get updated in database
System.out.println("5. Employee save called with transaction, id="+id6);
System.out.println("6. Before committing save transaction");
tx6.commit();
System.out.println("7. After committing save transaction");
System.out.println("*****");
// إغلاق الموارد
sessionFactory.close();
}
public static Employee getTestEmployee() {
Employee emp = new Employee();
Address add = new Address();
emp.setName("Test Emp");
emp.setSalary(1000);
add.setAddressLine1("Test address1");
add.setCity("Test City");
add.setZipcode("12121");
emp.setAddress(add);
add.setEmployee(emp);
return emp;
}
}
عند تنفيذ البرنامج أعلاه، ينتج الناتج التالي.
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
1. Employee save called without transaction, id=149
Hibernate: insert into ADDRESS (address_line1, city, zipcode, emp_id) values (?, ?, ?, ?)
*****
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
2. Employee save called with transaction, id=150
3. Before committing save transaction
Hibernate: insert into ADDRESS (address_line1, city, zipcode, emp_id) values (?, ?, ?, ?)
4. After committing save transaction
*****
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee Details=Id= 20, Name= Kumar1, Salary= 1000.0, {Address= AddressLine1= Test address1, City=Blr, Zipcode=12121}
5. Employee save called with transaction, id=20
6. Before committing save transaction
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
Hibernate: update ADDRESS set address_line1=?, city=?, zipcode=? where emp_id=?
7. After committing save transaction
*****
بعض النقاط المهمة التي يمكننا تأكيدها من الناتج أعلاه هي:
- يجب تجنب الحفظ خارج حدود المعاملة، وإلا فإن الكيانات المرتبطة لن تتم حفظها مما يسبب عدم اتساق البيانات. من الطبيعي جدًا نسيان تفريغ الجلسة لأنه لا يطلق أي استثناءات أو تحذيرات.
- تُرجع طريقة الحفظ في هايبرنيت الهوية المولدة فورًا، وهذا ممكن لأن الكائن الأساسي يتم حفظه فور استدعاء طريقة الحفظ.
- إذا كانت هناك كائنات أخرى مرتبطة بالكائن الأساسي، فإنها تحفظ في وقت تأكيد المعاملة أو عندما نقوم بتفريغ الجلسة.
- بالنسبة للكائنات التي تكون في حالة دائمة، يقوم الحفظ بتحديث البيانات من خلال استعلام التحديث. لاحظ أن هذا يحدث عند تأكيد المعاملة. إذا لم تكن هناك تغييرات في الكائن، فلن يكون هناك أي استعلام يُطلق. إذا قمت بتشغيل البرنامج أعلاه عدة مرات، فستلاحظ أن الاستعلامات التحديثية لم يتم تشغيلها في المرة القادمة لأنه لم يحدث أي تغيير في قيم العمود.
- هايبرنيت الحفظ يحمل كائن الكيان إلى السياق الدائم، إذا قمت بتحديث خصائص الكائن بعد استدعاء الحفظ ولكن قبل تأكيد المعاملة، فسيتم حفظه في قاعدة البيانات.
تخزين Hibernate
تخزين Hibernate مشابه للحفظ (مع المعاملة) حيث يضيف كائن الكيان إلى السياق الثابت، لذا يتم تتبع أي تغييرات لاحقة. إذا تم تغيير خصائص الكائن قبل التأكيد على المعاملة أو تفريغ الجلسة، سيتم أيضًا حفظه في قاعدة البيانات. الفرق الثاني هو أنه يمكننا استخدام طريقة persist()
فقط داخل حدود معاملة، لذا فهو آمن ويعتني بأي كائنات متداخلة. وأخيرًا، لا يُرجع الـ persist أي شيء لذلك نحتاج إلى استخدام كائن الحفظ للحصول على قيمة المعرف المولدة. دعنا نلقي نظرة على تخزين hibernate باستخدام برنامج بسيط.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernatePersistExample {
public static void main(String[] args) {
// الاستعداد
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
// مثال على الاستمرار - مع المعاملة
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Employee emp2 = HibernateSaveExample.getTestEmployee();
session2.persist(emp2);
System.out.println("Persist called");
emp2.setName("Kumar"); // will be updated in database too
System.out.println("Employee Name updated");
System.out.println("8. Employee persist called with transaction, id="+emp2.getId()+", address id="+emp2.getAddress().getId());
tx2.commit();
System.out.println("*****");
// إغلاق الموارد
sessionFactory.close();
}
}
الناتج الذي أنتجه الشيفرة أعلاه هو:
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
8. Employee persist called with transaction, id=158, address id=158
Hibernate: insert into ADDRESS (address_line1, city, zipcode, emp_id) values (?, ?, ?, ?)
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
*****
لاحظ أنه يتم إدراج كائن الموظف الأول، ثم في وقت تأكيد المعاملة، يتم تنفيذ استعلام التحديث لتحديث قيمة الاسم. كما يتم حفظ عنوان الكائن المعين أيضًا في قاعدة البيانات.
تخزين أو تحديث Hibernate
هايبرنيت saveOrUpdate ينتج استعلامات إدراج أو تحديث استنادًا إلى البيانات المقدمة. إذا كانت البيانات موجودة في قاعدة البيانات ، يتم تنفيذ استعلام التحديث. يمكننا استخدام saveOrUpdate()
بدون معامل أيضًا ، ولكن ستواجه مشاكل مرة أخرى في حالة عدم حفظ الكائنات المعينة إذا لم يتم تنشيط الجلسة. يقوم Hibernate saveOrUpdate بإضافة كائن الكيان إلى السياق الدائم وتتبع أي تغييرات لاحقة. يتم حفظ أي تغييرات لاحقة في وقت تنفيذ المعاملة ، مثل persist.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateSaveOrUpdateExample {
public static void main(String[] args) {
// العمل التحضيري
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
// مثال على saveOrUpdate - بدون معامل
Session session5 = sessionFactory.openSession();
Employee emp5 = HibernateSaveExample.getTestEmployee();
session5.saveOrUpdate(emp5);
System.out.println("*****");
// مثال على saveOrUpdate - مع معامل
Session session3 = sessionFactory.openSession();
Transaction tx3 = session3.beginTransaction();
Employee emp3 = HibernateSaveExample.getTestEmployee();
session3.saveOrUpdate(emp3);
emp3.setName("Kumar"); //will be saved into DB
System.out.println("9. Before committing saveOrUpdate transaction. Id="+emp3.getId());
tx3.commit();
System.out.println("10. After committing saveOrUpdate transaction");
System.out.println("*****");
Transaction tx4 = session3.beginTransaction();
emp3.setName("Updated Test Name"); //Name changed
emp3.getAddress().setCity("Updated City");
session3.saveOrUpdate(emp3);
emp3.setName("Kumar"); //again changed to previous value, so no Employee update
System.out.println("11. Before committing saveOrUpdate transaction. Id="+emp3.getId());
tx4.commit();
System.out.println("12. After committing saveOrUpdate transaction");
System.out.println("*****");
// إغلاق الموارد
sessionFactory.close();
}
}
ينتج البرنامج أعلاه الناتج التالي.
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
*****
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
9. Before committing saveOrUpdate transaction. Id=166
Hibernate: insert into ADDRESS (address_line1, city, zipcode, emp_id) values (?, ?, ?, ?)
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
10. After committing saveOrUpdate transaction
*****
11. Before committing saveOrUpdate transaction. Id=166
Hibernate: update ADDRESS set address_line1=?, city=?, zipcode=? where emp_id=?
12. After committing saveOrUpdate transaction
*****
لاحظ أنه بدون معامل ، يتم حفظ الـ Employee فقط وتفقد معلومات العنوان. مع المعامل ، يتم تتبع كائن الموظف لأي تغييرات ، ولهذا السبب في آخر استدعاء لا يوجد تحديث في جدول Employee على الرغم من تغيير القيمة في الوسط ، تظل القيمة النهائية كما هي.
هايبرنيت تحديث
يجب استخدام هايبرنيت تحديث عندما نعلم أننا نقوم بتحديث معلومات الكيان فقط. تقوم هذه العملية بإضافة كائن الكيان إلى السياق الدائم ويتم تتبع التغييرات وحفظها عند تنفيذ المعاملة. دعونا نتحقق من هذا السلوك باستخدام برنامج بسيط.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateUpdateExample {
public static void main(String[] args) {
// العمل التحضيري
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = (Employee) session.load(Employee.class, new Long(101));
System.out.println("Employee object loaded. " + emp);
tx.commit();
// تحديث المثال
emp.setName("Updated name");
emp.getAddress().setCity("Bangalore");
Transaction tx7 = session.beginTransaction();
session.update(emp);
emp.setName("Final updated name");
System.out.println("13. Before committing update transaction");
tx7.commit();
System.out.println("14. After committing update transaction");
// إغلاق الموارد
sessionFactory.close();
}
}
عند تنفيذ البرنامج أعلاه للمرة الأولى، نحصل على الناتج التالي.
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Id= 101, Name= Test Emp, Salary= 1000.0, {Address= AddressLine1= Test address1, City=Test City, Zipcode=12121}
13. Before committing update transaction
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
Hibernate: update ADDRESS set address_line1=?, city=?, zipcode=? where emp_id=?
14. After committing update transaction
عند التنفيذ المتكرر، نحصل على الناتج التالي.
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Id= 101, Name= Final updated name, Salary= 1000.0, {Address= AddressLine1= Test address1, City=Bangalore, Zipcode=12121}
13. Before committing update transaction
14. After committing update transaction
لاحظ أنه لا توجد تحديثات تم تنشيطها بعد التنفيذ الأول لأنه لا توجد تحديثات في القيم. كما لاحظ أن اسم الموظف هو “الاسم النهائي المحدث” الذي قمنا بتعيينه بعد استدعاء طريقة update(). هذا يؤكد أن هايبرنيت كان يتتبع الكائن لأي تغييرات وفي وقت تأكيد العملية، تم حفظ هذه القيمة.
هايبرنيت دمج
يمكن استخدام دمج هايبرنيت لتحديث القيم الموجودة، ومع ذلك يقوم هذا الأسلوب بإنشاء نسخة من الكائن الذي تم تمريره وإعادته. الكائن المعاد هو جزء من السياق المستمر ويتم تتبعه لأي تغييرات، بينما الكائن الممرر ليس متتبعًا. هذه هي الفارق الرئيسي مع merge() من جميع الأساليب الأخرى. دعونا نلقي نظرة على هذا من خلال برنامج بسيط.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateMergeExample {
public static void main(String[] args) {
// العمل التحضيري
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = (Employee) session.load(Employee.class, new Long(101));
System.out.println("Employee object loaded. " + emp);
tx.commit();
// مثال على الدمج - البيانات موجودة بالفعل في الجداول
emp.setSalary(25000);
Transaction tx8 = session.beginTransaction();
Employee emp4 = (Employee) session.merge(emp);
System.out.println(emp4 == emp); // returns false
emp.setName("Test");
emp4.setName("Kumar");
System.out.println("15. Before committing merge transaction");
tx8.commit();
System.out.println("16. After committing merge transaction");
// إغلاق الموارد
sessionFactory.close();
}
}
الناتج في التنفيذ الأول هو:
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Id= 101, Name= Final updated name, Salary= 1000.0, {Address= AddressLine1= Test address1, City=Bangalore, Zipcode=12121}
false
15. Before committing merge transaction
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
16. After committing merge transaction
أما في التنفيذ المتكرر، يتم إنتاج الناتج:
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Id= 101, Name= Kumar, Salary= 25000.0, {Address= AddressLine1= Test address1, City=Bangalore, Zipcode=12121}
false
15. Before committing merge transaction
16. After committing merge transaction
لاحظ أن كائن الكيان الذي يتم إرجاعه بواسطة الدالة merge() يختلف عن الكائن الممرر. كما لاحظ أنه في تنفيذ لاحق، يكون اسم الكائن “كومار”، وذلك لأن الكائن المُرجَع يتم تتبُّعه لأي تغييرات. هذا كل شيء بالنسبة للدوال save و update في جلسة هايبرنيت، آمل أن تساعدك الأمثلة أعلاه في توضيح أي شكوك قد تكون لديك.