hibernate中的merge()方法
Hibernate提供有save()、persist()、savaOrUpdate()和merge()等方法来提供插入数据的功能。前三者理解起来较后者容易一些,而merge()方法从api中的介绍就可以看出它是最复杂的,因此要特别留意一下。
Hibernate的api中关于merge()方法的原文
merge
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()方法的作用
Hibernate中有一个常见错误:A different object with the same identifier value was already associated with the session,即在一个session中存在两个不同的实体却有着相同的身份标签(主键)是会报错的,这时为了避免这种错误就可以使用Hibernate提供的merge()方法。
merge()方法的使用特性
1.new一个对象并设置ID时,这个对象会被当作游离态处理,在使用merge时,如果在数据库中不能能找到这条记录,则使用insert将数据插入;如果在数据库中找到这条记录,则使用update将数据更新。
2.new一个对象没有设置ID时,这个对象会被当作瞬态处理,在使用merge时会根据实体类的主键生成策略保存这条数据。
3.使用merge存储到数据库的对象,其本身不会转变为持久态对象。
Hibernate中三态的补充
1.瞬态:通过Java关键字new的实体类对象,不和Session实例关联并且在数据库中没有和瞬态对象关联的记录,此时的对象还没有纳入Hibernate的缓存管理中。
2.持久态: 已经被保存进数据库的实体对象,还存于Hibernate的缓存管理之中。
3.游离态(脱管态):持久态对象脱离了Hibernate的缓存管理后就会变成游离态,游离态对象与瞬态对象的最大区别在于,游离态对象在数据库中可能存在一条与之对应的记录,而瞬态对象则不会在数据库中存在与之对应的记录,简而言之就是游离态对象比瞬态对象多了一个ID属性。
代码实际校验
经实际代码的检验,从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在数据库中已经存在,此时又有两种情况。
①如果对象有改动,则执行更新操作,产生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 = ?
②如果对象无改动,则执行查询操作,产生的语句有:
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的中文意思(合并)我们就可以理解了。
"匆匆忙忙,恍恍惚惚,慌慌张张。"