Hibernate save, saveOrUpdate, persist, merge, update 區別

Hibernate Save

hibernate save()方法可以保存實體到數據庫,正如方法名稱save這個單詞所代表的意思。咱們可以在事務以外調用這個方法,這也是我不喜歡使用這個方法保存數據的緣由。假如兩個實體之間有關係(例如employee表和address表有一對一關係),若是在沒有事務的狀況下調用這個方法保存employee這個實體,除非調用flush()這個方法,不然僅僅employee實體會被保存。java

Employee.java

爲了方便理解,簡化Employee.java的屬性。git

/* 
 * @(#)Employee.java    Created on 2016年4月10日
 * Copyright (c) 2016. All rights reserved.
 */
package nd.esp.com.hibernate.model;

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")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "emp_id")
    private long id;
    @Column(name = "emp_name")
    private String name;
    @OneToOne(mappedBy = "employee")
    @Cascade(value = org.hibernate.annotations.CascadeType.ALL)
    private Address address;
    
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + "]";
    }
}

Address.java

爲了方便理解,簡化Address.java的屬性。github

/* 
 * @(#)Address.java    Created on 2016年4月10日
 * Copyright (c) 2016. All rights reserved.
 */
package nd.esp.com.hibernate.model;

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")
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 = "city")
    private String city;
    @OneToOne
    @PrimaryKeyJoinColumn
    private Employee employee;
    
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public Employee getEmployee() {
        return employee;
    }
    public void setEmployee(Employee employee) {
        this.employee = employee;
    }
    @Override
    public String toString() {
        return "Address [id=" + id + ", city=" + city + "]";
    }
}

HibernateSaveExample.java

如下是簡單的hibernate程序,演示save()方法的使用。sql

/* 
 * @(#)HibernateSaveExample.java    Created on 2016年4月10日
 * Copyright (c) 2016. All rights reserved.
 */
package nd.esp.com.hibernate.example;

import nd.esp.com.hibernate.model.Address;
import nd.esp.com.hibernate.model.Employee;
import nd.esp.com.hibernate.utils.HibernateUtil;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

public class HibernateSaveExample {
    public static void main(String[] args) {
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        System.out.println("***********************************************");
        // save example - without transaction
        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("***********************************************");

        // save example - with transaction
        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("***********************************************");

        // save example - existing row in table
        Session session6 = sessionFactory.openSession();
        Transaction tx6 = session6.beginTransaction();
        Employee emp6 = (Employee) session6.load(Employee.class, new Long(3));
        // update some data
        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("***********************************************");

        // Close resources
        sessionFactory.close();
    }
    public static Employee getTestEmployee() {
        Employee emp = new Employee();
        Address add = new Address();
        emp.setName("Test Emp");
        add.setCity("Test City");
        emp.setAddress(add);
        add.setEmployee(emp);
        return emp;
    }
}

執行上述示例程序,輸出結果(第一次)。數據庫

***********************************************
Hibernate: insert into EMPLOYEE (emp_name) values (?)
1. Employee save called without transaction, id=15
Hibernate: insert into ADDRESS (city, emp_id) values (?, ?)
***********************************************
Hibernate: insert into EMPLOYEE (emp_name) values (?)
2. Employee save called with transaction, id=16
3. Before committing save transaction
4. After committing save transaction
***********************************************
Hibernate: select employee0_.emp_id as emp1_1_1_, employee0_.emp_name as emp2_1_1_, address1_.emp_id as emp1_0_0_, address1_.city as city2_0_0_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee Details=Employee [id=3, name=Test Emp]
5. Employee save called with transaction, id=3
6. Before committing save transaction
Hibernate: update ADDRESS set city=? where emp_id=?
Hibernate: update EMPLOYEE set emp_name=? where emp_id=?
7. After committing save transaction
***********************************************

執行上述示例程序,輸出結果(第二次)。安全

***********************************************
Hibernate: insert into EMPLOYEE (emp_name) values (?)
1. Employee save called without transaction, id=17
Hibernate: insert into ADDRESS (city, emp_id) values (?, ?)
***********************************************
Hibernate: insert into EMPLOYEE (emp_name) values (?)
2. Employee save called with transaction, id=18
3. Before committing save transaction
4. After committing save transaction
***********************************************
Hibernate: select employee0_.emp_id as emp1_1_1_, employee0_.emp_name as emp2_1_1_, address1_.emp_id as emp1_0_0_, address1_.city as city2_0_0_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee Details=Employee [id=3, name=New Name1]
5. Employee save called with transaction, id=3
6. Before committing save transaction
7. After committing save transaction
***********************************************

