hibernate中同时保存主从表信息----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>

猜你喜欢

转载自blog.csdn.net/dimples_qian/article/details/80859245