Hibernate对象的生命周期

本文深入探讨了Hibernate中对象的瞬时、持久和脱管三种状态,详细解释了每种状态的特征和意义,并通过实例展示了如何在实际应用中使用这些状态。重点介绍了merge方法的使用场景及部分字段更新的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[b]一、情景描述[/b]

鉴于:hibernate是面向对象(实体、entity)操作,而不是某个对象的字段或属性。
鉴于:使用hibernate从数据库获取对象后,无需对数据库操作,而是直接操作获取的对象,hibernate就可以自动同步数据库。

因此:hibernate将对象分为三种状态,以便进行区分和管理:

1:瞬时/临时(Transient)
可以理解为该对象和hibernate一点关系也没有。
对该对象的操作不会和hibernate有任何联系或影响。

2:持久(Persistent)
使用hibernate对该对象进行管理,这会涉及到hibernate自动对数据库的相应的操作。

更细节的解释:
(hibernate通过对外提供session对象,和session对象的一系列方法,完成对数据的增删改查。hibernate自动对数据库的操作也是依赖于和session绑定)。
也就是说,在持久状态下,hibernate对对象的操作和seesion是分不开的。

3:脱管/游离(Detached)
这种状态是根据hibernate是否建立了session链接,以便与第2种状态进行区分。
可以理解为除了没有建立session(未连接数据库),其它的和第2种状态都一样。
hibernate已经对该对象进行绑定,并管理。只是没有建立session链接。


[b]二、词汇定义[/b]


[img]http://dl2.iteye.com/upload/attachment/0086/7738/12cfb448-d256-30e8-aae1-a5a0f4d04153.gif[/img]

1:瞬时/临时(Transient) - 由new操作符创建,且尚未与Hibernate Session 关联的对象被认定为瞬时的。瞬时对象不会被持久化到数据库中,也不会被赋予持久化标识(identifier)。 如果瞬时对象在程序中没有被引用,它会被垃圾回收器销毁。

2:持久(Persistent) - 持久的实例在数据库中有对应的记录,并拥有一个持久化标识。 持久的实例可能是刚被保存的,或刚被加载的,无论哪一种,按定义,它存在于相关联的Session作用范围内。 Hibernate会检测到处于持久状态的对象的任何改动,在当前操作单元执行完毕时将对象数据与数据库同步。开发者不需要手动执行UPDATE。

3:脱管/游离(Detached) - 与持久对象关联的Session被关闭后,对象就变为脱管的。 对脱管对象的引用依然有效,对象可继续被修改。脱管对象如果重新关联到某个新的Session上, 会再次转变为持久的,在脱管期间的改动将被持久化到数据库。


[b]三、理解这三种状态的意义[/b]


便于使用hibernate提供的各种方法。意义是重大的。
比如:
merge()
session.close()对对象状态的影响
获取新的session后,对象状态,对数据库造成的影响。


[b]四、讨论[/b]

说,这三种状态是hibernate的对象的生命周期,是不准确的。
因为每个状态都不是对象必须经过的。
但是这对象在这三种状态中可以相互转换。
就是状态。对象在hibernate中所表示的三种状态。


[b]五、应用举例[/b]


1、关于merge方法

什么时候用?:
这个方法就是为在前台修改从后台读取的数据准备的。

【hibernate 需要设置为: hibernate update部分更新。因为直接使用merge()方法,会把数据库中有而传递过来的对象中没有的字段,会重置。这样就得在传参数时,把数据库中的字段都得写全。
具体参照:】
[url]http://www.cnblogs.com/hyteddy/archive/2011/07/21/2113175.html[/url]

【其实,merge()方法解决了新new一个对象时,如何将其同步到数据库中去的问题。但是,需要把所有的字段写全,需要在前台页面中添加隐藏变量,这显然是不一个好方法。关于如何实现hibernate部分字段更新问题,请看这里(内容会拷贝到本文后面):】
[url]http://www.rritw.com/a/JAVAbiancheng/Hibernate/20101207/52757.html[/url]

session.merge ()方法对状态的变化
    public void run() {    
UserInfo userInfo = new UserInfo();//创建UserInfo实例
userInfo.setId(11112);//使之成为脱管状态
userInfo.setName("RW3");
userInfo.setSex("M");

UserInfo userInfo2 = new UserInfo();//创建UserInfo实例
userInfo2.setId(11112); //使之成为脱管状态
userInfo2.setName("RW4");
userInfo2.setSex("F");

Session session = HibernateSessionFactory.currentSession();//启动Session
Transaction tx = session.beginTransaction();//启动事务

//调用merge方法,此时UserInfo实体状态并没有被持久化
//但是数据库中的记录被更新了
①session.merge(userInfo);
session.merge(userInfo2);//调用merge方法,此时UserInfo实体状态并没有被持久化

//merge方法与update方法的差别在于针对同样的操作update方法会报错
//原因在于update方法使得实体状态成为了持久化状态,而Session中不允许两个持久化实体有同样的持久化标识
②//session.update(userInfo);
//session.update(userInfo2);
//以下两句不会发送SQL,因为userInfo2不是持久化状态的实体
③userInfo2.setName("RW5");
userInfo2.setSex("M");

//提交事务
tx.commit();
//关闭Hibernate Session
HibernateSessionFactory.closeSession();
}

关于使用setId()后对象就成为托管状态,可以这么猜测:hibernate对我们new的实体类进行了管理。

针对该段代码将执行如下SQL语句:
Hibernate:
/* ①session.merge(userInfo2)的动作 */
select
userinfo0_.id as id0_0_,
userinfo0_.NAME as NAME0_0_,
userinfo0_.SEX as SEX0_0_,
userinfo0_.roomid as roomid0_0_
from
userinfo userinfo0_
where
userinfo0_.id=?

Hibernate:
/* ①session.merge(userInfo2)的动作 */
update
userinfo
set
NAME=?,
SEX=?,
roomid=?
where
id=?

[color=green]session.merge()方法会首先发送一句select语句,去数据库端获取UserInfo持久化标识所对应的表记录;
然后自动生成一个持久化状态的UserInfo实体,与脱管状态的UserInfo实体做比较是否有所改变;一旦发生了改变,才会发送update语句执行更新。

而按执行顺序,若两句session.merge()方法针对同一个脱管状态的UserInfo实体,
那其结果只会执行最后一个session.merge()方法所发出的update语句。
即使执行了session.merge()方法,UserInfo实体依然是脱管状态,
因此③userInfo2. setName("RW5")的语句不会同步数据库中的表。[/color]


扩展阅读:
[url]http://selvemen.iteye.com/blog/457225[/url]


2、我看merge()

[color=green]merge的作用:对于一个对象(无论是否是新new的,还是处于游离态的):
如果数据库中有对应的ID,则会update;
如果没有,则会insert;

使用merge方法后,对对象之前的状态,不产生任何影响。
即:对象在使用merge()方法之前是什么状态,使用merge()方法后还是什么状态。[/color]


例子:
下面是当对象(str1)在第一个session关闭后,处于游离状态。
第二个session开启,又get或load一样的ID的数据出来时, 对那个游离态对象(str1),使用update肯定会出错,原因是持久层中已经有对象(str2),你的update会让那个游离态对象也变成持久态,两个持久态会冲突。
然而用merge的话,它会把第一个的对象数据赋值给已经处于持久化的那个对象中,而自己本身不得变为持久态;
    Session session1 = HibernateUtils.getSession();
Transaction transaction1 = session1.beginTransaction();
Students str1 = (Students)session1.get(Students.class, 2);
transaction1.commit();
session1.clear();
session1.close();//session关闭,str1为游离态(detached)

Session session2 = HibernateUtils.getSession();
Transaction transaction2 = session2.beginTransaction();
Students str2 = (Students)session2.get(Students.class, 2);
str1.setName("wer");
session2.merge(str1);//更新数据库
System.out.println(str2.getName()); //这里改变了。因为str2是持久状态的。
str2.setName("ee");
System.out.println(str1.getName()); //这里不会改变,说明对str1使用merge()后没有被持久化;
transaction2.commit();
session2.clear();
session2.close();



===============================================
[b]题外话题[/b]
hibernate部分字段更新的解决方案

在调用Hibernate的update方法时会更新对象的全部字段,若对象属性值为null,则相应的数据库字段也会被更新为空值。
通常的做法 是先将需要更新的对象加载上来,再将需要更新的属性值一个个的setter到对象中,但这样显然影响了代码的可读性,而且在使用 Struts2+hibernate进行开发时,页面传递参数后Struts2会将对象进行自动赋值,已经赋值的对象在更新前又要进行一次手动赋值再 update,这样明显散失去了sturts2的自动赋值的意义。

解决方法:

将需要更新的对象加载上来后,利用BeanUtils类的copyProperties方法,将需要更新的值copy到对象中,最后再调用update方法。

注 意:这里推荐使用的方法并非org.apache.comm*****.beanutils包中的方法,而是 org.springframework.beans.BeanUtils中的copyProperties方法。原因是Spring工具类提供了 copyProperties(source, target, ignoreProperties)方法,它能在复制对象值的时候忽略指定属性值,保护某些值不被恶意修改,从而更安全的进行对象的更新。此外,根据一些 测试结果spring中的copyProperties方法效率要高于apache的方法(这点未作进一步验证)。

参考代码:

Admin persistent = adminService.load(id);// 加载对象
BeanUtils.copyProperties(admin, persistent, new String[]{"id", "username"});// 复制对象属性值时忽略id、username属性,可避免username被恶意修改
adminService.update(persistent);// 更新对象

具体代码可参考SHOP++源代码。
以上为SHOP++技术教程。来源:http://www.shopxx.net

本文摘自:http://chinaxxren.javaeye.com/blog/834675


--
引用:
[url]http://zhidao.baidu.com/question/469345066.html[/url]
[url]http://blog.youkuaiyun.com/lang_man_xing/article/details/7572964[/url]


-
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值