Fenriswolf 程式筆記

奮利斯狼的地盤,小綿羊勿入

DWR – 與 Spring 和 Hibernate 整合

這篇是 dwr+spring+hibernate 整合的文章
用一個比較複雜的資料結構介紹如何傳接值
本篇重點在 dwr,並沒有從 hibernate 到 dwr step by step 的教學
故 spring 及 hibernate 的設定並無著墨
只是在這裡提出幾個整合的方法
完整範例請參考程式下載區

先建立一個簡單的 table 結構

Order 跟 OrderDetail 是一對多的關係
Order 跟 OrderStatus 是多對一的關係
3 個 tables 的 DDL 會放在程式下載區

1. 設定 dwr.xml
在 spring 及 hibernate 都設定好之後接下來就是設定 dwr.xml

<dwr>
    <allow>
        <create creator="spring" javascript="orderService"
            scope="application">
            <param name="beanName" value="orderService" />
        </create>

        <convert converter="exception" match="java.lang.Exception" />
        <convert converter="bean" match="java.lang.StackTraceElement" />

        <convert converter="hibernate3" match="com.fw.dwr.*" />
    </allow>
</dwr>

create tag 介紹 :
creator : 設為 “spring" 表示 object 由 spring 提供而不是 dwr 自行 new
javascript : javascript call 後端 service 所用到的名稱,dwr 會用這個值產生 orderService.js 的檔案供 web 端使用
scope : bean 存在 web 端的生命週期,有 “application"、"session"、"request" 可設定,預設為 “page"
param : beanName 的值是 spring 內所定義的 bean id 而不是 full class name

converter tag 介紹 :
converter : 比較常用的是 “bean",主要 match 對象是 value object,這邊為了在 web 端可以顯示錯誤訊息故使用 exception converter 做轉換
match : 決定哪些 classes 使用這個 converter,可用 “*" 表示某 package 之下的所有 classes 都要轉換,就不用每個 class 都做設定

在這個例子裡比較重要的地方是 hibernate 所用到的 classes 需設 hibernate3 converter(舊版 hibernate 可改用 hibernate2 converter)
一般用 bean converter 的壞處是 :
a. 以這個範例而言,dwr 轉換的時間點 hibernate session 已經關閉
所以 dwr call order.getOrderDetails() method 會有 session is closed 的錯誤訊息
就算用 OpenSessionInViewFilter 也容易發生 b 的問題
b. dwr 會轉換 order object 內的所有 attributes,包括 collection attributes
作用跟 collection lazy 設 false 效果是一樣的
在 call orderService.getOrderList() method 時會有嚴重的效能問題

hibernate3 converter 則不會處理任何 lazy init 的 attributes
因此何時要 init collection data 需自行 call Hibernate.initialize() 來決定
以下是取得單一 order object 的範例

public Order getOrder(int orderId) {
    Order order = orderDao.getOrder(orderId);
    Hibernate.initialize(order.getOrderDetails());
    
    return order;
}

2. 設定 web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:appContext_core.xml,
        classpath:appContext_hibernate.xml,
        classpath:appContext_dwr.xml
    </param-value>
</context-param>

<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

<servlet>
    <servlet-name>dwr</servlet-name>
    <servlet-class>
        org.directwebremoting.servlet.DwrServlet
    </servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dwr</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

設定蠻簡單的,不過在正式環境最好把 debug 的參數拿掉

3. web 端的傳接值問題
在 jsp 使用 dwr 的方式很簡單,只要加上以下程式碼就好了

<script type='text/javascript'
    src='<%=request.getContextPath()%>/dwr/interface/orderService.js'></script>
<script type='text/javascript'
    src='<%=request.getContextPath()%>/dwr/engine.js'></script>
<script type='text/javascript'
    src='<%=request.getContextPath()%>/dwr/util.js'></script>

engine.js 及 util.js 是 dwr 預設提供的 javascript function
orderService.js 則是動態產生的,命名規則上方已有說明

用 hibernate 所建立的 classes 結構都會比較複雜
例如 Order class 裡有 nested attribute – OrderStatus
是否能用 dwr.util.setValues() 及 dwr.util.getValues() 來傳接值
答案是可以的
a. 先定義 javascript objects,順序一定要對
Order class 會用到 OrderStatus class,故 OrderStatus class 要先定義

OrderStatus = function() {};
OrderStatus.prototype = {
    statusId: null,
    statusName: null
};

Order = function() {};
Order.prototype = {
    orderId: null,
    orderName: null,
    updateDt: null,

    orderStatus: new OrderStatus(),
    orderDetails: []
};

OrderDetail = function() {};
OrderDetail.prototype = {
    detailId: null,
    orderId: null,
    detailName: null,
    updateDt: null
};

b. html 裡 nested attribute 的 id 定義為 orderStatus.statusId,有多層時只要以逗點分隔即可

<input id="orderName" type="text" size="20" />
<select id="orderStatus.statusId">
    <option value="1">Draft</option>
    <option value="2">Submitted</option>
    <option value="3">Closed</option>
</select>

c. 接下來就可以用 dwr 提供的 method 輕鬆取值

var order = new Order();
dwr.util.getValues(order);
orderService.updateOrder(order);

以 java code 的方式來看 dwr 會分別 call order.setOrderName() 及 order.getOrderStatus().setStatusId()

4. 執行
deploy dwrweb.war 到 tomcat 之後用 http://localhost:8080/dwrweb 就可以看到結果

 
 
執行環境
JDK 1.5.0_06
tomcat 6.0.10
spring 2.5
hibernate 3.2.5
dwr 2.0.5

參考資料
DWR

程式下載
tableschema.sql
dwrweb.war

廣告

2012/03/21 - Posted by | Java Tool | , ,

仍無迴響。

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

%d 位部落客按了讚: