总结:inverse不让主表对象生成快照和缓存时生成外键那部分信息,从而达到两个save语句执行后只会有两条insert语句而不会有update语句;cascade(级联)是为了只写一句save就能同时保存主从表信息。
这是inverse部分:
如果我们需要新增一个user和一个dog,而这个dog正好属于这个user,那么我们会这么写:
@org.junit.Test
public void test() {
Session sesission = SessionFactoryUtils.getSessionFactory().getCurrentSession();
Transaction tx = sesission.beginTransaction();
User user = new User();
user.setUsername("xixixi");
Dog dog = new Dog();
dog.setDogname("哈士奇");
user.getDogs().add(dog);
dog.setDogmaster(user);
sesission.save(user);//语句1
sesission.save(dog);//语句2
tx.commit();
}
这样执行的结果是执行两条insert语句和一条update语句,其中update语句如下:
update dog set dogmaster = ? where dogid = ?
为什么?因为在执行完语句1之后,马上会在session中产生一级缓存和快照,这时候user中的那个dog还是没有OID的,然后当执行完语句2之后,马上返回一个dog,此时的dog已经有了OID,而这个dog和刚才那个一级缓存当中的user中的dog是同一对象(但快照中的那个user的内容是不会改变的,那是拷贝的),所以一级缓存中的user对象也随之改变了,然后hibernate再对dog做缓存和快照。从上面可以看出,在commit的时候,会检测到user发生了变化,而dog不受影响,故会执行后面的update语句。
那为什么会是上面那种update方式,就只更新一个字段呢?始终记住,dogs属性只不过是User对象用来表达外键的一种方式,所以它的信息也就是象征着Dog表中dogmaster那个字段的信息,所以还有必要去写update(dog)来更新全字段嘛?根本不用啊,所以它只要更新Dog表中那条记录的外键就可以了,因为整个这条记录的信息都记录在dog对象里里,所以获取参数dogmaster 和 dogid自然很容易了。细想后你发现,这个update语句完全没有存在的必要,因为语句2执行完后Dog表中的记录已经完整了,此时在一级缓存中的dog就是数据库中的真实记录,然后你又根据它去做update操作,这不是废话?
最后解决方法,简单了,在User.hbm.xml的Set标签下配置一下inverse属性为true,让user在生成快照时放弃生成外键那部分快照即可:
<set name="dogs" table="dog" inverse="true">
<key column="dogmaster"></key>
<one-to-many class="Dog"/>
</set>
这是cascade:
还是上面的例子,我现在想只写一句save就同时保存住表、从表信息,行不行呢?答案是可以! 因为你想嘛,user对象里有dog,dog对象里也有user,从理论上来说肯定可以实现,那么我们这么写:
<!-- 就是这个cascade属性起了作用,值save-update代表保存更新操作都会级联执行。 -->
<set name="dogs" table="dog" inverse="true" cascade="save-update">
<key column="dogmaster"></key>
<one-to-many class="Dog"/>
</set>
然后 session.save(user);
再或者这么写:
<many-to-one name="dogmaster" column="dogmaster" class="User" cascade="save-update"></many-to-one>
然后 session.save(dog);
友情提醒:当cascade和inverse属性都配置了的时候,我们可以实现一句save保存两张表的记录,并且保存时执行的都是insert不会有update语句出来。但是如果你只配cascade没有inverse,不要以为只有一句话所以不存在对象的快照和一级缓存不一致的问题,就妄想它应该不会输出update,告诉你,照样输出,它执行的原理和你写两句save其实是一模一样的!因为虽然你只写了一句,但是hibernate执行的时候仍然是一条条insert依次执行的,所以insert完主表信息后,照样产生带外键信息的快照和一级缓存,剩下的就不用说了吧,所以记得两个属性一起配,顺带截个图,这是只配cascade不配inverse的结果:
Hibernate:
insert
into
user
(username, password)
values
(?, ?)
Hibernate:
insert
into
dog
(dogname, dogcolor, dogage, dogmaster)
values
(?, ?, ?, ?)
Sun Jul 01 10:50:59 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended.
Hibernate:
update
dog
set
dogmaster=?
where
dogid=?