Hibernate Session 是 Java 應用程式與 Hibernate 框架之間的介面。今天我們將研究在表格中保存和更新數據的 Session 重要方法 – save、saveOrUpdate、persist、update 和 merge。
Hibernate Session
Hibernate Session save
正如方法名所示,hibernate save() 可用於將實體保存到數據庫中。我們可以在事務之外調用此方法,這就是為什麼我不喜歡使用此方法來保存數據的原因。如果我們在沒有事務的情況下使用它,並且我們之間存在實體之間的級聯,那麼只有主要實體才會被保存除非我們刷新會話。為了測試目的,我們有兩個實體 bean – 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;
//Getter setter 方法
@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;
//Getter setter 方法
@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保存方法立即返回生成的id,這是可能的,因為一旦調用保存方法,主對象就會立即保存。
- 如果有其他從主對象映射的對象,則它們將在提交事務時或刷新會話時保存。
- 對於處於持久狀態的對象,保存通過更新查詢更新數據。請注意,這發生在提交事務時。如果對象沒有變化,則不會發出任何查詢。如果您多次運行上述程序,則會注意到下次不會發出更新查詢,因為列值沒有變化。
- Hibernate保存將實體對象加載到持久上下文中,如果您在保存調用之後但在事務提交之前更新對象屬性,則它將保存到數據庫中。
持久化
持久化與save(帶有事務)類似,它將實體對象添加到持久上下文,因此任何進一步的更改都將被跟踪。如果在事務提交或會話刷新之前更改了對象的屬性,它也將保存到數據庫中。第二個區別是我們只能在事務範圍內使用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 對象進行任何更改,這就是為什麼在最後一次調用中雖然在中間更改了值,但在 Employee 表中沒有更新的原因。
Hibernate update
Hibernate update 應該在我們知道只更新實體信息的情況下使用。此操作將實體對象添加到持久上下文中,並在提交事務時跟踪和保存進一步的更改。讓我們通過一個簡單的程序來檢查這種行為。
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
請注意,在第一次執行後,沒有觸發更新,因為值沒有更新。還要注意,員工姓名是“Final updated name”,我們在調用update()方法後設置的。這證實了Hibernate正在追蹤對象以進行任何更改,並在提交事務時,此值已保存。
Hibernate Merge
Hibernate merge可用於更新現有值,但此方法從傳遞的實體對象創建一個副本並返回它。返回的對象是持久上下文的一部分,並追蹤任何更改,而傳遞的對象則不被追蹤。這是與所有其他方法的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();
// merge示例-資料已經存在於表中
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()`返回的实体对象与传入的实体对象不同。同时,请注意在进一步的执行中,`name`是“Kumar”,这是因为返回的对象被跟踪以检测任何更改。关于Hibernate Session的`save`和`update`方法就介绍到这里,希望上述示例能帮助您澄清任何疑惑。