使用JSON進行資料傳輸的總結
四、Struts 2 中使用Json ajax 支援
JSON 外掛提供了一種名為json 的ResultType ,一旦為某個Action 指定了一個型別為json 的Result ,則該Result 無需對映到任何檢視資源。因為JSON 外掛會負責將Action 裡的狀態資訊序列化成JSON 格式的資料,並將該資料返回給客戶端頁面的 JavaScript 。
簡單地說,JSON 外掛允許我們在JavaScript 中非同步呼叫Action ,而且Action 不再需要使用檢視資源來顯示該Action 裡的狀態資訊,而是由JSON 外掛負責將Action 裡的狀態資訊返回給呼叫頁面——通過這種方式,就可以完成Ajax
Struts2 提供了一種可插拔方式來管理外掛,安裝Struts2 的JSON 外掛與安裝普通外掛並沒有太大的區別,一樣只需要將Struts2 外掛的JAR 檔案複製到Web 應用的WEB-INF/lib 路徑下即可。
安裝JSON 外掛按如下步驟進行:
(1 )登陸http://code.google.com/p/jsonplugin/downloads/list 站點,下載Struts2 的JSON 外掛的最新版本,當前最新版本是0.7 ,我們可以下載該版本的JSON 外掛。
(2 )將下載到的jsonplugin-0.7.jar 檔案複製到Web 應用的WEB-INF 路徑下,即可完成JSON
實現Action 邏輯
假設deptnew.html 輸入頁面中包含了四個表單域,這四個表單域對於四個請求引數,因此應該使用Action 來的dept 物件封裝這四個請求引數。四個表單域的name 分別為dept.deptid 、dept.deptname 、dept.deptnum 和dept.deptdesc 。
處理該請求的Action 類程式碼如下:
package org.wllt.ajax.actions;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.wllt.ajax.beans.Dept;
import com.googlecode.jsonplugin.annotations.JSON;
import com.opensymphony.xwork2.Action;
public class JSONExample
{
// 封裝請求引數的三個屬性
private String field1;
private transient String field2;
private String field3;
// 封裝處理結果的屬性
private int[] ints ={10, 20};
private Map map = new HashMap();
private String customName = "custom";
// 日期格式的屬性
private Date date;
private Dept dept;
// 三個請求引數對應的 setter 和 getter 方法
@JSON(serialize=false)
public String getField1(){
return field1;
}
public void setField1(String field1)
{
this.field1 = field1;
}
// 封裝處理結果的屬性的 setter 和 getter 方法
public int[] getInts()
{
return ints;
}
public void setInts(int[] ints)
{
this.ints = ints;
}
@JSON(format="yyyy-MM-dd")
public Map getMap()
{
return map;
}
public void setMap(Map map)
{
this.map = map;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
// 使用註釋語法來改變該屬性序列化後的屬性名
@JSON(name="newName")
public String getCustomName()
{
return this.customName;
}
@JSON(format="yyyy-MM-dd")
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String execute()
{
map.put("name", "yeeku");
map.put("curdate",new Date());
return Action.SUCCESS;
}
}
在上面程式碼中,使用了JSON 註釋,註釋時指定了name 域,name 域指定Action 屬性被序列化成JSON 物件的屬性名。除此之外,JSON 註釋還支援如下幾個域:
Name : Action 屬性被序列化成JSON 物件的屬性名
serialize :設定是否序列化該屬性( 預設的action 的所有屬性都會序列化成json 文字響應到原頁面,也可以根據需要設定某些屬性不序列化 ) 。也可以通過在配置json 型別的結果的時候定義需要不序列化的屬性:
<result name="success" type="json">
<param name="excludeProperties">service,joindate,dept</param>
</result>
deserialize :設定是否反序列化該屬性。
format :設定用於格式化輸出、解析日期表單域的格式。例如"yyyy-MM-dd'T'HH:mm:ss" 。
一般需要註釋到需要轉換日期格式屬性的get 方法上面
配置該Action 與配置普通Action 存在小小的區別,為該Action 配置結果型別為json 的Result 。而這個Result 無需配置任何檢視資源, 只需要把通過該結果把json 文字響應到原頁面。
配置該Action 的struts.xml 檔案程式碼如下:
<? xml version = "1.0" encoding = "UTF-8" ?>
<! DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd" >
< struts >
< constant name = "struts.locale" value = "zh_CN" />
< constant name = "struts.action.extension" value = "do,action" />
< package name = "json" extends = "json-default" >
< action name = "dept" class = "org.wllt.ajax.actions.DeptAction" >
< result type = "json" />
</ action >
</ package >
</ struts >
在上面配置檔案中有兩個值得注意的地方:
第一個地方是配置struts.i18n.encoding 常量時,不再是使用GBK 編碼,而是UTF-8 編碼,這是因為Ajax 的POST 請求都是以UTF-8 的方式進行編碼的。
第二個地方是配置包時,自己的包繼承了json-default 包,而不再繼承預設的default 包,這是因為只有在該包下才有json 型別的Result 。
實現JSP 頁面first.jsp :
為了簡單地訪問DOM 節點,這裡用了JavaScript 框架Prototype.js.
< html >
< head >
< title > 使用 JSON 外掛 </ title >
< meta http-equiv = "content-type" content = "text/html; charset=UTF-8" >
< script src = "./js/prototype.js" type = "text/javascript" >
</ script >
< script language = "JavaScript" >
function gotClick()
{
// 請求的地址
var url= './dept.do' ;
// 將 Form 中所有 Input 物件的值轉化為一個 URL String ,方便把 Form 轉為用 URL Get 方式 // 的 Ajax提交,常用 Ajax 提交 Form 的例子 :
var params=Form.serialize( 'form1' );
// 建立 Ajax.Request 物件,對應於傳送請求
var myAjax= new Ajax.Request(url, {
// 請求方式: POST
method: 'post' ,
// 請求引數
parameters:params,
// 指定回撥函式
onComplete: processResponse,
// 是否非同步傳送請求
asynchronous: true
} );
}
function processResponse(request)
{
$( "show" ).innerHTML=request.responseText;
}
</ script >
</ head >
< body >
< form id = "form1" name = "form1" method = "post" >
部門編號 < INPUT TYPE = "text" name = "dept.deptid" id = "deptid" />< br >
部門名稱 < INPUT TYPE = "text" name = "dept.deptname" id = "deptname" />< br >
部門編制 < INPUT TYPE = "text" name = "dept.deptnum" id = "deptnum" />< br >
部門描述 < INPUT TYPE = "text" name = "dept.deptdesc" id = "deptdesc" />< br >
< INPUT TYPE = "button" value = " 提交 " onClick = "gotClick();" />
</ form >
< div id = "show" >
</ div >
</ body >
</ html >
五、當json 碰到hibernate 延時載入
在開發Struts2.0+hibernate3.2+spring2.0 專案過程中,遇到了 failed to lazily initialize a collection of role: org.wllt.www.pojo.Dept.emps, no session or session was closed 這個異常的出現,通過以下方法可以解決這個問題:
1 、是把對應一對多的那兩個列lazy=true 改為lazy =false 即可;
2 、對於查詢中如果用的是xxx.load (class ,id )則改為xxx,get(class ,id) ;
3 、在web.xml 檔案中加入:
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>false</param-value>
</init-param>
<!-- 這個 -- <init-param> 一定要加不然很可能會報錯 : org.springframework.dao.InvalidDataAccessApiUsageException:Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition
-->
</filter>
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.wllt</url-pattern>
</filter-mapping>
對以上方法進行一一測試,到後來結果都是一樣,出現同樣的異常,其實spring 能很好地解決這個問題,Spring 框架為Hibernate 延遲載入與DAO 模式的整合提供了一種方便的解決方法。以一個Web 應用為例,Spring 提供了OpenSessionInViewFilter 和OpenSessionInViewInterceptor 。我們可以隨意選擇一個類來實現相同的功能。兩種方法唯一的不同就在於interceptor 在Spring