Hibernate 奇怪的问题!

Hibernate 奇怪的问题!

写了一些hibernate存储查询的方法,用EJB session bean来封装其调用,然后把这些EJB都成功发布到jboss服务器上。

新建一个测试工程,只把EJB包和一些实体/Model类加入工程CLASSPATH中,在这个工程里面写一些类来测试session bean中

的方法。按我的理解,hibernate的映射和存储方法都在EJB服务器的JVM中运行,远程客户端是不需要知道任何Hibernate

相关包的。但测试中却出问题了:很多方法都不能正常运行,出现类似这样的异常:


java.lang.reflect.UndeclaredThrowableException

at $Proxy1.getRootOrgnization(Unknown Source)

at yadan.client.api.org.OrgManager.getRootOrgnization(OrgManager.java:220)

at yadan.client.test.TestOrgManager.testCreateRootOrg(TestOrgManager.java:42)

at yadan.client.test.TestOrgManager.main(TestOrgManager.java:50)

Caused by: java.lang.ClassNotFoundException: net.sf.hibernate.collection.Set (no security manager: RMI class loader disabled)

at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:368)

at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:159)

at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:631)

at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:257)

at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:200)

at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1513)

at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1435)

at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1626)

at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1274)

at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1845)

at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1769)

at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1646)

at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1274)

at java.io.ObjectInputStream.readObject(ObjectInputStream.java:324)

at java.rmi.MarshalledObject.get(MarshalledObject.java:135)

at org.jboss.invocation.jrmp.interfaces.JRMPInvokerProxy.invoke(JRMPInvokerProxy.java:136)

at org.jboss.invocation.InvokerInterceptor.invoke(InvokerInterceptor.java:87)

at org.jboss.proxy.TransactionInterceptor.invoke(TransactionInterceptor.java:46)

at org.jboss.proxy.SecurityInterceptor.invoke(SecurityInterceptor.java:45)

at org.jboss.proxy.ejb.StatelessSessionInterceptor.invoke(StatelessSessionInterceptor.java:100)

at org.jboss.proxy.ClientContainer.invoke(ClientContainer.java:85)

... 4 more

Exception in thread "main"

错误表明:客户端找不到net.sf.hibernate.collection.Set类。当我把Hibernate所有相关的jar包加入之后,就可以运行通过了。

同样的错误在web应用中出现,(我单独启一个tomcat来测试ejb的调用)web程序如果没有hibernate的jar包就不能正常运行。

非常不解!不知道是什么地方出错了,还是别的原因。请大家指点和测试!

补充,我EJB发布的classpath(server\default\lib)中是加入了 ibernate相关包的。出错是因为客户端和web应用程序CLASSPATH没有加入Hibernate包。我觉得客户端和web应用程序根本没必要这些包啊!

那是因为你在程序中用到了持久对象的集合属性,比如说Set。实际上这个Set不是java.util.HashSet,而是Hibernate自己实现的Set,就是net.sf.hibernate.collection.Set,所以客户端会报错。

冤枉啊~~虽然我在mapping文件中有set类型的映射,但在测试hibernate的方法中根本就没有用这个set。并且把lazy设置成true。

比如说一个Org包含一个Set,Set是User的集合,但我测试的方法,只是创建一个Org,由于User表中的orgId是可以为空的,所以我创建的时候并没有在Org中加入User,但也出这个问题了。如何解释。

问题就在你设的lazy="true"上。

你知道为什么Hibernate要自己实现一套集合类吗?就是为了支持延迟加载。java.util的集合类是不能够用来实现延迟加载的,所以Hibernate才自己实现,因此如果你设了lazy="true",但是在把持久对象序列化传给客户端的之前又没有初始化延迟加载的属性,就肯定会出问题。

谢谢robbin解答!

但是,如何避免这样的问题?如果lazy=true的时候。

不希望客户端有多余的包

在把持久对象传递给客户端之前对延迟加载的集合属性进行初始化。

Hibernate.initialize(PO.set);

那样作,是不是每次load一个Org对象时,会把映射的Set中的User都load一遍呢?

效率会不会有问题?

你传递到客户端的PO对象的延迟加载属性是不可能再取到的,因为延迟加载没有办法进行透明的RMI,所以在取到客户端之前你不强迫进行初始化,即使拿到客户端的PO也不能再用延迟加载属性了。如果你根本就不使用延迟加载属性,那么又何必给PO对象加上这个属性呢?

这样是不是说,在分布式应用中,lazy load作用不大?因为用Hibernate.initialize(PO.set)已经把PO及相关的PO都加载了,一次全序列化到客户端. 不是很清楚.

当然不是,延迟加载当然很有用处。其实你仔细想想看,在一个远程客户端的界面是二维的情况下,是不可能同时显示PO对象列表和所有PO的集合属性的。

或者界面显示的是PO对象列表,例如以表格的形式显示对象的基本属性,然后用户可能会点击某个对象,想要查看它的明细信息,于是会单独显示某个PO的集合属性信息。所以要么客户端取PO对象列表,此时不需要集合属性,因此不必要对集合属性进行初始化,等到客户请求某一个PO对象的集合属性的时候,服务端当然需要对它进行初始化,然后传给客户端了。这是一个和程序界面交互有关的策略问题。

哇塞! 原来又变成客户端程序界面对PO对象的需求问题,好象跟显示的虚模式有类似的地方。

>>>你传递到客户端的PO对象的延迟加载属性是不可能再取到的,因为延迟加载没有办法进行透明的RMI,所以在取到客户端之前你不强迫进行
>>>初始化,即使拿到客户端的PO也不能再用延迟加载属性了。如果你根本就不使用延迟加载属性,那么又何必给PO对象加上这个属性呢?

其实我对lazy=true这个属性本身不太明白。只是记得看过一些文章说最好把lazy设成true,能提高效率。

我只是希望达到这样的目的:在一个查找PO类的方法中,我并不关心其集合类,只需要查找PO的一般属性,

以为设置lazy=true可以不查找集合类,提高效率。但没想到会出现上面那个错误。

ejb 环境下应该有 value object 的层次。

就本质上而言 hibernate 和 cmp 一样,都是 or-map 的层次。所以, cmp 和 hibernate 在很多地方可以类比。 ejb 层面的 cmp 显然是不应该直接返回给前端客户程序使用的,所以有了 senssion facade - value object 模式,将包装过后的数据返回给前端的 web 层,对于 hibernate 也是一样。 ejb 中的 senssion bean 通过 local 的 dao 访问 hibernate 得到数据,再包装成 value object 返回给前端。

从设计的原则上来将,前端应用需要了解和依赖后端的实现方式,这既不必要也不合理。value object 的引入,很好的“隔离”了这两个层次的实现细节。