從上述的輸出能夠獲得如下重要的幾點:session

  • 應該避免在事務以外調用save()方法,不然關聯實體(例如employee和address是一對一關係,相互關聯)將不會被保存從而致使不一致。很容易忘記在最後調用flush()方法,由於不會有任務的異常或者警告拋出。
  • hibernate save()方法會當即返回id,緣由極可能是調用save()的同時這個實體對象已經被寫入數據庫(當即執行sql語句insert into)
  • 提交事務或者調用flush()方法,save()方法纔會將關聯對象也寫入數據庫。
  • save()方法保存持久化狀態的對象,hibernate會經過update操做完成。注意這個會發生在提交事務的時候。若是該持久化對象沒有改變,hibernate不會發出update語句。若是屢次運行示例程序HibernateSaveExample.java,會發現從第二次開始程序就不會發送update語句。由於hibernate在更新以前會先select,查詢該持久化對象,發現該對象和數據庫中的一致,就不會作update操做。

Hibernate Persist

hibernate persist()方法與save()方法(在事務中執行)相似,persist()方法會將實體對象添加到持久化上下文中,如此被保存的實體後續改變會被記錄。若是在提交事務或者會話flush(),對象的屬性被從新賦值,那麼這個變化也會被保存到數據庫中。app

第二個不一樣在於persist()方法只可以在事務內執行,因此它可以很好地保證關聯對象的安全與存儲(原文:Second difference is that we can use persist() method only within the boundary of a transaction, so it’s safe and takes care of any cascaded objects.)。(發現原做者的第一點讓人很疑惑,感受是persist()不在事務中執行就會報錯。通過測試,persist()在事務外運行不會報錯,可是不會插入數據到數據庫。原做者的意思多是:persist()方法只可以在事務中被執行,纔可以將數據插入到數據庫中)ide

最後,persist()方法返回值是void,也就是說不會返回任何的值。測試

HibernatePersistExample.java

如下是簡單的hibernate程序,演示persist()方法的使用。

/* 
 * @(#)HibernatePersistExample.java    Created on 2016年4月10日
 * Copyright (c) 2016. All rights reserved.
 */
package nd.esp.com.hibernate.example;

import nd.esp.com.hibernate.model.Address;
import nd.esp.com.hibernate.model.Employee;
import nd.esp.com.hibernate.utils.HibernateUtil;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

public class HibernatePersistExample {
    public static void main(String[] args) {
        // Prep Work
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        System.out.println("***********************************************");
        // persist example - with transaction
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = session2.beginTransaction();
        Employee emp2 = 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("***********************************************");
        // Close resources
        sessionFactory.close();
    }

    public static Employee getTestEmployee() {
        Employee emp = new Employee();
        Address add = new Address();
        emp.setName("Test Emp");
        add.setCity("Test City");
        emp.setAddress(add);
        add.setEmployee(emp);
        return emp;
    }
}

執行上述示例程序,輸出結果。

***********************************************
Hibernate: insert into EMPLOYEE (emp_name) values (?)
Persist called
Employee Name updated
8. Employee persist called with transaction, id=19, address id=19
Hibernate: insert into ADDRESS (city, emp_id) values (?, ?)
Hibernate: update EMPLOYEE set emp_name=? where emp_id=?
***********************************************

須要注意,第一次employee對象被插入數據庫,提交事務的時候執行address實體的插入操做,因爲employee實體name屬性從新賦值,因此執行update操做。

Hibernate saveOrUpdate

hibernate saveOrUpdate()方法會執行插入或者更新操做。若是該對象在數據庫中已經存在則更新,不存在則插入。

saveOrUpdate()方法能夠在沒有事務的狀況下執行,可是若是沒有手動調用flush()方法會面臨關聯對象不被保存的問題

save()方法與saveOrUpdate()方法最大的不一樣點在於,saveOrUpdate()方法會將實體對象添加到持久化上下文中,該實體的後續改變會被跟蹤。

HibernateSaveOrUpdateExample.java

如下是簡單的hibernate程序,演示saveOrUpdate()方法的使用。

/* 
 * @(#)HibernateSaveOrUpdateExample.java    Created on 2016年4月10日
 * Copyright (c) 2016. All rights reserved.
 */
package nd.esp.com.hibernate.example;

import nd.esp.com.hibernate.model.Address;
import nd.esp.com.hibernate.model.Employee;
import nd.esp.com.hibernate.utils.HibernateUtil;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

