论坛首页 Java企业应用论坛

这样的对象是否持久化了呢?

浏览 21310 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-09-13  
一个对象是否被持久化过,应该取决于这个对象是否是从数据库查询出来的。这个是显而易见的。今天在做项目的时候发现这样一个问题:例如有一个类Parent,其主键是id,还有name等字段,另一个类Child于Parent构成父子关系。我现在希望通过级联删除将某一个父亲和它的儿子都删除了,按照Hibernate手册上的做法:
Parent p = (Parent); session.load(Parent.class, pid);;
session.delete(p);;

这个非常容易理解,将父亲从数据库中读出来,那么这个对象显然已经被持久化了。再删除时,如果配置文件都正确,会进行级联删除。
如果现在我已经有了parent的主键pid,直接去构造一个和数据库查出来的parent类一摸一样的对象p:
Parent p = new Parent();;
p.setId(pid);;
p.setName(name);;
......

并用上面的一段代码来代替:
Parent p = (Parent); session.load(Parent.class, pid);;

同样执行删除操作,Hibernate会报错,并且告诉我有外键的约束。

我不明白的是:对于delete操作,Hibernate到底是如何判别这个Parent对象是否是被持久化过的呢?我直接构造一个和数据库中查出来一样的对象,从外观上看似乎是一致的,但是我也知道,从本质上讲,一定不一样。只是对于Hibernate来说,它到底是如何分辨出这两种不同的对象的呢?
   发表时间:2004-09-14  
通过Parent p = new Parent(); 声明出来的 Parent对象只是处于Transient状态而不是Persistent状态 ,只有在Persistent状态的对象才可以执行delete操作,而用get或load得到的直接就是Persistent状态 的对象,而在Transient状态的对象只有通过 saveOrUpdate或者update才能转换到persistent状态,如果利用上面的new Parent()得到的对象,可能会equal persistent状态的对象,但绝对不是==persistent状态的对象。
0 请登录后投票
   发表时间:2004-09-14  
手动把所有的child都建出来, 这样就能删除了.

Parent p = new Parent();
p.setId(pid);
Child c = new Child();
c.setId(cid);
p.addChild(c);

session.delete(p);

Hibernate通过什么来判断一个对象是否被持久化过??
是id 的 unsaved-value

搜索论坛一下吧, 偶记得Robbin有一篇帖子讲得很清楚的.
0 请登录后投票
   发表时间:2004-09-14  
昨天晚上又重新阅读了robbin的两篇大作:《update和saveOrUpdate详解》和《关于unsaved-value的再问》,今天早上又做了一点实验,稍微做一下总结:
引用

在Hibernate中,最核心的概念就是对PO的状态管理。一个PO有三种状态:

1、未被持久化的VO
此时就是一个内存对象VO,由JVM管理生命周期

2、已被持久化的PO,并且在Session生命周期内
此时映射数据库数据,由数据库管理生命周期

3、曾被持久化过,但现在和Session已经detached了,以VO的身份在运行
这种和Session已经detached的PO还能够进入另一个Session,继续进行PO状态管理,此时它就成为PO的第二种状态了。这种PO实际上是跨了Session进行了状态维护的。


在我楼上的这个帖子中,两个不同的删除方法,之所以会不同,就是在于,上面两种删除方法中的PO,分别对应于robbin所说的第一种和第二种状态的PO,因此,只有第二种状态的PO才会被正确删除,而第一种状态的PO不会被认可。

今天早上我尝试了第三种PO的状态。事实证明,PO在第三种状态下,同样可以被正确删除。
ParentDAO dao = new ParentDAOImpl();;
Parent p = dao.getParent(pid);;
dao.deleteParent(p);;
 

其中getParent函数和deleteParent都是封装在DAO中的,而我的每个DAO操作都在操作结束后关闭session。显然这个p对象在传入dao.deleteParent(p);时,处于第三种状态。但是在这种情况下,p即使有外键关联,也可以成功的进行级联删除。

总结下来,只要PO的状态是第二种或者第三种,Hibernate都可以成功进行delete和update操作,如果是第一种,那么Hibernate就认为,还没有资格调用上面两种操作。

我现在最大的疑问只是:对于传入dao.deleteParent(p);的这个对象p,Hibernate到底是如何分辨其状态的?毕竟三种状态的对象可以有一摸一样的属性的对应值。

希望大家踊跃讨论。
0 请登录后投票
   发表时间:2004-09-14  
引用

如果是第一种,那么Hibernate就认为,还没有资格调用上面两种操作。

No, a transient instance can be deleted also.
check the source code: SessionImpl.delete()
0 请登录后投票
   发表时间:2004-09-14  
引用

手动把所有的child都建出来, 这样就能删除了.

Parent p = new Parent();
p.setId(pid);
Child c = new Child();
c.setId(cid);
p.addChild(c);

session.delete(p);

经过我的试验,这种方法是不可行的。

引用

Hibernate通过什么来判断一个对象是否被持久化过??
是id 的 unsaved-value

我认为这句话也是不对的。首先,我的unsaved-value设置为null,而我在新建一个po对象的时候
Parent p = new Parent();; 
p.setId(pid);; 
p.setName(name);;
......

也显然把主键的id设置了进去,它于unsaved-value显然不会相等。这样它就认为这是一个已经被持久化了的对象了?应该不是吧。

引用

如果利用上面的new Parent()得到的对象,可能会equal persistent状态的对象,但绝对不是==persistent状态的对象

不知道这个是什么意思?在没有load一个persistent对象的时候,new出来的对象去和谁比呢?
0 请登录后投票
   发表时间:2004-09-14  
引用

手动把所有的child都建出来, 这样就能删除了.

Parent p = new Parent();
p.setId(pid);
Child c = new Child();
c.setId(cid);
p.addChild(c);

session.delete(p);

经过试验,这种方法是可行的,为我的不仔细向可敬的Readonly同志道歉。

这样的话,我想我应该明白了,load出来的对象由于每个字段都被load出来,再传入的时候,Hibernate发现它的主键所指的那条记录所对应的对象和传入的完全一致,就认为是一个持久对象。看来它不是只比较一个主键就简单完事了。

谢谢大家的回答,尤其是Readonly,谢谢!
0 请登录后投票
   发表时间:2004-09-14  
我的意思是说,如果有下面这样的语句
  PO1 a=session.load(PO1.class,id);;
  PO1 b=session.load(PO1.class,id);;


那么
肯定有a==b 为true
但如果
PO1 c=new PO1();;

则 a==c 肯定为false
0 请登录后投票
   发表时间:2004-09-17  
这个贴子质量很高啊.阿扑阿扑
0 请登录后投票
   发表时间:2004-10-14  
Hibernate通过unsaved-value来确定对象是否被持久化过。你完全可以通过自己setId的方式来欺骗Hibernate,让Hibernate认为你new出来的临时对象已经被持久化过。

只不过这样的方式没有太大实际的用途,你也许可以用来写测试。如果你new出来的临时对象是数据库没有的记录,你让Hibernate去操作该记录,只会造成程序运行异常。

总而言之,最好不要自己管理id,而是让Hibernate管理Id,POJO的setId()方法虽然是public的,但那不是开放给你用的,只是开放给Hibernate使用的。我一向认为setId是罪恶的。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics