הפעלת הסשן של היברנייט היא הממשק בין יישום ג'אווה לפרימפטורת היברנייט. היום נסתכל על שיטות חשובות של הסשן לשמירה ועדכון של נתונים בטבלאות – שמירה, saveOrUpdate, לשמור, עדכון ו־מיזוג.
הפעלת הסשן של היברנייט
שמירת הפעלת הסשן של היברנייט
כפי ששמו המציין, היברנייט save() ניתן להשתמש בו כדי לשמור ישות בבסיס נתונים. אנו יכולים לקרוא לשיטה זו מחוץ לעסקה, ולכן אני לא אוהב את השיטה זו לשמירת נתונים. אם נשתמש בה בלי עסקה ויש נפילה בין יישויות, אז רק הישות הראשית תישמר אלא אם כן אנחנו מנפיחים את הסשן. לצורך הבדיקה יש לנו שני סוגי יישויות – עובד
ו־כתובת
.
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;
}
}
הנה תוכנית פשוטה של היברנייט בה אנו קוראים לשיטת 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 מעביר אובייקטי ישות טעונים להקשר התמד, אם תעדכן את תכונות האובייקט אחרי שיחול שיחול השמירה אבל לפני שהעסקה מתבצעת, היא תישמר במסד הנתונים.
שינויים יציבים ב-Hibernate
השמירה ב-Hibernate דומה ל-save (עם עסקת העברה) והיא מוסיפה את אובייקט היישות ל־ההקשר הישית, כך שכל שינוי נוסף נעקב. אם תכנת העסקה או ההקשר לא נסגרים, השינויים במאפייני האובייקט יישמרו במסד נתונים. ההבחנה השנייה היא שניתן להשתמש בשיטת persist()
רק בגבולות עסקה, לכן זה בטוח ודואג לכל אובייקטים מקושרים. לבסוף, persist לא מחזירה דבר, ולכן עלינו להשתמש באובייקט שנשמר כדי לקבל את ערך הזיהוי שנוצר. בואו נסתכל על Hibernate 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 HibernatePersistExample {
public static void main(String[] args) {
// עבודות הכנה
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
//דוגמה ל־persist - עם עסקה
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
הרמת או עדכון של Hibernate גורמת לביצוע שאילתות הכנסה או עדכון על פי המידע שסופק. אם המידע כבר קיים במסד הנתונים, יתבצע פעולת עדכון. ניתן להשתמש ב־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
*****
שים לב שבלעדי עסקאות, יש רק שמירה של העובד והמידע על הכתובת אבד. עם עסקאות, אובייקט העובד משמש למעקב אחר שינויים, ולכן בשיחה האחרונה אין עדכון בטבלת העובד, גם אם הערך השתנה בינתיים, הערך הסופי נשמר כפי שהוא.
עדכון Hibernate
עדכון 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 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
שים לב שאין עדכונים אחרי ביצוע הראשון מכיוון שאין שינויים בערכים. גם שים לב ששם העובד הוא "שם מעודכן אחרון" שקובענו לאחר קריאה לשיטת העדכון. זה מאשר שהיברנייט מעקבת אחר האובייקט לשינויים ובעת ביצוע העסקה, הערך הזה נשמר.
היברנייט מיזוג
המיזוג בהיברנייט ניתן לשמש לעדכון של ערכים קיימים, אך שיטה זו יוצרת העתק מהאובייקט המועבר ומחזירה אותו. האובייקט שמוחזר הוא חלק מהקשר התקפותי ומועקב אחר שינויים, והאובייקט שנמסר אינו מועקב. זהו ההבחנה המרכזית עם 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()` הוא שונה מהעצם שמועבר. יש לשים לב כי בביצוע נוסף, השם הוא "קומאר", זה בגלל שהעצם שמוחזר מודע לכל שינוי. זהו כל המידע שיש לי לגבי הפעולה של Hibernate Session ושיטות save
ו־update
. מקווה שהדוגמאות למעלה יעזרו לך להבהיר כל ספק שיש לך.