public class HibernateSaveOrUpdateExample {
    public static void main(String[] args) {
        // Prep Work
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        System.out.println("***********************************************");
        // saveOrUpdate example - without transaction
        Session session5 = sessionFactory.openSession();
        Employee emp5 = getTestEmployee();
        session5.saveOrUpdate(emp5);
        System.out.println("***********************************************");

        // saveOrUpdate example - with transaction
        Session session3 = sessionFactory.openSession();
        Transaction tx3 = session3.beginTransaction();
        Employee emp3 = 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("***********************************************");

        // Close resources
        sessionFactory.close();
    }
    public static Employee getTestEmployee() {
        Employee emp = new Employee();
        Address add = new Address();
        emp.setName("Test Emp");
        add.setCity("Test City");
        emp.setAddress(add);
        add.setEmployee(emp);
        return emp;
    }
}

執行上述示例程序,輸出結果。

***********************************************
Hibernate: insert into EMPLOYEE (emp_name) values (?)
***********************************************
Hibernate: insert into EMPLOYEE (emp_name) values (?)
9. Before committing saveOrUpdate transaction. Id=21
Hibernate: insert into ADDRESS (city, emp_id) values (?, ?)
Hibernate: update EMPLOYEE set emp_name=? where emp_id=?
10. After committing saveOrUpdate transaction
***********************************************
11. Before committing saveOrUpdate transaction. Id=21
Hibernate: update ADDRESS set city=? where emp_id=?
12. After committing saveOrUpdate transaction
***********************************************

注意若是沒有事務,僅僅是employee實體被保存到數據庫,而address的信息丟失了。

在事務tx4中的幾行代碼employee實體的name屬性先被修改成「Updated Test Name」,以後又被賦值爲原來的值「Kumar」,所以employee這個實體在事務提交以前並無改變,因此並無update操做。

Hibernate update

當肯定只更新實體信息時使用Hibernate update()方法。update()方法會將實體添加到持久化上下文,實體後續的改變會被跟蹤而且當事務提交時這些改變會被保存到數據庫中。

HibernateUpdateExample.java

如下是簡單的hibernate程序,update()方法的使用。

/* 
 * @(#)HibernateUpdateExample.java    Created on 2016年4月10日
 * Copyright (c) 2016. All rights reserved.
 */
package nd.esp.com.hibernate.example;

import nd.esp.com.hibernate.model.Employee;
import nd.esp.com.hibernate.utils.HibernateUtil;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

public class HibernateUpdateExample {
    public static void main(String[] args) {
        // Prep Work
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        System.out.println("***********************************************");
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        Employee emp = (Employee) session.load(Employee.class, new Long(19));
        System.out.println("Employee object loaded. " + emp);
        tx.commit();
        System.out.println("***********************************************");
        // update example
        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");
        System.out.println("***********************************************");
        // Close resources
        sessionFactory.close();
    }
}

執行上述示例程序,輸出結果(第一次)。

***********************************************
Hibernate: select employee0_.emp_id as emp1_1_1_, employee0_.emp_name as emp2_1_1_, address1_.emp_id as emp1_0_0_, address1_.city as city2_0_0_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Employee [id=19, name=Kumar]
***********************************************
13. Before committing update transaction
Hibernate: update ADDRESS set city=? where emp_id=?
Hibernate: update EMPLOYEE set emp_name=? where emp_id=?
14. After committing update transaction
***********************************************

執行上述示例程序,輸出結果(第二次)。

***********************************************
Hibernate: select employee0_.emp_id as emp1_1_1_, employee0_.emp_name as emp2_1_1_, address1_.emp_id as emp1_0_0_, address1_.city as city2_0_0_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Employee [id=19, name=Final updated name]
***********************************************
13. Before committing update transaction
14. After committing update transaction
***********************************************

注意第二次運行並無執行update操做,由於employee實體與數據庫中的一致。還有語句:

session.update(emp);
emp.setName("Final updated name");

修改employee實體name屬性值爲:「Final updated name」,是在update()方法以後,而最後保存到數據庫中是「Final updated name」,代表hibernate update()方法會跟蹤實體的改變,在提交事務時保存到數據庫中。

Hibernate Merge

hibernate merge()方法被用於更新數據庫中的記錄,然而merge()方法經過建立一個傳遞進來的實體對象副本而且將這個副本做爲返回值返回。返回值屬於持久化上下文,可以跟蹤實體的改變,而傳遞進來的實體並不能被跟蹤。這一點是merge()方法與其餘方法最大的不一樣。

HibernateMergeExample.java

如下是簡單的hibernate程序,merge()方法的使用。

/* 
 * @(#)HibernateMergeExample.java    Created on 2016年4月10日
 * Copyright (c) 2016. All rights reserved.
 */
package nd.esp.com.hibernate.example;

import nd.esp.com.hibernate.model.Employee;
import nd.esp.com.hibernate.utils.HibernateUtil;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

