Hibernate学习_008_update()等方法总结

数据库操作中,更新一个对象是常常发生的一个动作,update方法其实涉及到很多的细节问题,现在一一将其理清。

第一:update不可以操作一个没有分配ID的瞬态对象,因为,hibernate更新的时候,发出的where条件是根据id过滤,所有,如果给一个瞬态对象分配一个数据库中已经存在的id值,然后在更新这个数据,就不会报错。

代码示例1:

@Test
	public void testUpdate1() {
		Teacher t = new Teacher();
		t.setName("testupdate1");
		Session session = sf.getCurrentSession();
		session.beginTransaction();
		session.update(t);//此时数据库中不存在对应数据,数据处于Transient状态。
	    session.getTransaction().commit();
	}
测试输出如下:

Hibernate: 
    update
        _Teacher 
    set
        birthDay=?,
        myWifeName=?,
        name=?,
        title=?,
        zhicheng=? 
    where
        id=?
20:08:16,618 ERROR AbstractBatcher:73 - Exception executing batch: 
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
这个例子说明:本来可以更新一行数据的,但是这个SQL语句由于没有找到ID对应的记录,所以,值更新了0行,hibernate把这个当成一种错误处理。

代码示例2:

@Test
	public void testUpdate1() {
		Teacher t = new Teacher();
		t.setName("testupdate1");
		t.setId(1);
		Session session = sf.getCurrentSession();
		session.beginTransaction();
		session.update(t);
	    session.getTransaction().commit();
	}
在这里,我们给我们的对象一个我们数据库中已存在记录的id值,这个时候,就可以更新数据了。发出的sql语句和上面是一样的,但是,注意看,他们更新所有字段,由于我们瞬态对象是我们自己new出来的,所以,除了ID,name有值外,其他为null,所以数据更新后数据库中对应的记录就只有两个属性值了。

第二:当我们更新一个detached状态的数据后,数据就会在缓存中放一个引用来指向内存中的数据,这个时候,数据的状态就会变为persistent状态。还有,当处于persistent状态的数据,只要数据的状态发生改变,就会更新数据库。

代码示例:

@Test
	public void testUpdate1() {
		Session session = sf.getCurrentSession();
		session.beginTransaction();
		Teacher t = (Teacher)session.load(Teacher.class, 1);
		<span style="color:#ff0000;">t.setName("update1");
		t.setName("update2");</span>
	    session.getTransaction().commit();
	}
测试输出结果:

Hibernate: 
    select
        teacher0_.id as id1_0_,
        teacher0_.birthDay as birthDay1_0_,
        teacher0_.myWifeName as myWifeName1_0_,
        teacher0_.name as name1_0_,
        teacher0_.title as title1_0_,
        teacher0_.zhicheng as zhicheng1_0_ 
    from
        _Teacher teacher0_ 
    where
        teacher0_.id=?
Hibernate: 
    update
        _Teacher 
    set
        birthDay=?,
        myWifeName=?,
        name=?,
        title=?,
        zhicheng=? 
    where
        id=?
仔细看,上面的代码我两次设置了那么属性,但是我们从输出结果看,但是只发出了一条update语句,这就说明的两点:第一:persistent状态的数据更新发生在事物提交的时候,第二:在提交之前,无论对属性修改多少次,真正提交的时候,只会更新一次数据,发出一条sql语句。

究其原因其实很简单:当一个对象为persist状态的时候,session缓存中就有这个对象的引用,当我们修改持久化状态的对象的属性时,其实不管修改多少次,都无所谓,在我们提交事务的时候,这个时候hibernate就会将缓存的对象和数据库中的数据对比,如果发现缓存的对象和数据库中的数据不一致的时候,就会发出sql语句,来更新数据库中的数据。

第三:我们发现,在上面的两种更新中,我们每次只更新一个字段,从发出的SQL语句来看,数据库中却把全部的数据字段给我们都更新了。这个时候假设一条记录中有一个字段值长度特别长,这个时候,更新这个数据的效率就会比较低,那如何使得每次更新数据库的时候,只更新我们改过的字段呢?hibernate提供了三种方式:

第一种方式:将XML映射文件中的对象属性加上update。

 <class name="Student" table="student">
        <id name="id" column="id">
        	<generator class="identity"></generator>
        </id>
        <property name="name" column="name" /> 
        <property name="age" column="age" <span style="color:#ff0000;">update="false"</span>/>
   </class>
