使用DWR開始開發項目到現在,感覺真的經歷了一段很長的路,因為其間遇到了很多的問題需要解決,一點兒不順利。這個過程差不多總是這樣的:寫一小段程序,一運行就出錯了,是什么原因呢,看了半天錯誤提示,不明白。怎么辦,到DWR官方網站去看看吧,也許可以解決問題。如果不能,就把錯誤提示粘到百度去搜索一下國內網站吧。如果還不能,就到谷歌里去搜索一下國外網站。如果這些都不行,只能用最土的辦法,設斷點跟源碼了。可以說DWR是我所用過的目前資料最少的一個框架,也是自身問題最多的一個框架。我所遇到過的很多問題都是通過跟蹤源碼的方式才發現和解決問題的。現在我就把發現到并解決的問題跟大家共享一下吧。當然它們中的有些問題不一定就是DWR的事,也有hibernate的問題。
一、轉換一對多值對象出現的問題
我在《DWR幫助文檔-dwr.xml文件的配置》一文中提到,我們應當把在dwr中用到的所有值對象都注冊到dwr.xml配置文件中。在配置值對象的時候,有2種方式:使用model.*這種通配符的方式一次性將多個值對象進行注冊;使用hibernate3或hibernate2的方式一個一個注冊值對象。同時我也提到,使用第一種方式注冊雖然簡便,但存在不利于編寫前端頁面和性能差的問題,不推薦使用;使用第二種方式注冊有利于程序的編寫和性能的提高,但在dwr中存在bug。這樣的bug是什么呢?我們不妨舉一個實例來說明。假如在項目中存在部門和員工的值對象,并且是一對多的關系(每一個員工都只能在一個部門,但多個員工可以在用一個部門),那么hibernate在查詢員工的時候會返回一個員工值對象的集合,集合中的每一個員工值對象都有一個部門屬性,對應一個部門值對象,但多個員工值對象對應的可能是一個部門值對象。這個關系很清晰,也應該沒有什么疑問,但在dwr將其轉換為js中的對象的過程中卻會發生錯誤。錯誤的提示是這樣的:
- directwebremoting.extend.MarshallException: Error marshalling com.htxx.demo.model.Employee: Ignoring request to inline on reference for: Object:s0:{departmentId=Simple:"001", departmentName=Simple:""u4EBA"u529B"u8D44"u6E90"u90E8", employees=Simple:null}. See the logs for more details.
- at org.directwebremoting.convert.BasicObjectConverter.convertOutbound(BasicObjectConverter.java:200)
- at org.directwebremoting.dwrp.DefaultConverterManager.convertOutbound(DefaultConverterManager.java:192)
- ...
- Caused by: java.lang.IllegalStateException: Ignoring request to inline on reference for: Object:s0:{departmentId=Simple:"001", departmentName=Simple:""u4EBA"u529B"u8D44"u6E90"u90E8", employees=Simple:null}
- at org.directwebremoting.dwrp.AbstractOutboundVariable.getReferenceVariable(AbstractOutboundVariable.java:148)
- at org.directwebremoting.dwrp.DefaultConverterManager.convertOutbound(DefaultConverterManager.java:181)
- at org.directwebremoting.convert.BasicObjectConverter.convertOutbound(BasicObjectConverter.java:189)
- ... 28 more
org.
仔細查看和跟蹤源碼,我發現這個問題出在AbstractOutboundVariable類的getReferenceVariable方法中,它的源碼是這樣寫的:
- /* (non-Javadoc)
- * @see org.directwebremoting.OutboundVariable#getReference()
- */
- public OutboundVariable getReferenceVariable()
- {
- if (reference == null)
- {
- reference = new ReferenceOutboundVariable(getVariableName());
- if (forcedInlineStatus)
- {
- throw new IllegalStateException("Ignoring request to inline on reference for: " + this);
- }
- else
- {
- setInline(false);
- }
- }
- return reference;
- }
二、轉換一對一值對象出現的問題
這個問題我同樣在《DWR幫助文檔-dwr.xml文件的配置》一文中提到過,就是dwr在轉換一對一值對象關系時出現死循環的問題。假如Employee值對象存在一個且最多一個Address值對象的時候,Employee有一個Address屬性指向一個Address值對象,而Address值對象也同樣會有一個Employee屬性指向這個Employee值對象。我們知道hibernate對于一對一關聯是不進行延遲加載的,所以dwr在裝載和轉換Employee值對象的時候會去加載它的Address值對象,然后在加載這個Address值對象的時候會又去加載那個Employee值對象。如此反復加載就形成了死循環。解決這個問題的辦法就是在dwr.xml配置Address的時候,使用exclude禁用它對Employee的加載。詳細說明見《DWR幫助文檔-dwr.xml文件的配置》,這里就不再羅嗦了。
三、找不到值對象中set函數的問題
在使用dwr執行插入和更新操作的時候,有時會出現以下的警告:
Missing java bean property to match javascript property:[屬性名]. For causes see debug level logs:如果將log4j中dwr的級別降低為DEBUG級別,你還可以在控制臺中看到一下提示:
You may be missing the correct setter: setXXX()The property may be excluded using include or exclude rules.其實你并沒有在dwr中禁用這個屬性,同時也正確地在值對象中編寫了一個setXXX()方法的,這個問題到底出在哪里呢?我查看了dwr的源碼發現,dwr是如何獲得一個值對象的所有屬性及其各自的get和set方法呢?是采用Java的反向機制。如果你通過自己去編寫或修改某個屬性及其它的get和set方法,而不是通過工具生成,那么反向機制就可能找不到這個屬性的get和set方法(注意,是可能而并不總是這樣)。這個問題解決的辦法就是刪除該屬性的get和set方法,然后使用MyEclipse的get和set方法生成工具自動生成。
四、找不到值對象的LazyInitializer的問題
在使用dwr執行插入和更新操作的時候,有時會拋出這樣一個離譜的異常:
java.lang.
- NoSuchMethodError: com.htxx.demo.model.Department.getHibernateLazyInitializer()Lorg/hibernate/proxy/LazyInitializer;
- at com.htxx.demo.model.Department$$EnhancerByCGLIB$$f4b4f445.getHibernateLazyInitializer()
- at org.directwebremoting.hibernate.H3BeanConverter.getClass(H3BeanConverter.java:139)
之所以說這個異常比較離譜,是因為它似乎就沒有dwr什么事。事實上它也是沒有dwr什么事,而是hibernate出錯了。查看hibernate的官方網站你可以發現這個bug的發布,并在hibernate3.2.0.rc4以后的版本這解決這個問題了。將hibernate3換成最新版本后果然就解決這個問題了。
五、執行查詢時找不到hibernate查詢工廠類的問題
這個還是hibernate3的問題。在執行查詢的時候在控制臺顯示這樣一個錯誤:
CharScanner; panic: ClassNotFoundException: org.hibernate.hql.ast.HqlToken
遇到這個問題不要怕,其實解決的方法很簡單,就是在spring的配置文件ApplicationContext-hibernate.xml中,在sessionFactory的hibernateProperties屬性中增加這樣一段話:
- <property name="hibernateProperties">
- <props>
- ...
- <prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactoryprop>
- props>
- property
六、在JDK1.4中運行和部署DWR2和hibernate3出現的問題
在DWR2.0中提供了一些JDK5中才能使用的annotations的功能。然而,這個功能在JDK1.4的環境中進行啟動或者部署會發生錯誤。抱歉的是這個錯誤信息我沒有及時記錄,日后補上。這個錯誤在網上可以輕易地搜索到解決方案,即在dwr.jar中刪除掉org.directwebremoting.annotations.AnnotationsConfigurator這個類再重新部署就可以了。
同樣的問題出現在hibernate3中,hibernate3對annotations的支持在JDK1.4中也可能出現異常,解決的辦法就是在部署文件中刪除掉hibernate-annotations.jar就可以了。
另外值得說明的是,以上2個問題并不是每次都會出現的。它們就如同幽靈一樣有時出現有時不出現,因此我們并不需要總是刪除這個類和jar包,只是在發生錯誤時才刪除。