文章一:
一直很纳闷这个merge和persist方法,平时也很少用。今天研究下总结下
一、merge 翻译:合并;融合
其实merge方法很简单,看代码。
- @Entity
- public class Father {
- @Id @GeneratedValue
- private int id;
- private String name;
唯一一个father实体类,执行session.merge(father)方法时,首先会查看father的id是否为空,
1.要是为空的话那就直接执行insert语句将father持久化,结束。
2.要是不为空,那么先执行select的语句查询father表中id为这个father.id的记录是否存在
A:存在,那好执行update语句,结束。
B:不存在,那么执行inset语句,结束。
很简单吧。
但是有一点要注意,看代码;
- @Test
- public void testChildSave() {
- Father f = new Father();
- f.setId(7);
- f.setName("父亲1");
- Session session = sessionFactory.getCurrentSession();
- session.beginTransaction();
- session.merge(f);
- session.getTransaction().commit();
- System.out.println(f.getId());
- }
先说明在执行这段代码之前,father表没有任何记录。 按照上面的流程,先执行select语句,然后执行insert语句
这是控制台给出的hibernate的sql语句
- Hibernate:
- select
- father0_.id as id1_0_,
- father0_.name as name1_0_
- from
- Father father0_
- where
- father0_.id=?
- Hibernate:
- insert
- into
- Father
- (name)
- values
- (?)
- 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:
- Account account = (Account) getHibernateTemplate().merge(po);
- Long id = account.getId();
- System.out.println("/tGet obj after added in dao start ...");
- Account readAccount = (Account) getHibernateTemplate().get(
- Account.class, id);
- System.out.println("/tGet obj after added in dao end ...");
- System.out.println("/tIs po==readAccount ? " + (po == readAccount));
- System.out.println("/tShow detai of po: " + po.toDetailString());
- System.out.println("/tShow detai of readAccount: " + readAccount.toDetailString());
其中,为po设置了两个group
输出结果:
- Hibernate: select ... from SYS_GROUPS where ID=?
- Hibernate: select ... from SYS_GROUPS where ID=?
- Get obj after added in dao start ...
- Get obj after added in dao end ...
- Is po==readAccount ? false
- Show detai of po: Account[0.account_22, groups[2.any one 1.invalid ]]
- Show detai of readAccount: Account[22.account_22, groups[2.engineers 1.administrators ]]
- Hibernate: insert into SYS_ACCOUNTS (...) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- Hibernate: insert into SYS_GROUP_MEMBER (ACCOUNT_ID, GROUP_ID) values (?, ?)
- Hibernate: insert into SYS_GROUP_MEMBER (ACCOUNT_ID, GROUP_ID) values (?, ?)
代码B:
- Long id = (Long) getHibernateTemplate().save(po);
- System.out.println("/tGet obj after added in dao start ...");
- Group group = (Group)getHibernateTemplate().get(Group.class,new Long(1));
- System.out.println("/tGroup detai:" + group.toString());
- Account readAccount = (Account) getHibernateTemplate().get(
- Account.class, id);
- System.out.println("/tGet obj after added in dao end ...");
- System.out.println("/tIs po==readAccount ? " + (po == readAccount));
- System.out.println("/tShow detai of po: " + po.toDetailString());
- System.out.println("/tShow detai of readAccount: "
- + readAccount.toDetailString());
- getHibernateTemplate().merge(readAccount);
- Account readAgain = (Account) getHibernateTemplate().get(Account.class,
- id);
- System.out.println("/tIs po==readAgain ? " + (readAgain == po));
- System.out.println("/tIs readAgain== readAccount? "
- + (readAgain == readAccount));
- System.out.println("/tShow detai again: " + readAgain.toDetailString());
输出结果:
- Get obj after added in dao start ...
- Hibernate: select ... from SYS_GROUPS where ID=?
- Group detai:Group 1. administrators
- Get obj after added in dao end ...
- Is po==readAccount ? true
- Show detai of po: Account[27.account_27, groups[1.invalid 2.any one ]]
- Show detai of readAccount: Account[27.account_27, groups[1.invalid 2.any one ]]
- Hibernate: select ... from SYS_GROUPS where ID=?
- Is po==readAgain ? true
- Is readAgain== readAccount? true
- Show detai again: Account[27.account_27, groups[1.administrators 2.engineers ]]
- Hibernate: insert into SYS_ACCOUNTS (...) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- Hibernate: insert into SYS_GROUP_MEMBER (ACCOUNT_ID, GROUP_ID) values (?, ?)
- 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还是托管状态