@Test
	public void testUpdate1() {
		 Session session = sf.openSession();
	     session.beginTransaction();
	     Student s = (Student)session.get(Student.class, 1);
	     s.setName("update2");
	     session.getTransaction().commit();
	     session.close();
	}
测试输出如下:由于我们只改变了name,因此,只会更新name属性。

Hibernate: 
    update
        student 
    set
        name=? 
    where
        id=?
第二种方式:第一种方式要每个属性单独设置,如果属性一多,将会很麻烦,第二种方式就是在类的层次结构上设置dynamic-update。 不过这种方式在JPA中并不支持,hibernate的扩展提供了支持。

示例代码:(此种方式,和第一种方式只有xml配置文件不一样)

 <class name="Student" table="student"<span style="color:#ff0000;"> dynamic-update="true"</span>>
        <id name="id" column="id">
        	<generator class="identity"></generator>
        </id>
        <property name="name" column="name" /> 
        <property name="age" column="age"/>
   </class>
第三种方式:直接使用EJBQL(HQL)语句。这种方式由于自己写sql语句,肯定是只会更新更改发生更改了的字段。无需多解释。

第四:关于更新的两点说明:

第一点说明:更新数据的时候,hibernate会比较session缓存中的对象和数据库中保存的记录的一致性,发现状态不一致的时候,才会发出更新语句,如果数据一致的话,是不会发出更新语句的,比如,我们加载了Student对象T,假设数据库库中T的name属性为“stuName”,假设我们在程序中更改了T的name属相,但是更改后的值依旧是“stuName”,此时在事物提交的时候,由于缓存中的对象的状态和数据库一致,所以,是不会发出update语句的。这点很容易混淆。

第二点说明:上面第三讲解了三种如何只更新数据发生了变化了的属性,这是如何做到的?其实就是比较了缓存中的对象和数据库中的数据的一致性,只更新发生了变化的部分。所以,我们可以想到,如果一个对象是属于detached状态的话,这个时候,内存中存在对象,但是session缓存中是不存在这个对象的(其实是一个指向内存对象的引用),所以,就没有了比较的对象,所以,我们可以猜想,如果update一个处于detached状态的数据时,即使是一个属性都没有修改,只要一update,一定会更新所有的状态。

看如下的代码就知道事实就是这样的:

示例代码:

@Test
	public void testUpdate1() {
		 Session session = sf.getCurrentSession();
	     session.beginTransaction();
	     Student s = (Student)session.get(Student.class, 1);
	     session.getTransaction().commit();
	    <span style="color:#ff0000;"> //此时的s处于detached状态。</span>
	     Session session2 = sf.getCurrentSession();
	     session2.beginTransaction();
	     session2.update(s);<span style="color:#ff0000;">//更新一个处于detached的对象,其实这个对象没有改变任何属性。依旧会发出update语句。</span>
	     session2.getTransaction().commit();
	}
测试输出代码:

Hibernate: 
    select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        student student0_ 
    where
        student0_.id=?
Hibernate: 
    update
        student 
    set
        name=?,
        age=? 
    where
        id=?
那么要如何避免这种情况呢?在hibernate中提供了一个merge方法,从方法名知道这对应的而是一个合并的操作。这种情况下,在detached状态的数据在merge到数据库的时候,首先将会先加载数据库中的数据,然后对比和内存中数据的差异,从而只更新已经更改的属性,如果对象属性没有发生改变,就不会发出更新语句。

测试代码如下:

@Test
	public void testUpdate1() {
		 Session session = sf.getCurrentSession();
	     session.beginTransaction();
	     Student s = (Student)session.get(Student.class, 1);
	     session.getTransaction().commit();
	     //此时的s处于detached状态。
	     s.setName("tasteapple");
	     Session session2 = sf.getCurrentSession();
	     session2.beginTransaction();
	     session2<span style="color:#ff0000;">.merge</span>(s);
	     session2.getTransaction().commit();
	}
测试输出代码:

Hibernate: 
    select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        student student0_ 
    where
        student0_.id=?
Hibernate: 
   <span style="color:#ff0000;"> select
        student0_.id as id0_0_,
        student0_.name as name0_0_,
        student0_.age as age0_0_ 
    from
        student student0_ 
    where
        student0_.id=?</span>
Hibernate: 
    update
        student 
    set
        name=? 
    where
        id=?
从输出结果看,确实发出了两条select语句,第二条语句,就是去先加载数据库中的数据,然后和detached状态的对象比较,如果属性状态存在差异,就发出update语句更新数据库的数据状态,如果没有任何差异,update语句就不会发出了。









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值