public class HibernateMergeExample {
    public static void main(String[] args) {
        // Prep Work
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        System.out.println("***********************************************");
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        Employee emp = (Employee) session.load(Employee.class, new Long(19));
        System.out.println("Employee object loaded. " + emp);
        tx.commit();
        System.out.println("***********************************************");
        // merge example - data already present in tables
        emp.setName("test1");
        Transaction tx8 = session.beginTransaction();
        Employee emp4 = (Employee) session.merge(emp);
        System.out.println(emp4 == emp); // returns false
        emp.setName("test2");
        emp4.setName("merge");
        System.out.println("15. Before committing merge transaction");
        tx8.commit();
        System.out.println("16. After committing merge transaction");
        System.out.println("***********************************************");
        // Close resources
        sessionFactory.close();
    }
}

執行上述示例程序,輸出結果(第一次)。

***********************************************
Hibernate: select employee0_.emp_id as emp1_1_1_, employee0_.emp_name as emp2_1_1_, address1_.emp_id as emp1_0_0_, address1_.city as city2_0_0_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Employee [id=19, name=Kumar]
***********************************************
false
15. Before committing merge transaction
Hibernate: update EMPLOYEE set emp_name=? where emp_id=?
16. After committing merge transaction
***********************************************

執行上述示例程序,輸出結果(第二次)。

***********************************************
Hibernate: select employee0_.emp_id as emp1_1_1_, employee0_.emp_name as emp2_1_1_, address1_.emp_id as emp1_0_0_, address1_.city as city2_0_0_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Employee [id=19, name=merge]
***********************************************
false
15. Before committing merge transaction
16. After committing merge transaction
***********************************************

注意merge()方法傳遞進入的實體與返回值實體是不同的,

Employee emp4 = (Employee) session.merge(emp);
emp.setName("test2");
emp4.setName("merge");

上述代碼會將employee表的name屬性賦值爲「merge」,由於返回實體emp4屬於持久化上下文,會被跟蹤改變。

以上內容翻譯自http://www.journaldev.com/3481/hibernate-save-vs-saveorupdate-vs-persist-vs-merge-vs-update-explanation-with-examples。例子代碼作了一點簡化,而且工程化,能夠在以下地址找到上述例子工程:https://github.com/xiaoheike/HibernateSavePersistUpdateMergeDiff

總結

save()方法:

  • 應該避免在事務以外調用save()方法,不然關聯實體(例如employee和address是一對一關係,相互關聯)將不會被保存從而致使不一致。很容易忘記在最後調用flush()方法,由於不會有任務的異常或者警告拋出。
  • hibernate save()方法會當即返回id,緣由極可能是調用save()的同時這個實體對象已經被寫入數據庫(當即執行sql語句insert into)
  • 提交事務或者調用flush()方法,save()方法纔會將關聯對象也寫入數據庫。

persist()方法:

  • persist()方法會將實體對象添加到持久化上下文中,如此被保存的實體後續改變會被記錄。若是在提交事務或者會話flush(),對象的屬性被從新賦值,那麼這個變化也會被保存到數據庫中。
  • persist()方法必須在事務內執行,纔可以將數據插入數據庫。若是不在事務範圍內執行,數據將丟失。
  • 最後,persist()方法返回值是void,也就是說不會返回任何的值。

saveOrUpdate()方法:

  • hibernate saveOrUpdate()方法會執行插入或者更新操做。若是該對象在數據庫中已經存在則更新,不存在則插入。
  • saveOrUpdate()方法能夠在沒有事務的狀況下執行,可是若是沒有手動調用flush()方法會面臨關聯對象不被保存的問題
  • save()方法與saveOrUpdate()方法最大的不一樣點在於,saveOrUpdate()方法會將實體對象添加到持久化上下文中,該實體的後續改變會被跟蹤。

update()方法:

  • 當肯定只更新實體信息時使用Hibernate update()方法。update()方法會將實體添加到持久化上下文,實體後續的改變會被跟蹤而且當事務提交時這些改變會被保存到數據庫中
  • hibernate update()方法會跟蹤實體的改變,在提交事務時保存到數據庫中。

merge()方法:

  • hibernate merge()方法被用於更新數據庫中的記錄,然而merge()方法經過建立一個傳遞進來的實體對象副本而且將這個副本做爲返回值返回。返回值屬於持久化上下文,可以跟蹤實體的改變,而傳遞進來的實體並不能被跟蹤。這一點是merge()方法與其餘方法最大的不一樣。

教程結束,感謝閱讀。

歡迎轉載,但請註明本文連接,謝謝。

2016/4/10 星期日 17:05:41

2016/4/15 星期五 18:12:41 修改關於persist()方法與事務關係描述

相關文章
相關標籤/搜索