Hibernate 中的 merge方法何解?

文章一:

一直很纳闷这个merge和persist方法,平时也很少用。今天研究下总结下

    一、merge  翻译:合并;融合

 

          其实merge方法很简单,看代码。

         

Java代码   收藏代码
  1. @Entity  
  2. public class Father {  
  3.     @Id @GeneratedValue  
  4.     private int id;  
  5.       
  6.     private String name;  

 

    唯一一个father实体类,执行session.merge(father)方法时,首先会查看father的id是否为空,

    1.要是为空的话那就直接执行insert语句将father持久化,结束。

    2.要是不为空,那么先执行select的语句查询father表中id为这个father.id的记录是否存在

                   A:存在,那好执行update语句,结束。

                   B:不存在,那么执行inset语句,结束。

     很简单吧。

     但是有一点要注意,看代码;

Java代码   收藏代码
  1. @Test  
  2.     public void testChildSave() {  
  3.         Father f = new Father();  
  4.         f.setId(7);  
  5.         f.setName("父亲1");  
  6.                   
  7.         Session session = sessionFactory.getCurrentSession();  
  8.         session.beginTransaction();  
  9.         session.merge(f);  
  10.         session.getTransaction().commit();  
  11.           
  12.         System.out.println(f.getId());  
  13.   
  14.     }  

 先说明在执行这段代码之前,father表没有任何记录。 按照上面的流程,先执行select语句,然后执行insert语句

这是控制台给出的hibernate的sql语句

Java代码   收藏代码
  1. Hibernate:   
  2.     select  
  3.         father0_.id as id1_0_,  
  4.         father0_.name as name1_0_   
  5.     from  
  6.         Father father0_   
  7.     where  
  8.         father0_.id=?  
  9. Hibernate:   
  10.     insert   
  11.     into  
  12.         Father  
  13.         (name)   
  14.     values  
  15.         (?)  
  16. 7  

  注意看最后的一个7,这是我们之前set的id,但是数据库的记录下对应的id是1;也就是说father对象经过merge方法后没有任何的变化,一直是一个transient状态。所以要注意,一个对象经过merge后它和数据库不一定一致。

  现在我们来看看saveorupdate方法,这个方法也很简单,它会根据对象的id是否为空来决定进行inset或者update,

为空就inset,不为空就update,任何情况下都不会执行select语句,所以在update一个数据库不存在的id时会有Exception。


文章二:

merge

add操作


背景:
    Account 和 Group 两个对象,设置了双向的many-to-many关系,lazy=true
    不使用open session in view 模式
    不使用hibernate二级缓存

    考虑web应用场景,设置account和group关联时,只需要group和account的id就够了。
    数据库中存在两个group: 1.administrators, 2.engineers
    而po对象中,group信息为:1.invalid, 2.any one


代码A:

[java:nogutter]  view plain copy
  1. Account account = (Account) getHibernateTemplate().merge(po);  
  2. Long id = account.getId();  
  3. System.out.println("/tGet obj after added in dao start ...");  
  4. Account readAccount = (Account) getHibernateTemplate().get(  
  5.     Account.class, id);  
  6. System.out.println("/tGet obj after added in dao end ...");  
  7. System.out.println("/tIs po==readAccount ? " + (po == readAccount));  
  8. System.out.println("/tShow detai of po: " + po.toDetailString());  
  9. System.out.println("/tShow detai of readAccount: " + readAccount.toDetailString());  

   其中,为po设置了两个group

输出结果:

[java:nogutter]  view plain copy
  1. Hibernate: select ... from SYS_GROUPS where ID=?  
  2. Hibernate: select ... from SYS_GROUPS where ID=?  
  3.     Get obj after added in dao start ...  
  4.     Get obj after added in dao end ...  
  5.     Is po==readAccount ? false  
  6.     Show detai of po: Account[0.account_22, groups[2.any one 1.invalid ]]  
  7.     Show detai of readAccount: Account[22.account_22, groups[2.engineers 1.administrators ]]  
  8. Hibernate: insert into SYS_ACCOUNTS (...) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  
  9. Hibernate: insert into SYS_GROUP_MEMBER (ACCOUNT_ID, GROUP_ID) values (?, ?)  
  10. Hibernate: insert into SYS_GROUP_MEMBER (ACCOUNT_ID, GROUP_ID) values (?, ?)  

 

代码B:

[java:nogutter]  view plain copy
  1. Long id = (Long) getHibernateTemplate().save(po);  
  2. System.out.println("/tGet obj after added in dao start ...");  
  3. Group group = (Group)getHibernateTemplate().get(Group.class,new Long(1));  
  4. System.out.println("/tGroup detai:" + group.toString());  
  5. Account readAccount = (Account) getHibernateTemplate().get(  
  6.     Account.class, id);  
  7. System.out.println("/tGet obj after added in dao end ...");  
  8. System.out.println("/tIs po==readAccount ? " + (po == readAccount));  
  9. System.out.println("/tShow detai of po: " + po.toDetailString());  
  10. System.out.println("/tShow detai of readAccount: "  
  11.     + readAccount.toDetailString());  
  12.   
  13. getHibernateTemplate().merge(readAccount);  
  14. Account readAgain = (Account) getHibernateTemplate().get(Account.class,  
  15.     id);  
  16. System.out.println("/tIs po==readAgain ? " + (readAgain == po));  
  17. System.out.println("/tIs readAgain== readAccount? "  
  18.     + (readAgain == readAccount));  
  19. System.out.println("/tShow detai again: " + readAgain.toDetailString());  


输出结果:

[java]  view plain copy
  1.     Get obj after added in dao start ...  
  2. Hibernate: select ... from SYS_GROUPS where ID=?  
  3.     Group detai:Group 1. administrators  
  4.     Get obj after added in dao end ...  
  5.     Is po==readAccount ? true  
  6.     Show detai of po: Account[27.account_27, groups[1.invalid 2.any one ]]  
  7.     Show detai of readAccount: Account[27.account_27, groups[1.invalid 2.any one ]]  
  8. Hibernate: select ... from SYS_GROUPS where ID=?  
  9.     Is po==readAgain ? true  
  10.     Is readAgain== readAccount? true  
  11.     Show detai again: Account[27.account_27, groups[1.administrators 2.engineers ]]  
  12. Hibernate: insert into SYS_ACCOUNTS (...) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  
  13. Hibernate: insert into SYS_GROUP_MEMBER (ACCOUNT_ID, GROUP_ID) values (?, ?)  
  14. Hibernate: insert into SYS_GROUP_MEMBER (ACCOUNT_ID, GROUP_ID) values (?, ?)  
  


结论:

 1. merge()方法会导致执行查询group对象的select语句,在调用merge()命令时立即执行(条件:目标group对象没有被缓存)

 2. 无论merger()或save()方法,insert语句都在最后执行,并非在调用相应命令时立即执行

 3. 直接调用merge()方法时,会返回一个新的instance,原po保持不变

 4. save()之后,po中的group对象并没有被关联到session,因此查询group(id=1)会触发select语句

 5. save()之后,po对象被关联到session,再次查询,不会触发select语句,并且不会检查group对象是否被关联到session

 6. save()之后再调用merge,返回的是同一个instance,但其关联group对象会被更新

    如果在add一个对象之后,如果存在关联对象,并且需要再同一个hibernate session中进行回显,则建议使用merge()方法。


解答一:

主要的区别在于POJO对象的状态!!!

update或者saveorupdate后,POJO的状态为持久态
而merge后,对象的状态仍然为托管状态!


解答二:

如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象的状态覆盖旧有的持久实例  
如果session没有相应的持久实例,则尝试从数据库中加载,或创建新的持久化实例,最后返回该持久实例  
用户给出的这个对象没有被关联到session上,它依旧是脱管的  
重点是最后一句:
当我们使用update的时候,执行完成后,我们提供的对象A的状态变成持久化状态
但当我们使用merge的时候,执行完成,我们提供的对象A还是脱管状态,hibernate或者new了一个B,或者检索到
一个持久对象B,并把我们提供的对象A的所有的值拷贝到这个B,执行完成后B是持久状态,而我们提供的A还是托管状态


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值