save()、saveOrUpdate()、merge()的区别

一、Save()

save()方法能够保存实体到数据库。假如两个实体之间有关系(例如employee表和address表有一对一关系),如果在没有事务的情况下调用这个方法保存employee这个实体,除非调用flush()这个方法,否则仅仅employee实体会被保存。 

二、SaveOrUpdate()

 

saveOrUpdate()方法会执行插入或者更新操作。如果该对象在数据库中已经存在则更新,不存在则插入。

 

也可以在没有事务的情况下执行,但是如果没有手动调用flush()方法会面临关联对象不被保存的问题

 

save()方法与saveOrUpdate()方法最大的不同点在于:saveOrUpdate()方法会将实体对象添加到持久化上下文中,该实体的后续改变会被跟踪。

例子:

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操作。

三、Merge()

如果某对象处于游离态,游离态是指该对象的id值为空。hibernate判断一个对象在数据库中是否存在不是看对象的其他信息,而是判断该id在数据库中是不是存在。如果id为空,那自然是不存在,所以当我们调用merge方法的时候,就会直接执行插入操作。这一点有点像saveorupdate()方法。

 

Object merge(Object object)
             throws HibernateException

 

Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge"
The semantics of this method are defined by JSR-220.
Parameters:
object - a detached instance with state to be copied
Returns:
an updated persistent instance
Throws:
HibernateException

 

首先从参数说明来看,merge的参数应该是一个处于托管状态的实例对象,而返回值则是一个持久化对象。但是这里的参数并不是一定要是托管状态的对象,它还可以是瞬态和持久化的实例对象。正因如此,才使merge方法变得复杂化。

 

经代码检验从merge方法产生的效果来看,它和saveOrUpdate方法相似,因此虽然上面提到是因为参数状态的不同造成复杂化,但是这里我并不打算分参数的不同状态来理解merge,而是根据参数有无id或id是否已经存在来理解merge。个人认为这样更容易理解,而且从执行他们两个方法而产生的sql语句来看是一样的。

 

1. 参数实例对象没有提供id或提供的id在数据库中不存在:这时merge将执行插入操作,产生的sql语句如下,       

 

          Hibernate: select max(uid) from user     

 

          Hibernate: insert into hibernate1.user (name, age, uid) values (?, ?, ?)

 

2. 参数实例对象的id在数据库中已经存在,此时又有两种情况:

 

(1)如果对象有改动,则执行更新操作,产生sql语句有,

 

         Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=? 
         Hibernate: update hibernate1.user set name=?, age=? where uid=?

 

(2)如果对象为改动,则执行查询操作,产生的语句有,

 

         Hibernate: select user0_.uid as uid0_0_, user0_.name as name0_0_, user0_.age as age0_0_ from hibernate1.user user0_ where user0_.uid=?

 

不管哪种情况,merge的返回值都是一个持久化的实例对象,但对于参数而言不会改变它的状态。

 

虽然从功能上merge方法与saveOrUpdate类似,但他们仍有区别。现在有这样一种情况:我们先通过session的get方法得到一个对象u,然后关掉session,再打开一个session并执行saveOrUpdate(u)。此时我们可以看到抛出异常:Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session,即在session缓存中不允许有两个id相同的对象。不过若使用merge方法则不会异常,其实从merge的中文意思(合并)我们就可以理解了。

 

 

 

posted @ 2016-08-31 18:07  Fighting`  阅读(226)  评论(0编辑  收藏  举报