<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    176142998

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      116 Posts :: 0 Stories :: 45 Comments :: 0 Trackbacks

    #

     

    posted @ 2008-08-18 17:51 飛飛 閱讀(11316) | 評(píng)論 (3)編輯 收藏

    2.調(diào)用有簡(jiǎn)單返回值的java方法
    2.1、dwr.xml的配置
    配置同1.1
    <dwr>
    <allow>
    <create creator="new" javascript="testClass" >
    <param name="class" value="com.dwr.TestClass" />
    <include method="testMethod2"/>
    </create>
    </allow>
    </dwr>
    2.2、javascript中調(diào)用
    首先,引入javascript腳本
    其次,編寫調(diào)用java方法的javascript函數(shù)和接收返回值的回調(diào)函數(shù)
    Function callTestMethod2(){
    testClass.testMethod2(callBackFortestMethod2);
    }
    Function callBackFortestMethod2(data){
    //其中date接收方法的返回值
    //可以在這里對(duì)返回值進(jìn)行處理和顯示等等
    alert("the return value is " + data);
    }
    其中callBackFortestMethod2是接收返回值的回調(diào)函數(shù)


    3、調(diào)用有簡(jiǎn)單參數(shù)的java方法
    3.1、dwr.xml的配置
    配置同1.1
    <dwr>
    <allow>
    <create creator="new" javascript="testClass" >
    <param name="class" value="com.dwr.TestClass" />
    <include method="testMethod3"/>
    </create>
    </allow>
    </dwr>
    3.2、javascript中調(diào)用
    首先,引入javascript腳本
    其次,編寫調(diào)用java方法的javascript函數(shù)
    Function callTestMethod3(){
    //定義要傳到j(luò)ava方法中的參數(shù)
    var data;
    //構(gòu)造參數(shù)
    data = “test String”;
    testClass.testMethod3(data);
    }


    4、調(diào)用返回JavaBean的java方法
    4.1、dwr.xml的配置
    <dwr>
    <allow>
    <create creator="new" javascript="testClass" >
    <param name="class" value="com.dwr.TestClass" />
    <include method="testMethod4"/>
    </create>
    <convert converter="bean" match=""com.dwr.TestBean">
    <param name="include" value="username,password" />
    </convert>
    </allow>
    </dwr>
    <creator>標(biāo)簽負(fù)責(zé)公開用于Web遠(yuǎn)程的類和類的方法,<convertor>標(biāo)簽則負(fù)責(zé)這些方法的參數(shù)和返回類型。convert元素的作用是告訴DWR在服務(wù)器端Java 對(duì)象表示和序列化的JavaScript之間如何轉(zhuǎn)換數(shù)據(jù)類型。DWR自動(dòng)地在Java和JavaScript表示之間調(diào)整簡(jiǎn)單數(shù)據(jù)類型。這些類型包括Java原生類型和它們各自的封裝類表示,還有String、Date、數(shù)組和集合類型。DWR也能把JavaBean轉(zhuǎn)換成JavaScript 表示,但是出于安全性的原因,要求顯式的配置,<convertor>標(biāo)簽就是完成此功能的。converter="bean"屬性指定轉(zhuǎn)換的方式采用JavaBean命名規(guī)范,match=""com.dwr.TestBean"屬性指定要轉(zhuǎn)換的javabean名稱,<param>標(biāo)簽指定要轉(zhuǎn)換的JavaBean屬性。
    4.2、javascript中調(diào)用
    首先,引入javascript腳本
    其次,編寫調(diào)用java方法的javascript函數(shù)和接收返回值的回調(diào)函數(shù)
    Function callTestMethod4(){
    testClass.testMethod4(callBackFortestMethod4);
    }
    Function callBackFortestMethod4(data){
    //其中date接收方法的返回值
    //對(duì)于JavaBean返回值,有兩種方式處理
    //不知道屬性名稱時(shí),使用如下方法
    for(var property in data){
    alert("property:"+property);
    alert(property+":"+data[property]);
    }
    //知道屬性名稱時(shí),使用如下方法
    alert(data.username);
    alert(data.password);
    }
    其中callBackFortestMethod4是接收返回值的回調(diào)函數(shù)

    5、調(diào)用有JavaBean參數(shù)的java方法
    5.1、dwr.xml的配置
    配置同4.1
    <dwr>
    <allow>
    <create creator="new" javascript="testClass" >
    <param name="class" value="com.dwr.TestClass" />
    <include method="testMethod5"/>
    </create>
    <convert converter="bean" match="com.dwr.TestBean">
    <param name="include" value="username,password" />
    </convert>
    </allow>
    </dwr>
    5.2、javascript中調(diào)用
    首先,引入javascript腳本
    其次,編寫調(diào)用java方法的javascript函數(shù)
    Function callTestMethod5(){
    //定義要傳到j(luò)ava方法中的參數(shù)
    var data;
    //構(gòu)造參數(shù),date實(shí)際上是一個(gè)object
    data = { username:"user", password:"password" }
    testClass.testMethod5(data);
    }


    6、調(diào)用返回List、Set或者M(jìn)ap的java方法
    6.1、dwr.xml的配置
    配置同4.1
    <dwr>
    <allow>
    <create creator="new" javascript="testClass" >
    <param name="class" value="com.dwr.TestClass" />
    <include method="testMethod6"/>
    </create>
    <convert converter="bean" match="com.dwr.TestBean">
    <param name="include" value="username,password" />
    </convert>
    </allow>
    </dwr>
    注意:如果List、Set或者M(jìn)ap中的元素均為簡(jiǎn)單類型(包括其封裝類)或String、Date、數(shù)組和集合類型,則不需要<convert>標(biāo)簽。
    6.2、javascript中調(diào)用(以返回List為例,List的元素為TestBean)
    首先,引入javascript腳本
    其次,編寫調(diào)用java方法的javascript函數(shù)和接收返回值的回調(diào)函數(shù)
    Function callTestMethod6(){
    testClass.testMethod6(callBackFortestMethod6);
    }
    Function callBackFortestMethod6(data){
    //其中date接收方法的返回值
    //對(duì)于JavaBean返回值,有兩種方式處理
    //不知道屬性名稱時(shí),使用如下方法
    for(var i=0;i<data.length;i++){
    for(var property in data){
    alert("property:"+property);
    alert(property+":"+data[property]);
    }
    }
    //知道屬性名稱時(shí),使用如下方法
    for(var i=0;i<data.length;i++){
    alert(data[i].username);
    alert(data[i].password);
    }
    }


    7、調(diào)用有List、Set或者M(jìn)ap參數(shù)的java方法
    7.1、dwr.xml的配置
    <dwr>
    <allow>
    <create creator="new" javascript="testClass" >
    <param name="class" value="com.dwr.TestClass" />
    <include method="testMethod7"/>
    </create>
    <convert converter="bean" match="com.dwr.TestBean">
    <param name="include" value="username,password" />
    </convert>
    </allow>
    <signatures>
    <![CDATA[
    import java.util.List;
    import com.dwr.TestClass;
    import com.dwr.TestBean;
    TestClass.testMethod7(List<TestBean>);
    ]]>
    </signatures>
    </dwr>
    <signatures>標(biāo)簽是用來聲明java方法中List、Set或者M(jìn)ap參數(shù)所包含的確切類,以便java代碼作出判斷。
    7.2、javascript中調(diào)用(以返回List為例,List的元素為TestBean)
    首先,引入javascript腳本
    其次,編寫調(diào)用java方法的javascript函數(shù)
    Function callTestMethod7(){
    //定義要傳到j(luò)ava方法中的參數(shù)
    var data;
    //構(gòu)造參數(shù),date實(shí)際上是一個(gè)object數(shù)組,即數(shù)組的每個(gè)元素均為object
    data = [
    {
    username:"user1",
    password:"password2"
    },
    {
    username:"user2",
    password:" password2"
    }
    ];
    testClass.testMethod7(data);
    }

    注意:
    1、對(duì)于第6種情況,如果java方法的返回值為Map,則在接收該返回值的javascript回調(diào)函數(shù)中如下處理:
    function callBackFortestMethod(data){
    //其中date接收方法的返回值
    for(var property in data){
    var bean = data[property];
    alert(bean.username);
    alert(bean.password);
    }
    }
    2、對(duì)于第7種情況,如果java的方法的參數(shù)為Map(假設(shè)其key為String,value為TestBean),則在調(diào)用該方法的javascript函數(shù)中用如下方法構(gòu)造要傳遞的參數(shù):
    function callTestMethod (){
    //定義要傳到j(luò)ava方法中的參數(shù)
    var data;
    //構(gòu)造參數(shù),date實(shí)際上是一個(gè)object,其屬性名為Map的key,屬性值為Map的value
    data = {
    "key1":{
    username:"user1",
    password:"password2"
    },
    "key2":{
    username:"user2",
    password:" password2"
    }
    };
    testClass.testMethod(data);
    }
    并且在dwr.xml中增加如下的配置段
    <signatures>
    <![CDATA[
    import java.util.List;
    import com.dwr.TestClass;
    import com.dwr.TestBean;
    TestClass.testMethod7(Map<String,TestBean>);
    ]]>
    </signatures>
    3、由以上可以發(fā)現(xiàn),對(duì)于java方法的返回值為L(zhǎng)ist(Set)的情況,DWR將其轉(zhuǎn)化為Object數(shù)組,傳遞個(gè)javascript;對(duì)于java方法的返回值為Map的情況,DWR將其轉(zhuǎn)化為一個(gè)Object,其中Object的屬性為原Map的key值,屬性值為原Map相應(yīng)的value值。
    4、如果java方法的參數(shù)為L(zhǎng)ist(Set)和Map的情況,javascript中也要根據(jù)3種所說,構(gòu)造相應(yīng)的javascript數(shù)據(jù)來傳遞到j(luò)ava中。
    posted @ 2008-08-15 14:56 飛飛 閱讀(388) | 評(píng)論 (0)編輯 收藏

    FreeMarker的指令的文件就稱為模板(Template)。
    模板設(shè)計(jì)者不關(guān)心數(shù)據(jù)從那兒來,只知道使用已經(jīng)建立的數(shù)據(jù)模型。
    數(shù)據(jù)模型由程序員編程來創(chuàng)建,向模板提供變化的信息,這些信息來自于數(shù)據(jù)庫、文件,甚至于在程序中直接生成。

    數(shù)據(jù)類型:

    一、基本:
    1、scalars:存儲(chǔ)單值

    字符串:簡(jiǎn)單文本由單或雙引號(hào)括起來。
    數(shù)字:直接使用數(shù)值。
    日期:通常從數(shù)據(jù)模型獲得
    布爾值:true或false,通常在<#if …>標(biāo)記中使用

    2、hashes:充當(dāng)其它對(duì)象的容器,每個(gè)都關(guān)聯(lián)一個(gè)唯一的查詢名字

    具有一個(gè)唯一的查詢名字和他包含的每個(gè)變量相關(guān)聯(lián)。

    3、sequences:充當(dāng)其它對(duì)象的容器,按次序訪問

    使用數(shù)字和他包含的每個(gè)變量相關(guān)聯(lián)。索引值從0開始。

    4、集合變量:

    除了無法訪問它的大小和不能使用索引來獲得它的子變量:集合可以看作只能由<#list...>指令使用的受限sequences。

    5、方法:通過傳遞的參數(shù)進(jìn)行計(jì)算,以新對(duì)象返回結(jié)果

    方法變量通常是基于給出的參數(shù)計(jì)算值在數(shù)據(jù)模型中定義。

    6、用戶自定義FTL指令:宏和變換器

    7、節(jié)點(diǎn)

    節(jié)點(diǎn)變量表示為樹型結(jié)構(gòu)中的一個(gè)節(jié)點(diǎn),通常在XML處理中使用。

    模板:

    使用FTL(freeMarker模板語言)編寫

    組成部分

    一、整體結(jié)構(gòu)

    1、注釋:<#--注釋內(nèi)容-->,不會(huì)輸出。

    2、文本:直接輸出。

    3、interpolation:由 ${var} 或 #{var} 限定,由計(jì)算值代替輸出。

    4、FTL標(biāo)記

    二、指令:
    freemarker指令有兩種:
    1、預(yù)定義指令:引用方式為<#指令名稱>
    2、用戶定義指令:引用方式為<@指令名稱>,引用用戶定義指令時(shí)須將#換為@。
    注意:如果使用不存在的指令,FreeMarker不會(huì)使用模板輸出,而是產(chǎn)生一個(gè)錯(cuò)誤消息。

    freemarker指令由FTL標(biāo)記來引用,F(xiàn)TL標(biāo)記和HTML標(biāo)記類似,名字前加#來加以區(qū)分。如HTML標(biāo)記的形式為<h1></h1>則FTL標(biāo)記的形式是<#list></#list>(此處h1標(biāo)記和list指令沒有任何功能上的對(duì)應(yīng)關(guān)系,只是做為說明使用一下)。

    有三種FTL標(biāo)記:
    1)、開始標(biāo)記:<#指令名稱>
    2)、結(jié)束標(biāo)記:</#指令名稱>
    3)、空標(biāo)記:<#指令名稱/>

    注意:

    1) FTL會(huì)忽略標(biāo)記之中的空格,但是,<#和指令 與 </#和指令 之間不能有空格。
    2) FTL標(biāo)記不能夠交叉,必須合理嵌套。每個(gè)開始標(biāo)記對(duì)應(yīng)一個(gè)結(jié)束標(biāo)記,層層嵌套。 如:
    <#list>
    <li>
    ${數(shù)據(jù)}
    <#if 變量>
    <p>game over!</p>
    </#if>
    </li>
    </#list>

    注意事項(xiàng):
    1)、FTL對(duì)大小寫敏感。所以使用的標(biāo)記及interpolation要注意大小寫。name與NAME就是不同的對(duì)象。<#list>是正確的標(biāo)記,而<#List>則不是。
    2)、interpolation只能在文本部分使用,不能位于FTL標(biāo)記內(nèi)。如<#if ${var}>是錯(cuò)誤的,正確的方法是:<#if var>,而且此處var必須為布爾值。
    3)、FTL標(biāo)記不能位于另一個(gè)FTL標(biāo)記內(nèi)部,注釋例外。注釋可以位于標(biāo)記及interpolation內(nèi)部。


    三、表達(dá)式

    1、直接指定值:

    1-1、字符串:
    由雙引號(hào)或單引號(hào)括起來的字符串,其中的特殊字符(如' " \等)需要轉(zhuǎn)義。


    1-2、raw字符串:
    有一種特殊的字符串稱為raw字符串,被認(rèn)為是純文本,其中的\和{等不具有特殊含義,該類字符串在引號(hào)前面加r,下面是一個(gè)例子:
    ${r"/${data}"year""}屏幕輸出結(jié)果為:/${data}"year"


    轉(zhuǎn)義 含義
    序列

    \" 雙引號(hào)(u0022)

    \' 單引號(hào)(u0027)

    \\ 反斜杠(u005C)

    \n 換行(u000A)

    \r Return (u000D)

    \t Tab (u0009)

    \b Backspace (u0008)

    \f Form feed (u000C)

    \l <

    \g >

    \a &

    \{ {

    \xCode 4位16進(jìn)制Unicode代碼

    1-3、數(shù)字:直接輸入,不需要引號(hào)

    1)、精度數(shù)字使用“.”分隔,不能使用分組符號(hào)
    2)、目前版本不支持科學(xué)計(jì)數(shù)法,所以“1E3”是錯(cuò)誤的
    3)、不能省略小數(shù)點(diǎn)前面的0,所以“.5”是錯(cuò)誤的
    4)、數(shù)字8、+8、08和8.00都是相同的

    1-4、布爾值:true和false,不使用引號(hào)

    1-5、序列:由逗號(hào)分隔的子變量列表,由[]方括號(hào)限定。
    1)、子變量列表可以是表達(dá)式
    2)、可以使用數(shù)字范圍定義數(shù)字序列,<b>不需要方括號(hào)限定</b>,例如2..5等同于[2, 3, 4, 5],但是更有效率,可以定義反遞增范圍如:5..2。

    1-6、散列(hash)
    1)、由逗號(hào)分隔的鍵/值列表,由{}大括號(hào)限定,鍵和值之間用冒號(hào)分隔,如:{"key1":valu1,"key2":"character string"....}
    2)、鍵和值都是表達(dá)式,但是鍵必須是字符串。

    2、獲取變量:

    2-1、頂層變量:${變量名}

    變量名只能是字母、數(shù)字、下劃線、$、#、@ 的組合,且不能以數(shù)字開頭。

    2-2、散列:有兩種方法

    1)、點(diǎn)語法:變量名字和頂層變量的名字受同樣的限制
    2)、方括號(hào)語法:變量名字無限制,可以是任意的表達(dá)式的結(jié)果
    book.author.name
    book.author.["name"]
    book["author"].name
    book["author"]["name"]
    以上是等價(jià)的。

    2-3、序列:使用散列的方括號(hào)語法獲取變量,方括號(hào)中的表達(dá)式結(jié)果必須為數(shù)字。注意:第一個(gè)項(xiàng)目的索引為0。可以使用
    [startindex..endindex]語法獲取序列片段。

    2-4、特殊變量:FreeMarker內(nèi)定義變量,使用.variablename語法訪問。

    3、字符串操作

    3-1、interpolation:使用${}或#{}在文本部分插入表達(dá)式的值,例如:

    ${"hello${username}!"}
    ${"${username}${username}${username}"}

    也可以使用+來獲得同樣的結(jié)果:
    ${"hello"+username+"!"}
    ${username+username+username}

    注意:${}只能用于文本部分而不能出現(xiàn)于標(biāo)記內(nèi)。

    <#if ${user.login}>或<#if "${user.login}">都是錯(cuò)誤的;
    <#if user.login>是正確的。
    本例中user.login的值必須是布爾類型。

    3-2、子串:
    舉例說明:假如user的值為"Big Joe"
    ${user[0]}${user[4]}結(jié)果是:BJ
    ${user[1..4]}結(jié)果是:ig J

    4、序列操作

    4-1、連接操作:可以使用+來操作,例如:
    ["title","author"]+["month","day"]

    5、散列操作
    5-1、連接操作:可以使用+來操作,如果有相同的KEY,則右邊的值會(huì)替代左邊的值,例如:
    {"title":散列,"author":"emma"}+{"month":5,"day":5}+{"month":6}結(jié)果month的值就是6。

    6、算術(shù)運(yùn)算

    6-1、操作符:+、-、*、/、%
    除+號(hào)以外的其他操作符兩邊的數(shù)據(jù),必須都是數(shù)字類型。
    如果+號(hào)操作符一邊有一個(gè)字符型數(shù)據(jù),會(huì)自動(dòng)將另一邊的數(shù)據(jù)轉(zhuǎn)換為字符型數(shù)據(jù),運(yùn)算結(jié)果為字符型數(shù)據(jù)。

    6-2、比較操作符:
    1)、=
    2)、==
    3)、!=
    4)、<
    5)、<=
    6)、>
    7)、>=
    1-3的操作符,兩邊的數(shù)據(jù)類型必須相同,否則會(huì)產(chǎn)生錯(cuò)誤
    4-7的操作符,對(duì)于日期和數(shù)字可以使用,字符串不可以使用。

    注意:
    1)、FreeMarker是精確比較,所以"x" "x " "X"是不等的。
    2)、因?yàn)?lt;和>對(duì)FTL來說是開始和結(jié)束標(biāo)記,所以,可以用兩種方法來避免這種情況:
    一種是使用括號(hào)<#if (a<b)>
    另一是使用替代輸出,對(duì)應(yīng)如下:
    < lt
    <= lte
    > gt
    >= gte

    6-3、邏輯操作符:只能用于布爾值,否則會(huì)出現(xiàn)錯(cuò)誤。

    &&(and)與運(yùn)算
    ||(or)或運(yùn)算
    !(not)非運(yùn)算

    6-4、內(nèi)建函數(shù):使用方法類似于訪問散列的子變量,只是使用?代替.例如:${test?upper_case?html}

    常用的內(nèi)建函數(shù)列舉如下:

    1)、字符串使用:

    html:對(duì)字符串進(jìn)行HTML編碼
    cap_first:字符串第一個(gè)字母大寫
    lower_first:字符串第一個(gè)字母小寫
    upper_case:將字符串轉(zhuǎn)換成大寫
    trim:去年字符前后的空白字符

    2)、序列使用:
    size:獲得序列中元素的數(shù)目

    3)、數(shù)字使用:
    int:取得數(shù)字的整數(shù)部分

    7、操作符的優(yōu)先順序:

    后綴:[subbarName][subStringRange].(mathodParams)
    一元:+expr、-expr、! (not)
    內(nèi)建:?
    乘法:*、/、%
    加法:+、-
    關(guān)系:<、<=、>、>= (lt、lte、gt、gte)
    相等:=、==、!=
    邏輯與:&& (and)
    邏輯或:|| (or)
    數(shù)字范圍:..

    四、interpolation

    inperpolation只能用于文本,有兩種類型:通用interpolation及數(shù)字interpolation

    1、通用interpolation

    如${expr}

    1-1、插入字符串值:直接輸出表達(dá)式結(jié)果。
    1-2、插入數(shù)字值:根據(jù)缺省格式(由setting指令設(shè)置)將表達(dá)式結(jié)果轉(zhuǎn)換成文本輸出;可以使用內(nèi)建函數(shù)string來格式化單個(gè)interpolation

    如:
    <#setting number_format="currency" />
    <#assign answer=42 />
    ${answer} <#-- ¥42.00 -->
    ${answer?string} <#-- ¥42.00 -->
    ${answer?string.number} <#-- 42 -->
    ${answer?string.currency} <#-- ¥42.00 -->
    ${answer?string.percent} <#-- 42,00% -->

    1-3、插入日期值:根據(jù)缺省格式(由setting指令設(shè)置)將表達(dá)式結(jié)果轉(zhuǎn)換成文本輸出;可以使用內(nèi)建函數(shù)string來格式化單個(gè)interpolation

    如:

    ${lastupdata?string("yyyy-MM-dd HH:mm:ss zzzz")} <#-- 2003-04-08 21:24:44 Pacific Daylight Time -->
    ${lastupdata?string("EEE,MMM d, ''yy")} <#-- tue,Apr 8, '03 -->
    ${lastupdata?string("EEEE,MMMM dd, yyyy,hh:mm:ss a '('zzz')'")} <#-- Tuesday,April 08, 2003, 09:24:44 PM (PDT)-->

    1-4、插入布爾值:根據(jù)缺省格式(由setting指令設(shè)置)將表達(dá)式結(jié)果轉(zhuǎn)換成文本輸出;可以使用內(nèi)建函數(shù)string來格式化單個(gè)interpolation

    如:
    <#assign foo=ture />
    ${foo?string("yes","no")} <#-- yes -->

    2、數(shù)字interpolation:

    有兩種形式:
    1)、#{expr}
    2)、#{expr;format}:format可以用來格式化數(shù)字,format可以是如下:
    mX:小數(shù)部分最小X位
    MX:小數(shù)部分最大X位

    例如:
    <#assign x=2.582 />
    <#assign y=4 />
    #{x;M2} <#-- 2.58 -->
    #{y;M2} <#-- 4 -->
    #{x;m1} <#-- 2.582 -->
    #{y;m1} <#-- 4.0 -->
    #{x;m1M2} <#-- 2.58 -->
    #{y;m1M2} <#-- 4.0 -->


    雜項(xiàng)

    一、用戶定義指令

    宏和變換器變量是兩種不同類型的用戶自定義指令,他們的區(qū)別是:

    宏可以在模板中用macro指令來定義
    變換器是在模板外由程序定義

    1、宏:和某個(gè)變量關(guān)聯(lián)的模板片段,以便在模板中通過用戶自定義指令使用該變量
    1-1、基本用法:
    例如:
    <#macro greet>
    <font size="+2"> Hello JOE!</font>
    </#macro>


    使用時(shí):
    <@greet></@greet>
    如果沒有體內(nèi)容也可以用
    <@greet />

    1-2、變量:

    1)、可以在宏定義之后定義參數(shù),宏參數(shù)是局部變量,只在宏定義中有效。如:

    <#macro greet person>
    <font size="+2"> Hello ${person}!</font>
    </#macro>
    使用時(shí):
    <@greet person="emma"> and <@greet person="LEO">
    輸出為:
    <font size="+2"> Hello emma!</font>
    <font size="+2"> Hello LEO!</font>

    注意:宏的參數(shù)是FTL表達(dá)式,所以,person=emma和上面的例子中具有不同的意義,這意味著將變量emma的值傳給person,這個(gè)值可能是任意一種數(shù)據(jù)類型,甚至是一個(gè)復(fù)雜的表達(dá)式。


    宏可以有多個(gè)參數(shù),使用時(shí)參數(shù)的次序是無關(guān)的,但是只能使用宏中定義的參數(shù),并且對(duì)所有參數(shù)賦值。如:
    <#macro greet person color>
    <font size="+2" color="${color}"> Hello ${person}!</font>
    </#macro>

    使用時(shí):
    <@greet color="black" person="emma" />正確
    <@greet person="emma" />錯(cuò)誤,color沒有賦值,此時(shí),如果在定義宏時(shí)為color定義缺省值<#macro greet person color="black">這樣的話,這個(gè)使用方法就是正確的。
    <@greet color="black" person="emma" bgcolor="yellow" />錯(cuò)誤,宏greet定義中未指定bgcolor這個(gè)參數(shù)



    2、嵌套內(nèi)容:

    2-1、自定義指令可以有嵌套內(nèi)容,使用<#nested>指令,執(zhí)行自定義指令開始和結(jié)束標(biāo)記之間的模板片段。例如:
    <#macro greet>
    <p>
    <#nested>
    </p>
    </#macro>


    <@greet>hello Emma!</@greet>

    輸出為
    <p>hello Emma!</p>

    2-2、<#nested>指令可以被多次調(diào)用,例如
    <#macro greet>
    <p>
    <#nested>
    <#nested>
    <#nested>
    <#nested>
    </p>
    </#macro>

    <@greet>hello Emma!</@greet>

    輸出為
    <p>
    hello Emma!
    hello Emma!
    hello Emma!
    hello Emma!
    </p>

    2-3、嵌套的內(nèi)容可以是有效的FTL,例如:
    <#macro welcome>
    <p>
    <#nested>
    </p>
    </#macro>

    <#macro greet person color="black">
    <font size="+2" color="${color}"> Hello ${person}!</font>
    </#macro>

    <@welcome>
    <@greet person="Emma" color="red" />
    <@greet person="Andrew" />
    <@greet person="Peter" />
    </@welcome>

    輸出為:
    <p>
    <font size="+2" color="red"> Hello Emma!</font>
    <font size="+2" color="black"> Hello Andrew!</font>
    <font size="+2" color="black"> Hello Peter!</font>
    </p>

    2-4、宏定義中的局部變量對(duì)嵌套內(nèi)容是不可見的,例如:

    <#macro repeat count>
    <#local y="test" />
    <#list 1..count as x>
    ${y}${count}/${x}:<#nested />
    </#list>
    </#macro>

    <@repeat count=3>
    ${y?default("?")}
    ${x?default("?")}
    ${count?default("?")}
    </@repeat>

    輸出結(jié)果為
    test 3/1:???
    test 3/2:???
    test 3/3:???

    2-5、在宏定義中使用循環(huán)變量,通常用來重復(fù)嵌套內(nèi)容,基本用法為:作為nested指令的參數(shù),傳遞循環(huán)變量的實(shí)際值,而在調(diào)用自定義指令時(shí),在標(biāo)記的參數(shù)后面指定循環(huán)變量的名字。
    例如:
    <#macro repeat count>
    <#list 1..count as x>
    <#nested x,x/2,x==count />
    </#list>
    </#macro>

    <@repeat count=4;c,halfc,last>
    ${c}. ${halfc}
    <#if last>
    last!
    </#if>
    </@repeat>

    輸出結(jié)果是

    1. 0.5
    2. 1
    3. 1.5
    4. 2last!

    注意:指定循環(huán)變量的數(shù)目和用戶定義指令開始標(biāo)記指定的不同不會(huì)有問題
    調(diào)用時(shí),少指定循環(huán)變量,多指定的值會(huì)不見
    調(diào)用時(shí),多指定循環(huán)變量,多余的循環(huán)變量不會(huì)被創(chuàng)建

    二、在模板中定義變量

    1、在模板中定義的變量有三種類型

    1-1、plain變量:可以在模板的任何地方訪問,包括使用include指令插入的模板,使用assign指令創(chuàng)建和替換。
    1-2、局部變量:在宏定義體中有效,使用local指令創(chuàng)建和替換。
    1-3、循環(huán)變量:只能存在于指令的嵌套內(nèi)容,由指令(如list)自動(dòng)創(chuàng)建。

    注意:
    1)、宏的參數(shù)是局部變量,不是循環(huán)變量。
    2)、局部變量隱藏同名的plain變量
    3)、循環(huán)變量隱藏同名的plain變量和局部變量。

    例如:

    <#assign x="plain">
    1. ${x} <#-- plain -->

    <@test />

    6. ${x}
    <#list ["loop"] as x>
    7. ${x} <#-- loop -->
    <#assign x="plain2">
    8. ${x} <#-- loop -->
    </#list>
    9. ${x} <#-- plain2 -->

    <#macro test>
    2. ${x} <#-- plain -->
    <#local x="local">
    3. ${x} <#-- local -->
    <#list ["loop"] as x>
    4. ${x} <#-- loop -->
    </#list>
    5. ${x} <#-- local -->
    </#macro>

    4)、內(nèi)部循環(huán)變量隱藏同名的外部循環(huán)變量

    <#list ["loop1"] as x>
    ${x} <#-- loop1 -->
    <#list ["loop2"] as x>
    ${x} <#-- loop2 -->
    <#list ["loop3"] as x>
    ${x} <#-- loop3 -->
    </#list>
    ${x} <#-- loop2 -->
    </#list>
    ${x} <#-- loop1 -->
    </#list>

    5)、模板中的變量會(huì)隱藏?cái)?shù)據(jù)模型中的同名變量,如果需訪問數(shù)據(jù)模型中的變量,使用特殊變量global。

    例如:
    假設(shè)數(shù)據(jù)模型中的user值為Emma
    <#assign user="Man">
    ${user} <#-- Man -->
    ${.global.user} <#-- Emma -->
    posted @ 2008-08-15 14:35 飛飛 閱讀(276) | 評(píng)論 (0)編輯 收藏

    一:
    myeclipse 傻瓜式的完成spring和hibernate的載入;
    注意需要把lib里面的asm-XXcommons-collections-XX 低版本的刪除掉,因?yàn)檩d入spring和hibernate的時(shí)候會(huì)裝在2個(gè)不一樣的版本,包沖突.
    記得把dwr的包放進(jìn)去
    數(shù)據(jù)庫用mysql.
    CREATE TABLE `book` (
      `id` int(11) NOT NULL auto_increment,
      `name` varchar(11) default NULL,
      `isbm` varchar(11) default NULL,
      `author` varchar(11) default NULL,
      PRIMARY KEY  (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=gbk;
    id為自動(dòng)增長(zhǎng)
    二:
    完成上步后用hibernate的反向機(jī)制,完成數(shù)據(jù)庫表的映射
    如下:
    AbstractBook.java

    package com;

    /**
     * AbstractBook generated by MyEclipse Persistence Tools
     */

    public abstract class AbstractBook implements java.io.Serializable {

     // Fields

     private Integer id;
     private String name;
     private String isbm;
     private String author;

     // Constructors

     /** default constructor */
     public AbstractBook() {
     }

     /** full constructor */
     public AbstractBook(String name, String isbm, String author) {
      this.name = name;
      this.isbm = isbm;
      this.author = author;
     }

     // Property accessors

     public Integer getId() {
      return this.id;
     }

     public void setId(Integer id) {
      this.id = id;
     }

     public String getName() {
      return this.name;
     }

     public void setName(String name) {
      this.name = name;
     }

     public String getIsbm() {
      return this.isbm;
     }

     public void setIsbm(String isbm) {
      this.isbm = isbm;
     }

     public String getAuthor() {
      return this.author;
     }

     public void setAuthor(String author) {
      this.author = author;
     }

    }


    Book.java

    package com;

    // Generated by MyEclipse Persistence Tools

    /**
     * Book generated by MyEclipse Persistence Tools
     */
    public class Book extends AbstractBook implements java.io.Serializable {

     // Constructors

     /** default constructor */
     public Book() {
     }

     /** full constructor */
     public Book(String name, String isbm, String author) {
      super(name, isbm, author);
     }

    }


    BookDAO.java

    package com;

    import java.util.List;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.hibernate.LockMode;
    import org.springframework.context.ApplicationContext;
    import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

    /**
     * Data access object (DAO) for domain model class Book.
     *
     * @see com.Book
     * @author MyEclipse Persistence Tools
     */

    public class BookDAO extends HibernateDaoSupport {
     private static final Log log = LogFactory.getLog(BookDAO.class);

     protected void initDao() {
      // do nothing
     }

     public void save(Book transientInstance) {
      log.debug("saving Book instance");
      try {
       getHibernateTemplate().save(transientInstance);
       log.debug("save successful");
      } catch (RuntimeException re) {
       log.error("save failed", re);
       throw re;
      }
     }

     public void delete(Book persistentInstance) {
      log.debug("deleting Book instance");
      try {
       getHibernateTemplate().delete(persistentInstance);
       log.debug("delete successful");
      } catch (RuntimeException re) {
       log.error("delete failed", re);
       throw re;
      }
     }

     public Book findById(java.lang.Integer id) {
      log.debug("getting Book instance with id: " + id);
      try {
       Book instance = (Book) getHibernateTemplate().get("com.Book", id);
       return instance;
      } catch (RuntimeException re) {
       log.error("get failed", re);
       throw re;
      }
     }

     public List findByExample(Book instance) {
      log.debug("finding Book instance by example");
      try {
       List results = getHibernateTemplate().findByExample(instance);
       log.debug("find by example successful, result size: "
         + results.size());
       return results;
      } catch (RuntimeException re) {
       log.error("find by example failed", re);
       throw re;
      }
     }

     public List findByProperty(String propertyName, Object value) {
      log.debug("finding Book instance with property: " + propertyName
        + ", value: " + value);
      try {
       String queryString = "from Book as model where model."
         + propertyName + "= ?";
       return getHibernateTemplate().find(queryString, value);
      } catch (RuntimeException re) {
       log.error("find by property name failed", re);
       throw re;
      }
     }

     public List findAll() {
      log.debug("finding all Book instances");
      try {
       String queryString = "from Book";
       return getHibernateTemplate().find(queryString);
      } catch (RuntimeException re) {
       log.error("find all failed", re);
       throw re;
      }
     }

     public Book merge(Book detachedInstance) {
      log.debug("merging Book instance");
      try {
       Book result = (Book) getHibernateTemplate().merge(detachedInstance);
       log.debug("merge successful");
       return result;
      } catch (RuntimeException re) {
       log.error("merge failed", re);
       throw re;
      }
     }

     public void attachDirty(Book instance) {
      log.debug("attaching dirty Book instance");
      try {
       getHibernateTemplate().saveOrUpdate(instance);
       log.debug("attach successful");
      } catch (RuntimeException re) {
       log.error("attach failed", re);
       throw re;
      }
     }

     public void attachClean(Book instance) {
      log.debug("attaching clean Book instance");
      try {
       getHibernateTemplate().lock(instance, LockMode.NONE);
       log.debug("attach successful");
      } catch (RuntimeException re) {
       log.error("attach failed", re);
       throw re;
      }
     }

     public static BookDAO getFromApplicationContext(ApplicationContext ctx) {
      return (BookDAO) ctx.getBean("BookDAO");
     }
    }


    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
     http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
     <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/applicationContext.xml</param-value>
     </context-param>
     <listener>
      <listener-class>
       org.springframework.web.context.ContextLoaderListener
      </listener-class>
     </listener>
     <servlet>
      <servlet-name>dwr-invoker</servlet-name>
      <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
      <init-param>
       <param-name>debug</param-name>
       <param-value>true</param-value>
      </init-param>
     </servlet>

     <servlet-mapping>
      <servlet-name>dwr-invoker</servlet-name>
      <url-pattern>/dwr/*</url-pattern>
     </servlet-mapping>
     <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
     </welcome-file-list>
    </web-app>

     



    Book.hbm.xml

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
    <!--
        Mapping file autogenerated by MyEclipse Persistence Tools
    -->
    <hibernate-mapping>
        <class name="com.Book" table="book" catalog="test">
            <id name="id" type="java.lang.Integer">
                <column name="id" />
                <generator class="native" />
            </id>
            <property name="name" type="java.lang.String">
                <column name="name" length="11" />
            </property>
            <property name="isbm" type="java.lang.String">
                <column name="isbm" length="11" />
            </property>
            <property name="author" type="java.lang.String">
                <column name="author" length="11" />
            </property>
        </class>
    </hibernate-mapping>

    BookManageService.java 暴露給dwr使用的接口

    package com;

    import java.util.List;

    public interface BookManageService {

     public List<Book> getAllBooks();

     public List<Book> getBookByName(String name);

     public void updateBook(Book book);

     public void addBook(Book book);

     public void deleteBook(Integer id);
    }


    BookManageServiceImpl.java 接口的實(shí)現(xiàn)類

    package com;

    import java.util.List;

    public class BookManageServiceImpl implements BookManageService {

     private BookDAO bookDAO;

     public BookDAO getBookDAO() {
      return bookDAO;
     }

     public void setBookDAO(BookDAO bookDAO) {
      this.bookDAO = bookDAO;
     }
     public void addBook(Book book) {
      System.out.println("impl:"+book);
      bookDAO.save(book);
     }

     public void deleteBook(Integer id) {
      Book book = bookDAO.findById(id);
      bookDAO.delete(book);
     }


     @SuppressWarnings("unchecked")
     public List<Book> getAllBooks() {

      return bookDAO.findAll();
     }


     @SuppressWarnings("unchecked")
     public List<Book> getBookByName(String name) {

      return bookDAO.findByProperty(name, name);
     }

     public void updateBook(Book book) {
      bookDAO.attachDirty(book);
     }


    }



    applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
     <bean id="dataSource"
      class="org.apache.commons.dbcp.BasicDataSource">
      <property name="driverClassName"
       value="com.mysql.jdbc.Driver">
      </property>
      <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"></property>
      <property name="username" value="root"></property>
      <property name="password" value="root"></property>
     </bean>
     <bean id="sessionFactory"
      class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
      <property name="dataSource">
       <ref bean="dataSource" />
      </property>
      <property name="hibernateProperties">
       <props>
        <prop key="hibernate.dialect">
         org.hibernate.dialect.MySQLDialect
        </prop>
        <prop key="hibernate.show_sql">true</prop>
       </props>
      </property>
      <property name="mappingResources">
       <list>
        <value>com/Book.hbm.xml</value>
       </list>
      </property>
     </bean>
    <bean id="BookDAO" class="com.BookDAO">
      <property name="sessionFactory">
       <ref bean="sessionFactory" />
      </property>
     </bean>
    <bean id="bookManageServiceTarget"
      class="com.BookManageServiceImpl">
      <property name="bookDAO">
       <ref bean="BookDAO" />
      </property>
     </bean>
     <bean id="transactionManager"
      class="org.springframework.orm.hibernate3.HibernateTransactionManager">
      <property name="sessionFactory">
       <ref bean="sessionFactory" />
      </property>
     </bean>
     <bean id="bookManageService"
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
      <property name="transactionManager" ref="transactionManager" />
      <property name="target" ref="bookManageServiceTarget" />
      <property name="transactionAttributes">
       <props>
        <prop key="add*">PROPAGATION_REQUIRED</prop>
        <prop key="delete*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
       </props>
      </property>
     </bean>
    </beans>

    dwr.xml

    <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr//dwr20.dtd">
    <dwr>
     <allow>
      <convert converter="bean" match="com.Book" />
      <create creator="spring" javascript="BookManageService">
       <param name="beanName" value="bookManageService" />
       <include method="getAllBooks" />
       <include method="getBookByName" />
       <include method="updateBook" />
       <include method="addBook" />
       <include method="deleteBook" />
      </create>
     </allow>
    </dwr>

    index.jsp


    <html>
     <head>
      <title>DWR test</title>
      <script type='text/javascript' src='/shdwr/dwr/interface/BookManageService.js'></script>
      <script type='text/javascript' src='/shdwr/dwr/engine.js'></script>
      <script type='text/javascript' src='/shdwr/dwr/util.js'></script>

      <script type="text/javascript">
            var bookCache ={};
            var currentBook = null;
           
            function loadAllBooks(){
                BookManageService.getAllBooks(handleGetAllBooks,handleGetAllBooksError);
            }
            function handleGetAllBooks(books){
                dwr.util.removeAllRows("booksBody",{ filter:function(tr){
           return (tr.id != "pattern");
           }});
     
                var book,id;
                for(var i = 0; i < books.length; i++){
                    book = books[i];
                  
                    id = book.id;
              
                    dwr.util.cloneNode("pattern", { idSuffix:id });
                    dwr.util.setValue("t_name" + id, book.name);
                    dwr.util.setValue("t_isbm" + id, book.isbm);
                    dwr.util.setValue("t_author" + id,book.author);
                   $("pattern" + id).style.display = "block";
                   bookCache[id] = book;
                }
            }
            function handleGetAllBooksError(msg){
                alert("Error: " + msg);
            }
           
            function addBook(){
                var book ={name:null,isbm:null,author:null};
                dwr.util.getValues(book);
                dwr.engine.beginBatch();
                     BookManageService.addBook(book);
                     loadAllBooks();
                  dwr.engine.endBatch();
            }
           
            function editBook(btId){
                currentBook = bookCache[btId.substring(4)];
                dwr.util.setValues(currentBook);
            }
           
            function updateBook(){
                var book = {id:null,name:null,isbm:null,author:null};
                dwr.util.getValues(book);
                book.id = currentBook.id;
                BookManageService.updateBook(book,handleUpdateBook,handleUpdateBookError);
            }
           
            function handleUpdateBook(){
                alert("Update book successfully!");
                loadAllBooks();
            }
           
            function handleUpdateBookError(msg){
                alert("Error: " + msg);
            }
           
            function deleteBook(btId){
                var i = confirm("Are you sure to delete the book?");
                if(i == true)
                    BookManageService.deleteBook(btId.substring(6),handleDeleteBook,handleDeleteBookError);
            }
           
            function handleDeleteBook(){
              alert("The book has been delete successfully!");
              loadAllBooks();
            }
           
            function handleDeleteBookError(msg){
              alert("Error: " + msg);
            }
            function reset(){
               dwr.util.setValue("name", "");
               dwr.util.setValue("isbm", "");
               dwr.util.setValue("author", "");
            }
        </script>
     </head>


     <body onload="loadAllBooks()">
      <div>
       <h2>
        Add book
       </h2>
       <table>
        <tr>
         <td>
          Name:
         </td>
         <td>
          <input type="text" id="name">
         </td>
        </tr>
        <tr>
         <td>
          ISBN:
         </td>
         <td>
          <input type="text" id="isbm">
         </td>
        </tr>

        <tr>
         <td>
          Author:
         </td>
         <td>
          <input type="text" id="author">
         </td>
        </tr>
        <tr>
         <td colspan="2">
          <input type="button" id="add" value="Add" onclick="addBook()">
          <input type="reset"  id="reset" value="reset" onclick="reset()">
          <input type="button" id="update" value="Update"
           onclick="updateBook()">
         </td>
        </tr>
       </table>
      </div>
      <hr>
      <div id="list">
       <table border="1">
        <thead>
         <tr>
          <th>
           Name
          </th>
          <th>
           ISBN
          </th>
          <th>
           Author
          </th>
          <th>
           Action
          </th>
         </tr>
        </thead>
        <tbody id="booksBody">
         <tr id="pattern" style="display: none;">
          <td>
           <span id="t_name"></span>
          </td>
          <td>
           <span id="t_isbm"></span>
          </td>
          <td>
           <span id="t_author"></span>
          </td>
          <td>
           <span id="action"> <input id="edit" type="button"
             value="Edit" onclick="editBook(this.id)" /> <input id="delete"
             type="button" value="Delete" onclick="deleteBook(this.id)" />
           </span>
          </td>
         </tr>
        </tbody>
       </table>
      </div>
     </body>


    posted @ 2008-08-13 19:55 飛飛 閱讀(658) | 評(píng)論 (0)編輯 收藏

    1、addRows 增添數(shù)據(jù)行到指定的table
       方法基本語法:dwr.util.addRows(id, array, cellfuncs, [options]);
          * id:table 的 id (最好是一個(gè)tbody的id)
        * array: 需要被填充到table里的數(shù)據(jù)對(duì)象,可以是數(shù)組,集合等對(duì)象,每一個(gè)數(shù)組元素對(duì)應(yīng)table的一行
        * cellfuncs: function數(shù)組 每個(gè)元素對(duì)應(yīng)table某一列數(shù)據(jù)的取得方式
        * options: 包含幾個(gè)屬性的對(duì)象(可選)
      
        options:(來源:www.iocblog.net)
        # 屬性rowCreator: 一個(gè)function,默認(rèn)返回document.createElement("tr"),可以編輯返回自定義的tr(比如不同的樣式)
        # 屬性cellCreator: 一個(gè)function,默認(rèn)返回document.createElement("td"),可以編輯返回自定義的tr(比如不同的樣式)
        # 屬性escapeHtml: 是否轉(zhuǎn)義<,>,&,",'
        
        當(dāng)所有call back function 都需要轉(zhuǎn)義,則可以使用dwr.util.setEscapeHtml(false)

    2、removeAllRows 把指定table的所有行都移除
        方法基本語法:dwr.util.removeAllRows(id);
        * id:table 的 id (最好是一個(gè)tbody的id)
      
      
    3、byId
     你可以把它看成是document.getElementById()的替代版,如果指定的id有多個(gè)匹配項(xiàng),就會(huì)返回一個(gè)element數(shù)組
        方法基本語法:dwr.util.byId(id)
        另:在引入util.js的環(huán)境下,你還可以使用$(id)代替document.getElementById(),但是為了不和Prototype相沖突,還是建議各位使用    byId。
      
    4、getValue 取得html 頁面元素的value
        方法基本語法:dwr.util.getValue(id);
      
    5、getText
        用法和getValue方法相同,唯一的不同在于getText是用來取得下拉框列表的Text值,而非Value

    6、getValues
    方法基本語法:dwr.util.getValues(object) ;
    參數(shù)是一個(gè)擁有多個(gè)屬性的javascript object,屬性名稱是html頁面元素的id,屬性value為html頁面元素的value,該方法不返回任何東西,而是改變了object的屬性值。


    7、setValue 設(shè)置html 頁面元素的value
    方法基本語法:dwr.util.setValue(id, value [, options]) ;
       如果id參數(shù)指定的頁面元素是select列表,該列表與value參數(shù)值相匹配的option選項(xiàng)會(huì)處于選中狀態(tài)。

    8、setValues
    方法基本語法:dwr.util.setValues(object) ;
    參數(shù)是一個(gè)擁有多個(gè)屬性的javascript object,屬性名稱是html頁面元素的id,屬性value為html頁面元素的value

    9、addOptions
    方法基本語法:dwr.util.addOptions(...); 有多種調(diào)用方式:
    # dwr.util.addOptions(id,["first","second","third"]) id參數(shù)指定的頁面元素可以是ol、ul或select,String數(shù)組將被set到id指定的頁面元素
    # dwr.util.addOptions(id,[{name:"first",value:"1"},{name:"second",value:"2"},{name:"third",value:"3"}],"value","name")
    這種方式只對(duì)應(yīng)select的情形,如上所述,Object數(shù)組里每個(gè)元素的value屬性值將被set到option的value里,name屬性將被set到option的text里。
    如果沒有第四個(gè)參數(shù),將會(huì)把value屬性值同時(shí)set到option的value和text里。
    # dwr.util.addOptions(id,{first:"1",second:"1",third:"3"})
    這種方式也只對(duì)應(yīng)select的情形,第二個(gè)參數(shù)是一個(gè)Object,屬性名set到option的value里,屬性值set到option的text里

    10、removeAllOptions
    方法基本語法:dwr.util.removeAllOptions(id);
    除去所有動(dòng)態(tài)加載的Options或列表項(xiàng),與addOptions配合使用


    11、onReturn
    方法基本語法:dwr.util.onReturn(event, func)
    當(dāng)輸入回車時(shí),調(diào)用func名指定的方法

    12、useLoadingMessage
    方法基本語法:dwr.util.useLoadingMessage();
    顯示一個(gè)正在加載的圖片。必須在頁面loaded以后調(diào)用
    posted @ 2008-08-13 15:23 飛飛 閱讀(846) | 評(píng)論 (0)編輯 收藏

    一:
    用myeclipse中的傻瓜式添加spring組件。
    修改web.xml文件內(nèi)容,如下:(紅色標(biāo)記處)

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
     http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
     <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/applicationContext-dwr.xml</param-value>
     </context-param>
     <listener>
      <listener-class>
       org.springframework.web.context.ContextLoaderListener
      </listener-class>
     </listener>
     <servlet>
      <servlet-name>dwr-invoker</servlet-name>
      <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
      <init-param>
       <param-name>debug</param-name>
       <param-value>true</param-value>
      </init-param>
     </servlet>

     <servlet-mapping>
      <servlet-name>dwr-invoker</servlet-name>
      <url-pattern>/dwr/*</url-pattern>
     </servlet-mapping>
     <welcome-file-list>
      <welcome-file>index.jsp</welcome-file>
     </welcome-file-list>
    </web-app>
    二:
    將文件交給spring來管理

    <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://getahead.org/dwr//dwr20.dtd">

    <dwr>
      <allow>
        <create creator="spring" javascript="DWRUserAccess">
          <param name="beanName" value="dwrUserAccess"/>
        </create>
        <convert match="com.dwr.User" converter="bean"></convert>
      </allow>
    </dwr>

    三:
    在web-inf下新建applicationContext-dwr.xml文件,內(nèi)容如下
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans default-autowire="byName">  
        <bean name="dwrUserAccess" class="com.dwr.DWRUserAccess"></bean>
    </beans>


    OK。
    posted @ 2008-08-13 11:22 飛飛 閱讀(1579) | 評(píng)論 (0)編輯 收藏

    DWR(Direct Web Remoting)是一個(gè)WEB遠(yuǎn)程調(diào)用框架.利用這個(gè)框架可以讓AJAX開發(fā)變得很簡(jiǎn)單.利用DWR可以在客戶端利用JavaScript直接調(diào)用服務(wù)端的Java方法并返回值給JavaScript就好像直接本地客戶端調(diào)用一樣(DWR根據(jù)Java類來動(dòng)態(tài)生成JavaScrip代碼).它的最新版本DWR0.6添加許多特性如:支持Dom Trees的自動(dòng)配置,支持Spring(JavaScript遠(yuǎn)程調(diào)用spring bean),更好瀏覽器支持,還支持一個(gè)可選的commons-logging日記操作.

    以上摘自open-open,它通過反射,將java翻譯成javascript,然后利用回調(diào)機(jī)制,輕松實(shí)現(xiàn)了javascript調(diào)用Java代碼。

    其大概開發(fā)過程如下:
    1.編寫業(yè)務(wù)代碼,該代碼是和dwr無關(guān)的。
    2.確認(rèn)業(yè)務(wù)代碼中哪些類、哪些方法是要由javascript直接訪問的。
    3.編寫dwr組件,對(duì)步驟2的方法進(jìn)行封裝。
    4.配置dwr組件到dwr.xml文件中,如果有必要,配置convert,進(jìn)行java和javascript類型互轉(zhuǎn)。
    5.通過反射機(jī)制,dwr將步驟4的類轉(zhuǎn)換成javascript代碼,提供給前臺(tái)頁面調(diào)用。
    5.編寫網(wǎng)頁,調(diào)用步驟5的javascript中的相關(guān)方法(間接調(diào)用服務(wù)器端的相關(guān)類的方法),執(zhí)行業(yè)務(wù)邏輯,將執(zhí)行結(jié)果利用回調(diào)函數(shù)返回。
    6.在回調(diào)函數(shù)中,得到執(zhí)行結(jié)果后,可以繼續(xù)編寫業(yè)務(wù)邏輯的相關(guān)javascript代碼。

    下面以用戶注冊(cè)的例子,來說明其使用。(注意,本次例子只是用于演示,說明DWR的使用,類設(shè)計(jì)并不是最優(yōu)的)。

    1.先介紹下相關(guān)的Java類

      User: 用戶類,
      public class User {
    //登陸ID,主鍵唯一
    private String id;
    //姓名
    private String name;
    //口令
    private String password;
    //電子郵件
    private String email;
            
    //以下包含getXXX和setXXX方法
    .......
      }

      UserDAO:實(shí)現(xiàn)User的數(shù)據(jù)庫訪問,這里作為一個(gè)演示,編寫測(cè)試代碼
      public class UserDAO {
        //存放保存的數(shù)據(jù)
        private static Map dataMap = new HashMap();

        //持久用戶
        public boolean save(User user) {
          if (dataMap.containsKey(user.getId()))
            return false;
          System.out.println("下面開始保存用戶");
          System.out.println("id:"+user.getId());
          System.out.println("password:"+user.getPassword());
          System.out.println("name:"+user.getName());
          System.out.println("email:"+user.getEmail());
          dataMap.put(user.getId(), user);
          System.out.println("用戶保存結(jié)束");
          return true;
        }

        //查找用戶
        public User find(String id) {
          return (User)dataMap.get(id);
        }
    }

      DWRUserAccess:DWR組件,提供給javascript訪問的。

      public class DWRUserAccess {

          UserDAO userDAO = new UserDAO();

          public boolean save(User user) {
            return userDAO.save(user);
          }

          public User find(String id) {
            return userDAO.find(id);
          }
      }
      

      下面說明下程序執(zhí)行的流程

      1.用戶在頁面上輸入相關(guān)注冊(cè)信息,id、name、password、email,點(diǎn)擊“提交”按鈕
      2.javascript代碼開始執(zhí)行,根據(jù)用戶填寫相關(guān)信息,通過dwr提供的DWRUserAccess.js里save的方法,調(diào)用服務(wù)器端的DWRUserAccess類save方法,將注冊(cè)信息保存。
      3.通過DWRUserAccess.jsp里的find方法,調(diào)用服務(wù)器端DWRUserAccess類里的find方法,執(zhí)行用戶信息查找。

      注意,在以上的執(zhí)行過程中,DWRUserAccess是供DWR調(diào)用的,是DWR組件,因此需要將DWRUserAccess類配置到dwr中。

      接下來講解本次dwr測(cè)試環(huán)境的配置。

      1.新建一個(gè)webapp,命名為testApp
      2.將dwr.jar拷貝到testApp的WEB-INF的lib目錄下
      3.編譯上面的User,UserDAO,DWRUserAccess類,放到classes目錄下
      4.在web.xml中配置servlet,適配路徑到dwr目錄下,如下所示
        <servlet>
        <servlet-name>dwr-invoker</servlet-name>
        <display-name>DWR Servlet</display-name>
        <description>Direct Web Remoter Servlet</description>
        <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
        <init-param>
          <param-name>debug</param-name>
          <param-value>true</param-value>
        </init-param>
        <init-param>
          <param-name>scriptCompressed</param-name>
          <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>

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

      以上的配置可以攔截testApp下所有指向dwr的請(qǐng)求,關(guān)于這個(gè)攔截器,我們會(huì)在后面介紹。

      5.WEB-INF下新建一個(gè)dwr.xml文件,內(nèi)容如下:
      < xml version="1.0" encoding="UTF-8" >
    <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd">

    <dwr>
      <allow>
    <create creator="new" javascript="DWRUserAccess">
          <param name="class" value="test.DWRUserAccess"/>
        </create>
    <convert converter="bean" match="test.User"/>
      </allow>
    </dwr>

      這里我們把DWRUserAccess配置到了dwr中,create元素中,creater="new"表示每調(diào)用一次DWRUserAccess時(shí),需要new一個(gè)這樣的類;javascript="DWRUserAccess",表示提供給前臺(tái)頁面調(diào)用的javascirpt文件是DWRUserAccess.js。

      convert元素用于數(shù)據(jù)類型轉(zhuǎn)換,即java類和javascript之間相互轉(zhuǎn)換,因?yàn)楹颓芭_(tái)交換的是User對(duì)象,因此需要對(duì)此使用bean轉(zhuǎn)換,我們將在后面介紹這個(gè)類。

      4.編寫測(cè)試的HTML頁面 test.html
       <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    <HTML>
    <HEAD>
    <TITLE>DWR測(cè)試</TITLE>
    <meta http-equiv=Content-Type content="text/html; charset=gb2312">
    <script src="/oblog312/dwr/engine.js"></script>
    <script src="/oblog312/dwr/util.js"></script>
    <script src="/oblog312/dwr/interface/DWRUserAccess.js"></script>
    </HEAD>
    <BODY>
    <B>用戶注冊(cè)</B><br>
    ------------------------------------------------
    <Br>
    <form name="regForm">
    登陸ID:<input type="text" name="id"><br>
    口  令:<input type="password" name="password"><br>
    姓  名:<input type="text" name="name"><br>
    電子郵件:<input type="text" name="email"><br>
    <input type="button" name="submitBtn" value="提交" onclick="OnSave()"><br>
        </form>

    <br>
    <br><B>用戶查詢</B><br>
    ------------------------------------------------
    <Br>
    <form name="queryForm">
    登陸ID:<input type="text" name="id"><br>
    <input type="button" name="submitBtn" value="提交" onclick="OnFind()"><br>
    </form>
    <br>
    </BODY>
    </HTML>
    <SCRIPT LANGUAGE="JavaScript">
    <!--
    function saveFun(data) {
    if (data) {
      alert("注冊(cè)成功!");
    } else {
      alert("登陸ID已經(jīng)存在!");
    }
    }

    function OnSave() {
    var userMap = {};
    userMap.id = regForm.id.value;
    userMap.password = regForm.password.value;
    userMap.name = regForm.name.value;
    userMap.email = regForm.email.value;
    DWRUserAccess.save(userMap, saveFun);
    }

    function findFun(data) {
    if (data == null) {
      alert("無法找到用戶:"+queryForm.id.value);
      return;
    }

    alert("找到用戶,nid:"+data.id+",npassword:"+data.password+",nname:"+data.name+",nemail:"+data.email);

    }

    function OnFind() {
    DWRUserAccess.find(queryForm.id.value, findFun);
    }
    //-->
    </SCRIPT>


    以下對(duì)頁面的javascript進(jìn)行解釋

    <script src="/oblog312/dwr/engine.js"></script>
    <script src="/oblog312/dwr/util.js"></script>
    這兩個(gè)是dwr提供的,用戶可以不必關(guān)心,只需要導(dǎo)入即可

    <script src="/oblog312/dwr/interface/DWRUserAccess.js"></script>
    是我們編寫的DWRUserAccess類,經(jīng)dwr反射后,生成的javascript代碼,它和DWRUserAccess.java是對(duì)應(yīng)的,供用戶調(diào)用,實(shí)際上我們就是通過這個(gè)js文件去調(diào)用服務(wù)器端的DWRUserAccess類的。

    <SCRIPT LANGUAGE="JavaScript">
    <!--
    function saveFun(data) {
    if (data) {
      alert("注冊(cè)成功!");
    } else {
      alert("用戶名已經(jīng)存在!");
    }
    }

    function OnSave() {
    var userMap = {};
    userMap.id = regForm.id.value;
    userMap.password = regForm.password.value;
    userMap.name = regForm.name.value;
    userMap.email = regForm.email.value;
    DWRUserAccess.save(userMap, saveFun);
    }

    function findFun(data) {
    if (data == null) {
      alert("無法找到用戶:"+queryForm.id.value);
      return;
    }

    alert("找到用戶,nid:"+data.id+",npassword:"+data.password+",nname:"+data.name+",nemail:"+data.email);

    }

    function OnFind() {
    DWRUserAccess.find(queryForm.id.value, findFun);
    }
    //-->
    </SCRIPT>

    這段javascirpt代碼,我們來看下OnSave函數(shù),首先它構(gòu)造一個(gè)map,將表單數(shù)據(jù)都設(shè)置到map中,然后調(diào)用DWRUserAccess.save(userMap, saveFun),執(zhí)行save操作。大家可以注意到,服務(wù)器端的DWRUserAccess中的save方法是這樣的:boolean save(User user),其參數(shù)是一個(gè)User對(duì)象,返回一個(gè)boolean值;而客戶端的方法是這樣的:save(userMap,saveFun),第一個(gè)參數(shù)userMap是javascirpt中的map對(duì)象,在這里相當(dāng)于服務(wù)器端的User對(duì)象(在服務(wù)器端執(zhí)行時(shí),會(huì)通過convert轉(zhuǎn)換成User對(duì)象),前面我們提到dwr是利用回調(diào)函數(shù)來返回執(zhí)行結(jié)果的,第二個(gè)參數(shù)saveFun即是一個(gè)回調(diào)函數(shù)。在函數(shù)function saveFun(data)中,data是執(zhí)行結(jié)果,這里是一個(gè)bool值,非常簡(jiǎn)單的,我們通過判斷data是否為真,可以知道用戶名是否重復(fù),用戶是否注冊(cè)成功。

    看一下OnFind查找函數(shù),執(zhí)行結(jié)果在回調(diào)函數(shù)findFun(data)中,因?yàn)榉?wù)器端返回的是一個(gè)User對(duì)象,通過convert,將會(huì)轉(zhuǎn)換成javascript的一個(gè)map對(duì)象,
    于是在findFun中,通過data.id、data.name、data.password、data.email我們可以輕松的訪問到這個(gè)User對(duì)象。


    好了配置完畢,啟動(dòng)服務(wù)器,在目錄中打入localhost/testApp/test.html。

    1.在“用戶注冊(cè)”表單中,id框中輸入admin,password中輸入123456,name中輸入chenbug,email中輸入chenbug@zj.com,點(diǎn)擊提交按鈕,彈出對(duì)話框:“注冊(cè)成功”,在服務(wù)器后臺(tái)可以看到信息如下:

    下面開始保存用戶
    id:admin
    password:123456
    name:chenbug
    email:chenbug@zj.com
    用戶保存結(jié)束

    再次點(diǎn)擊提交按鈕,彈出對(duì)話框“登陸ID已經(jīng)存在”。

    2.在“用戶查詢”對(duì)話框中,輸入登陸ID為admin,點(diǎn)擊提交按鈕,提示找到用戶,并顯示相關(guān)信息,輸入admin123,點(diǎn)擊提交按鈕,提示無法找到用戶。

    至此,測(cè)試結(jié)束。


    后續(xù):
    1。攔截器 uk.ltd.getahead.dwr.DWRServlet
    該類攔截所有指向dwr目錄下的請(qǐng)求,并調(diào)用Processor的handler方法進(jìn)行處理,在uk.ltd.getahead.dwr.impl.DefaultProcessor下,我們可以看到詳細(xì)的處理過程。
    if (pathInfo.length() == 0 ||
                pathInfo.equals(HtmlConstants.PATH_ROOT) ||
                pathInfo.equals(req.getContextPath()))
            {
                resp.sendRedirect(req.getContextPath() + servletPath + HtmlConstants.FILE_INDEX);
            }
            else if (pathInfo.startsWith(HtmlConstants.FILE_INDEX))
            {
                index.handle(req, resp);
            }
            else if (pathInfo.startsWith(HtmlConstants.PATH_TEST))
            {
                test.handle(req, resp);
            }
            else if (pathInfo.startsWith(HtmlConstants.PATH_INTERFACE))
            {
                iface.handle(req, resp);
            }
            else if (pathInfo.startsWith(HtmlConstants.PATH_EXEC))
            {
                exec.handle(req, resp);
            }
            else if (pathInfo.equalsIgnoreCase(HtmlConstants.FILE_ENGINE))
            {
                file.doFile(req, resp, HtmlConstants.FILE_ENGINE, HtmlConstants.MIME_JS);
            }
            else if (pathInfo.equalsIgnoreCase(HtmlConstants.FILE_UTIL))
            {
                file.doFile(req, resp, HtmlConstants.FILE_UTIL, HtmlConstants.MIME_JS);
            }
            else if (pathInfo.equalsIgnoreCase(HtmlConstants.FILE_DEPRECATED))
            {
                file.doFile(req, resp, HtmlConstants.FILE_DEPRECATED, HtmlConstants.MIME_JS);
            }
            else
            {
                log.warn("Page not found (" + pathInfo + "). In debug/test mode try viewing /[WEB-APP]/dwr/"); //$NON-NLS-1$ //$NON-NLS-2$
                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            }

    通過判斷request請(qǐng)求的servlet路徑,進(jìn)行處理,大家可以自己去參看,這里不詳細(xì)討論。


    2.bean轉(zhuǎn)換器,<convert converter="bean" match="test.User"/>
    將dwr.jar解壓縮,在路徑ukltdgetaheaddwr下可以看到dwr.xml,這里配置了系統(tǒng)默認(rèn)的一些轉(zhuǎn)換器,
    <converter id="bean" class="uk.ltd.getahead.dwr.convert.BeanConverter"/>即是剛才用到User類的轉(zhuǎn)換器,進(jìn)入代碼我們來看看它是如何在javascript和java間進(jìn)行轉(zhuǎn)換的。

    打開BeanConverter代碼,定位到函數(shù)

    public Object convertInbound(Class paramType, InboundVariable iv, InboundContext inctx) throws ConversionException

    即是將javascript對(duì)象轉(zhuǎn)換成java對(duì)象的,其中
    paramType即Class類型,在上面的例子中是test.User,
    InboundVariable iv,是傳入的值,通過iv.getValue可以得到傳入的javascript值串
    InboundContext inctx,是入口參數(shù)上下文,用于保存轉(zhuǎn)換的后java對(duì)象。

    因?yàn)榍芭_(tái)傳入的是一個(gè)javascript的map類型,而map肯定是以{開始和以}結(jié)束的,于是在這個(gè)函數(shù)一開始進(jìn)行了判斷
    if (!value.startsWith(ConversionConstants.INBOUND_MAP_START))
            {
                throw new IllegalArgumentException(Messages.getString("BeanConverter.MissingOpener", ConversionConstants.INBOUND_MAP_START)); //$NON-NLS-1$
            }

            if (!value.endsWith(ConversionConstants.INBOUND_MAP_END))
            {
                throw new IllegalArgumentException(Messages.getString("BeanConverter.MissingCloser", ConversionConstants.INBOUND_MAP_START)); //$NON-NLS-1$
            }

    javascript中,map里各個(gè)項(xiàng)是用逗號(hào)連接的,如var userMap = {id:'admin',password:'123456',name:'chenbug',email:'chenbug@zj.com'};而每個(gè)項(xiàng)的鍵值對(duì)是用冒號(hào)連接的,
    在convertInbound函數(shù)的接下來的處理中,即是通過分析map字串,通過paramType構(gòu)造java實(shí)例(即User類),然后通過反射,將這些鍵值對(duì)設(shè)置到j(luò)ava實(shí)例中,并返回。
    這樣就完成了javascript到j(luò)ava的轉(zhuǎn)換。


    另一個(gè)函數(shù)
    public String convertOutbound(Object data, String varname, OutboundContext outctx) throws ConversionException

    即是將java對(duì)象轉(zhuǎn)換為javascript對(duì)象(其實(shí)是聲明和賦值語句)。
    Object data ,是待轉(zhuǎn)換的java對(duì)象
    String varname,是javascript中的該對(duì)象的變量名
    OutboundContext outctx,傳出參數(shù)上下文,用于保存轉(zhuǎn)換后的javascript值

    StringBuffer buffer = new StringBuffer();
            buffer.append("var "); //$NON-NLS-1$
            buffer.append(varname);
            buffer.append("={};"); //$NON-NLS-1$
    這里聲明了map類型的變量。

    即下來來的代碼即是通過反射進(jìn)行變量賦值,如下
      buffer.append(varname);
                        buffer.append('.');
                        buffer.append(name);
                        buffer.append('=');
                        buffer.append(nested.getAssignCode());
                        buffer.append(';');
    大家可以自己去參看更多的代碼。

    3.dwr本身提供了一個(gè)測(cè)試環(huán)境,大家在配置完后,可以在IE中輸入地址http://localhost/testApp/dwr/index.html,看到配置的各DWR組件,并進(jìn)行相關(guān)測(cè)試。
    posted @ 2008-08-13 10:21 飛飛 閱讀(460) | 評(píng)論 (0)編輯 收藏

      DWR(直接Web遠(yuǎn)程控制)項(xiàng)目是在Apache許可下的一個(gè)開源的解決方案,它提供了一種簡(jiǎn)單的方式使得HTML頁面上的javascript可以訪問應(yīng)用服務(wù)器的Java對(duì)象方法,是Ajax開發(fā)者可以方便使用的一個(gè)優(yōu)雅框架。DWR具有一套Javascript功能集,它們把從HTML頁面調(diào)用應(yīng)用服務(wù)器上的Java對(duì)象的方法簡(jiǎn)化,操控不同類型的參數(shù),同時(shí)保持了HTML代碼的可讀性。DWR實(shí)現(xiàn)的AJAX在某些方面很先進(jìn),包括動(dòng)態(tài)生成javascript代碼;隱藏http協(xié)議等。

    web.xml配置:
    <servlet>
     <servlet-name>dwr-invoker</servlet-name>
     <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>  </servlet>
    <servlet-mapping>
     <servlet-name>dwr-invoker</servlet-name>
     <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>
    DWRServlet是DWR主Servlet,所有的/dwr/*所有請(qǐng)求都由這個(gè)servlet來處理。
    DWRServlet init()主要做了以下工作:
    1實(shí)例化DWR用到的Singleton類:AccessControl,Configuration,ConverterManager,CreatorManager,Processor
    2 讀去配置文件:包括dwr.jar包中的dwr.xml,WEB-INF/dwr.xml,web.xml。
    WEB-INF/dwr.xml文件告訴DWR哪些服務(wù)是要直接向JavaScript代碼公開的,以DWR的test為例:
        <create creator="new" javascript="JDate">
          <param name="class" value="java.util.Date"/>
          <exclude method="getHours"/>
          <auth method="getMinutes" role="admin"/>
          <auth method="getMinutes" role="devel"/>
        </create>
    DWR會(huì)根據(jù)此自動(dòng)生成對(duì)應(yīng)的JDate.js文件。new意味著DWR調(diào)用類的構(gòu)造函數(shù)獲得實(shí)例,還可以通過跟Spring集成達(dá)到該目的。

    DWRServlet的doGet和doPost均直接調(diào)用Processor的handle方法處理,handle處理的/dwr/*請(qǐng)求包含以下幾類:
    1 dwr/index.html,dwr/test/只能在debug模式下供調(diào)試用
    2 對(duì)dwr/engine.js,dwr/util.js,dwr/deprecated.js的請(qǐng)求調(diào)用,直接從包中讀取相應(yīng)的js文件流響應(yīng)回去,
    并對(duì)相應(yīng)做緩存處理,緩存以一個(gè)hashmap實(shí)現(xiàn)。
    3 對(duì)dwr/interface/的請(qǐng)求調(diào)用,DWR生成跟Java對(duì)應(yīng)的javascript存根,DWR通過ConverterManager自動(dòng)調(diào)整Java和javacript的參數(shù)類型匹配。
    4 客戶端javascript的實(shí)際調(diào)用,將通過handler方法的doExec執(zhí)行。

    由此,DWR的運(yùn)作流程是:
    客戶端包含3類javascript腳本
    1 java代碼的存根,即通過dwr/interface/調(diào)用,如<script type='text/javascript' src='dwr/interface/JDate.js'></script>
    如上所述,該存根由DWR具體是CreatorManager和ConvertorManager根據(jù)dwr.xml的配置進(jìn)行自動(dòng)生成
    2 DWR核心javascript庫,至少要包含<script type='text/javascript' src='dwr/engine.js'></script>,有時(shí)也需要包含util.js,util.js封裝了prototype的若干函數(shù),便于第三方j(luò)avascript調(diào)用
    3 第三方j(luò)avascript,該腳本需要調(diào)用第一類存根腳本所封裝的Java代碼,第二類的engine.js等腳本為該調(diào)用提供通信支撐。這樣就達(dá)到了一個(gè)基本的RPC的目的,由于RPC本質(zhì)上是同步進(jìn)行,而AJAX的XMLHTTP為異步調(diào)用,為了實(shí)現(xiàn)異步機(jī)制,第三方j(luò)avascript可以提供一個(gè)回調(diào)函數(shù)句柄傳入存根,待存根函數(shù)返回調(diào)用該回調(diào),這樣即達(dá)到異步通信。
    posted @ 2008-08-12 16:41 飛飛 閱讀(628) | 評(píng)論 (0)編輯 收藏

    DWR(Direct Web Remoting)是一個(gè)開放源clip_image002碼的使用 Apache 許可協(xié)議的解決方案,它包含服務(wù)器端 Java 庫、一個(gè) DWR Servlet 以及 JavaScript 庫。雖然 DWR 不是 Java 平臺(tái)上唯一可用的 Ajax-RPC 工具包,但是它是最成熟的,而且提供了許多有用的功能。為什么要使用DWR,我們首先介紹基本AJAX流程,從中可以看到引入DWR會(huì)帶來什么好處。 DWR的標(biāo)準(zhǔn)流程如右圖所示:

    DWR框架基本介紹

    從最簡(jiǎn)單的角度來說,DWR 是一個(gè)引擎,可以把服務(wù)器端 Java 對(duì)象的方法公開給 JavaScript 代碼。使用 DWR 可以有效地從應(yīng)用程序代碼中把 Ajax 的全部請(qǐng)求-響應(yīng)循環(huán)消除掉。這意味著客戶端代碼再也不需要直接處理 XMLHttpRequest 對(duì)象或者服務(wù)器的響應(yīng)。不再需要編寫對(duì)象的序列化代碼或者使用第三方工具才能把對(duì)象變成 XML。甚至不再需要編寫 servlet 代碼把 Ajax 請(qǐng)求調(diào)整成對(duì) Java 域?qū)ο蟮恼{(diào)用。

    DWR 是作為 Web 應(yīng)用程序中的 servlet 部署的。把它看作一個(gè)黑盒子,這個(gè) servlet 有兩個(gè)主要作用:首先,對(duì)于公開的每個(gè)類,DWR 動(dòng)態(tài)地生成包含在 Web 頁面中的 JavaScript。生成的 JavaScript 包含存根函數(shù),代表 Java 類上的對(duì)應(yīng)方法并在幕后執(zhí)行 XMLHttpRequest。這些請(qǐng)求被發(fā)送給 DWR,這時(shí)它的第二個(gè)作用就是把請(qǐng)求翻譯成服務(wù)器端 Java 對(duì)象上的方法調(diào)用并把方法的返回值放在 servlet 響應(yīng)中發(fā)送回客戶端,編碼成 JavaScript。

    頁面觸發(fā)eventHandler()事件,事件內(nèi)部調(diào)用了AjaxService.getOptions方法,當(dāng)調(diào)用完成后,利用服務(wù)端返回的數(shù)據(jù)用客戶端的populateList()方法進(jìn)行數(shù)據(jù)展現(xiàn)。

    我們通過一個(gè)簡(jiǎn)單的DWR示例來說明如何使用DWR。

    為了使用DWR,需要將DWR的jar文件拷入Web應(yīng)用的WEB-INF/lib目錄中(可在http://getahead.org/dwr下載),在web.xml中增加一個(gè)servlet聲明,并創(chuàng)建DWR的配置文件。

    服務(wù)端web.xml的配置:

    <!– add the Servlet for DWR –>
    <servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <display-name>DWR Servlet</display-name>
    <description>Direct Web Remoter Servlet</description>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    <init-param>
    <param-name>debug</param-name>
    <param-value>true</param-value>
    </init-param>
    <init-param>
    <param-name>activeReverseAjaxEnabled</param-name>
    <param-value>true</param-value>
    </init-param>
    <init-param>
    <param-name>initApplicationScopeCreatorsAtStartup</param-name>
    <param-value>true</param-value>
    </init-param>
    <init-param>
    <param-name>maxWaitAfterWrite</param-name>
    <param-value>500</param-value>
    </init-param>
    <init-param>
    <param-name>logLevel</param-name>
    <param-value>debug</param-value>
    </init-param>
    <init-param>
    <param-name>config</param-name>
    <param-value>/WEB-INF/dwr/dwr.xml</param-value>
    </init-param>
    <load-on-startup>6</load-on-startup>
    </servlet>
    <!– Action Servlet Mapping –>
    <servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>

    servlet-class值為uk.ltd.getahead.dwr.DWRServlet (如果dwr版本是1.0版本的,則必須用這個(gè)class)也可以是org.directwebremoting.servlet.DwrServlet

    也可以使用精簡(jiǎn)的一份配置:

    <!– add the Servlet for DWR –>
        <servlet>
        <servlet-name>dwr-invoker</servlet-name>
        <display-name>DWR Servlet</display-name>
        <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/dwr/dwr.xml</param-value>
        </init-param>
    </servlet>

        <!– Action Servlet Mapping –>
        <servlet-mapping>
            <servlet-name>dwr-invoker</servlet-name>
            <url-pattern>/dwr/*</url-pattern>
        </servlet-mapping>

    在WEB-INF中的dwr文件夾中創(chuàng)建文件dwr.xml:

    <?xml version=”1.0″ encoding=”UTF-8″?>
    <!DOCTYPE dwr PUBLIC “-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN”
    “http://getahead.org/dwr/dwr20.dtd”>
    <dwr>
    <allow>
    <create creator=”new” javascript=”login”>
    <param name=”class” value=”com.webex.tmis.test.Login” />
    <include method=”sayHello” />
    </create>
    <convert converter=”bean” match=”com.webex.tmis.test.User”>
    </convert>
    </allow>
    </dwr>

    服務(wù)端創(chuàng)建Login.java和User.java

    public class Login {
    public User sayHello(String name) {
    User user=new User();
    user.setName(name);
    user.setMessage(“Hello,”+name);
    return user;
    }
    }
    
    public class User {
    private String name;
    private String message;
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public String getMessage() {
    return message;
    }
    public void setMessage(String message) {
    this.message = message;
    }
    }

    客戶端的配置logon.jsp:

    <%@ page language=”java” contentType=”text/html; charset=UTF-8″
        pageEncoding=”UTF-8″%>
    <!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
    <html>
    <head>
    <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″>
    <script type=’text/javascript’ src=’/Tms/dwr/interface/login.js’></script>
    <script type=’text/javascript’ src=’/Tms/dwr/engine.js’></script>
    <script type=’text/javascript’ src=’/Tms/dwr/util.js’></script>

    <script>
    function getDWRMessage () {
    var value=document.getElementById(’user’).value;
    //var bean= DWRUtil.getValues(value);
    login.sayHello(value,showMessage);
    }

    function showMessage(user) {
    if(user.name==’Allen’){
    alert(user.message);
    }else{
      document.getElementById(’MessageSpan’).innerHTML = user.name+” is illegal!!!”;
    }
    }
    </script>

    <title>Logon</title>
    </head>

    <body>
    <table>
        <tr>
            <td id=MessageSpan></td>
        </tr>
        <tr>
            <td><input id=’user’ type=”text” value=”"></td>
        </tr>
        <tr>
            <td><input id=’submitBtn’ type=”button” onClick=”getDWRMessage()”
                value=”Submit”></td>
        </tr>
    </table>
    </body>
    </html>

    效果如下:

    做完配置后,可以加載http://127.0.0.1:8080/Tms/dwr看看哪些服務(wù)可以用。

    Classes known to DWR:

    • login (com.webex.tmis.test.Login)

    如果你需要使用ajax完成表單提交的操作,那么你應(yīng)該使用DWRUtil.getValues,參數(shù)或者是個(gè)form對(duì)象,或者是個(gè)與領(lǐng)域?qū)ο髮?duì)應(yīng)的js對(duì)象

    DWR框加的高級(jí)話題

    A.配置服務(wù)端方法的可調(diào)用范圍

    在以上的2個(gè)DWR示例中,我們配置了兩個(gè)JAVA類,將它們的所有屬性和方法都暴露給了客戶端,為了提高安全性,我們通常在dwr.xml中應(yīng)該只配置那些客戶端需要使用的方法和屬性。DWR支持對(duì)dwr.xml更細(xì)粒度的配置來提高安全性,我們先看一下與配置有關(guān)的兩個(gè)元素:

    create 元素

     

    create 元素告訴 DWR 應(yīng)當(dāng)公開給 Ajax 請(qǐng)求的服務(wù)器端類,并定義 DWR 應(yīng)當(dāng)如何獲得要進(jìn)行遠(yuǎn)程的類的實(shí)例。這里的 creator 屬性被設(shè)置為值 new,這意味著 DWR 應(yīng)當(dāng)調(diào)用類的默認(rèn)構(gòu)造函數(shù)來獲得實(shí)例。其他的可能有:通過代碼段用 Bean 腳本框架(Bean Scripting Framework,BSF)創(chuàng)建實(shí)例,或者通過與 IOC 容器 Spring 進(jìn)行集成來獲得實(shí)例。默認(rèn)情況下,到 DWR 的 Ajax 請(qǐng)求會(huì)調(diào)用 creator,實(shí)例化的對(duì)象處于頁面范圍內(nèi),因此請(qǐng)求完成之后就不再可用。

    create 的 javascript 屬性指定從 JavaScript 代碼訪問對(duì)象時(shí)使用的名稱。嵌套在 create 元素內(nèi)的 param 元素指定 creator 要?jiǎng)?chuàng)建的 Java 類。最后,include 元素指定應(yīng)當(dāng)公開的方法的名稱。顯式地說明要公開的方法是避免偶然間允許訪問有害功能的良好實(shí)踐 —— 如果漏了這個(gè)元素,類的所有方法都會(huì)公開給遠(yuǎn)程調(diào)用。反過來,可以用 exclude 元素指定那些想防止被訪問的方法。

    convert 元素

    convert 元素的作用是告訴 DWR 在服務(wù)器端 Java 對(duì)象表示和序列化的 JavaScript 之間如何轉(zhuǎn)換數(shù)據(jù)類型。DWR 自動(dòng)地在 Java 和 JavaScript 表示之間調(diào)整簡(jiǎn)單數(shù)據(jù)類型。這些類型包括 Java 原生類型和它們各自的類表示,還有 String、Date、數(shù)組和集合類型。DWR 也能把 JavaBean 轉(zhuǎn)換成 JavaScript 表示,但是出于安全性的原因,做這件事要求顯式的配置。

     

    DWR分模塊配置

    一般來說,你只需要一個(gè)dwr.xml文件,并且放置在默認(rèn)的位置:WEB-INF/dwr.xml。如果有大量的遠(yuǎn)程調(diào)用類,則可以將dwr.xml分成多個(gè)文件。 則在每web .xml中可以這樣配置:

    <servlet>
    <servlet-name>dwr-user-invoker</servlet-name>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    <init-param>
    <param-name>config-user</param-name>
    <param-value>WEB-INF/dwr-user.xml</param-value>
    </init-param>
    </servlet>
    <servlet>
    <servlet-name>dwr-admin-invoker</servlet-name>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    <init-param>
    <param-name>config-admin</param-name>
    <param-value>WEB-INF/dwr-admin.xml</param-value>
    </init-param>
    </servlet>
    <servlet-mapping>
    <servlet-name>dwr-admin-invoker</servlet-name>
    <url-pattern>/dwradmin/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
    <servlet-name>dwr-user-invoker</servlet-name>
    <url-pattern>/dwruser/*</url-pattern>
    </servlet-mapping>

     

    DWR批量調(diào)用

    在 DWR 中,可以在一個(gè) HTTP 請(qǐng)求中向服務(wù)器發(fā)送多個(gè)遠(yuǎn)程調(diào)用。調(diào)用 DWREngine.beginBatch() 告訴 DWR 不要直接分派后續(xù)的遠(yuǎn)程調(diào)用,而是把它們組合到一個(gè)批請(qǐng)求中。DWREngine.endBatch() 調(diào)用則把批請(qǐng)求發(fā)送到服務(wù)器。遠(yuǎn)程調(diào)用在服務(wù)器端順序執(zhí)行,然后調(diào)用每個(gè) JavaScript 回調(diào)。

    批處理在兩方面有助于降低延遲:第一,避免了為每個(gè)調(diào)用創(chuàng)建 XMLHttpRequest 對(duì)象并建立相關(guān)的 HTTP 連接的開銷。第二,在生產(chǎn)環(huán)境中,Web 服務(wù)器不必處理過多的并發(fā) HTTP 請(qǐng)求,改進(jìn)了響應(yīng)時(shí)間。

    F.DWR同步與異步

    在頁面的執(zhí)行中,如果在js中使用了dwr去遠(yuǎn)程調(diào)用數(shù)據(jù),這時(shí),下面的JS將會(huì)是繼續(xù)執(zhí)行,

    如果你是用作校驗(yàn)的話,那就導(dǎo)致不同步問題,返回結(jié)果無法生效,

    這時(shí)你可以通過設(shè)置DWR為同步來達(dá)到效果

    DWREngine.setAsync(false); => 默認(rèn)為異步,即 true;

    調(diào)用完后,設(shè)置還原

    DWREngine.setAsync(true);

    posted @ 2008-08-12 15:38 飛飛 閱讀(531) | 評(píng)論 (0)編輯 收藏

    Spring的JDBCTemplate

    當(dāng)hql等查詢方式不能滿足性能或靈活性的要求,必須使用SQL時(shí),大家有三種選擇:

    第一、使用Hibernate 的sql 查詢函數(shù),將查詢結(jié)果對(duì)象轉(zhuǎn)為Entity對(duì)象。

    第二、使用Hibernate Session的getConnection 獲得JDBC Connection,然后進(jìn)行純JDBC API操作;

    第三、選擇把Spring的JDBCTemplate作為一種很不錯(cuò)的JDBC Utils來使用。

         JDBCTemplate的使用很簡(jiǎn)單,只要在ApplicationContext文件里定義一個(gè)jdbcTemplate節(jié)點(diǎn),POJO獲得注入后可以直接執(zhí)行操作,不需要繼承什么基類,詳見JDBCTemplate參考文檔

         AplicationContext定義:

        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"/>
        </bean>

    實(shí)際使用: 

    SqlRowSet rs = jdbcTemplate.queryForRowSet(sql, params);

    Tips1: jdbcTemplate有很多的ORM化回調(diào)操作將返回結(jié)果轉(zhuǎn)為對(duì)象列表,但很多時(shí)候還是需要返回ResultSet,Spring有提供一個(gè)類似ResultSet的 Spring SqlRowSet對(duì)象。

             

    Tips2:.注意jdbcTemplate盡量只執(zhí)行查詢操作,莫要進(jìn)行更新,否則很容易破壞Hibernate的二級(jí)緩存體系。


    Chapter 11. 使用JDBC進(jìn)行數(shù)據(jù)訪問

    11.1. 簡(jiǎn)介

    Spring JDBC抽象框架所帶來的價(jià)值將在以下幾個(gè)方面得以體現(xiàn):(注:使用了Spring JDBC抽象框架之后,應(yīng)用開發(fā)人員只需要完成斜體字部分的編碼工作。)

    1. 指定數(shù)據(jù)庫連接參數(shù)

    2. 打開數(shù)據(jù)庫連接

    3. 聲明SQL語句

    4. 預(yù)編譯并執(zhí)行SQL語句

    5. 遍歷查詢結(jié)果(如果需要的話)

    6. 處理每一次遍歷操作

    7. 處理拋出的任何異常

    8. 處理事務(wù)

    9. 關(guān)閉數(shù)據(jù)庫連接

    Spring將替我們完成所有單調(diào)乏味的JDBC底層細(xì)節(jié)處理工作。

    11.1.1. Spring JDBC包結(jié)構(gòu)

    Spring JDBC抽象框架由四個(gè)包構(gòu)成:coredataSourceobject以及support

    org.springframework.jdbc.core包由JdbcTemplate類以及相關(guān)的回調(diào)接口(callback interface)和類組成。

    org.springframework.jdbc.datasource包由一些用來簡(jiǎn)化DataSource訪問的工具類,以及各種DataSource接口的簡(jiǎn)單實(shí)現(xiàn)(主要用于單元測(cè)試以及在J2EE容器之外使用JDBC)組成。工具類提供了一些靜態(tài)方法,諸如通過JNDI獲取數(shù)據(jù)連接以及在必要的情況下關(guān)閉這些連接。它支持綁定線程的連接,比如被用于DataSourceTransactionManager的連接。

    接下來,org.springframework.jdbc.object包由封裝了查詢、更新以及存儲(chǔ)過程的類組成,這些類的對(duì)象都是線程安全并且可重復(fù)使用的。它們類似于JDO,與JDO的不同之處在于查詢結(jié)果與數(shù)據(jù)庫是“斷開連接”的。它們是在org.springframework.jdbc.core包的基礎(chǔ)上對(duì)JDBC更高層次的抽象。

    最后,org.springframework.jdbc.support包提供了一些SQLException的轉(zhuǎn)換類以及相關(guān)的工具類。

    在JDBC處理過程中拋出的異常將被轉(zhuǎn)換成org.springframework.dao包中定義的異常。因此使用Spring JDBC進(jìn)行開發(fā)將不需要處理JDBC或者特定的RDBMS才會(huì)拋出的異常。所有的異常都是unchecked exception,這樣我們就可以對(duì)傳遞到調(diào)用者的異常進(jìn)行有選擇的捕獲。

    11.2. 利用JDBC核心類實(shí)現(xiàn)JDBC的基本操作和錯(cuò)誤處理

    11.2.1. JdbcTemplate

    JdbcTemplate是core包的核心類。它替我們完成了資源的創(chuàng)建以及釋放工作,從而簡(jiǎn)化了我們對(duì)JDBC的使用。它還可以幫助我們避免一些常見的錯(cuò)誤,比如忘記關(guān)閉數(shù)據(jù)庫連接。JdbcTemplate將完成JDBC核心處理流程,比如SQL語句的創(chuàng)建、執(zhí)行,而把SQL語句的生成以及查詢結(jié)果的提取工作留給我們的應(yīng)用代碼。它可以完成SQL查詢、更新以及調(diào)用存儲(chǔ)過程,可以對(duì)ResultSet進(jìn)行遍歷并加以提取。它還可以捕獲JDBC異常并將其轉(zhuǎn)換成org.springframework.dao包中定義的,通用的,信息更豐富的異常。

    使用JdbcTemplate進(jìn)行編碼只需要根據(jù)明確定義的一組契約來實(shí)現(xiàn)回調(diào)接口。PreparedStatementCreator回調(diào)接口通過給定的Connection創(chuàng)建一個(gè)PreparedStatement,包含SQL和任何相關(guān)的參數(shù)。CallableStatementCreateor實(shí)現(xiàn)同樣的處理,只不過它創(chuàng)建的是CallableStatement。RowCallbackHandler接口則從數(shù)據(jù)集的每一行中提取值。

    我們可以在一個(gè)service實(shí)現(xiàn)類中通過傳遞一個(gè)DataSource引用來完成JdbcTemplate的實(shí)例化,也可以在application context中配置一個(gè)JdbcTemplate bean,來供service使用。需要注意的是DataSource在application context總是配制成一個(gè)bean,第一種情況下,DataSource bean將傳遞給service,第二種情況下DataSource bean傳遞給JdbcTemplate bean。因?yàn)镴dbcTemplate使用回調(diào)接口和SQLExceptionTranslator接口作為參數(shù),所以一般情況下沒有必要通過繼承JdbcTemplate來定義其子類。

    JdbcTemplate中使用的所有SQL將會(huì)以“DEBUG”級(jí)別記入日志(一般情況下日志的category是JdbcTemplate相應(yīng)的全限定類名,不過如果需要對(duì)JdbcTemplate進(jìn)行定制的話,可能是它的子類名)。

    11.2.2. NamedParameterJdbcTemplate

    NamedParameterJdbcTemplate類增加了在SQL語句中使用命名參數(shù)的支持。在此之前,在傳統(tǒng)的SQL語句中,參數(shù)都是用'?'占位符來表示的。 NamedParameterJdbcTemplate類內(nèi)部封裝了一個(gè)普通的JdbcTemplate,并作為其代理來完成大部分工作。下面的內(nèi)容主要針對(duì)NamedParameterJdbcTemplateJdbcTemplate的不同之處來加以說明,即如何在SQL語句中使用命名參數(shù)。

    通過下面的例子我們可以更好地了解NamedParameterJdbcTemplate的使用模式(在后面我們還有更好的使用方式)。

    // some JDBC-backed DAO class...
    public int countOfActorsByFirstName(String firstName) {
    String sql = "select count(0) from T_ACTOR where first_name = :first_name";
    NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(this.getDataSource());
    SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
    return template.queryForInt(sql, namedParameters);
    }

    在上面例子中,sql變量使用了命名參數(shù)占位符“first_name”,與其對(duì)應(yīng)的值存在namedParameters變量中(類型為MapSqlParameterSource)。

    如果你喜歡的話,也可以使用基于Map風(fēng)格的名值對(duì)將命名參數(shù)傳遞給NamedParameterJdbcTemplateNamedParameterJdbcTemplate實(shí)現(xiàn)了NamedParameterJdbcOperations接口,剩下的工作將由調(diào)用該接口的相應(yīng)方法來完成,這里我們就不再贅述):

    // some JDBC-backed DAO class...
    public int countOfActorsByFirstName(String firstName) {
    String sql = "select count(0) from T_ACTOR where first_name = :first_name";
    NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(this.getDataSource());
    Map namedParameters = new HashMap();
    namedParameters.put("first_name", firstName);
    return template.queryForInt(sql, namedParameters);
    }

    另外一個(gè)值得一提的特性是與NamedParameterJdbcTemplate位于同一個(gè)包中的SqlParameterSource接口。在前面的代碼片斷中我們已經(jīng)看到了該接口的實(shí)現(xiàn)(即MapSqlParameterSource類),SqlParameterSource可以用來作為NamedParameterJdbcTemplate命名參數(shù)的來源。MapSqlParameterSource類是一個(gè)非常簡(jiǎn)單的實(shí)現(xiàn),它僅僅是一個(gè)java.util.Map適配器,當(dāng)然其用法也就不言自明了(如果還有不明了的,可以在Spring的JIRA系統(tǒng)中要求提供更多的相關(guān)資料)。

    SqlParameterSource接口的另一個(gè)實(shí)現(xiàn)--BeanPropertySqlParameterSource為我們提供了更有趣的功能。該類包裝一個(gè)類似JavaBean的對(duì)象,所需要的命名參數(shù)值將由包裝對(duì)象提供,下面我們使用一個(gè)例子來更清楚地說明它的用法。

    // some JavaBean-like class...
    public class Actor {
    private Long id;
    private String firstName;
    private String lastName;
    public String getFirstName() {
    return this.firstName;
    }
    public String getLastName() {
    return this.lastName;
    }
    public Long getId() {
    return this.id;
    }
    // setters omitted...
    }
    // some JDBC-backed DAO class...
    public int countOfActors(Actor exampleActor) {
    // notice how the named parameters match the properties of the above 'Actor' class
    String sql = "select count(0) from T_ACTOR where first_name = :firstName and last_name = :lastName";
    NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(this.getDataSource());
    SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
    return template.queryForInt(sql, namedParameters);
    }

    大家必須牢記一點(diǎn):NamedParameterJdbcTemplate類內(nèi)部包裝了一個(gè)標(biāo)準(zhǔn)的JdbcTemplate類。如果你需要訪問其內(nèi)部的JdbcTemplate實(shí)例(比如訪問JdbcTemplate的一些方法)那么你需要使用getJdbcOperations()方法返回的JdbcOperations接口。(JdbcTemplate實(shí)現(xiàn)了JdbcOperations接口)。

    NamedParameterJdbcTemplate類是線程安全的,該類的最佳使用方式不是每次操作的時(shí)候?qū)嵗粋€(gè)新的NamedParameterJdbcTemplate,而是針對(duì)每個(gè)DataSource只配置一個(gè)NamedParameterJdbcTemplate實(shí)例(比如在Spring IoC容器中使用Spring IoC來進(jìn)行配置),然后在那些使用該類的DAO中共享該實(shí)例。

    11.2.3. SimpleJdbcTemplate

    [Note] Note

    請(qǐng)注意該類所提供的功能僅適用于Java 5 (Tiger)。

    SimpleJdbcTemplate類是JdbcTemplate類的一個(gè)包裝器(wrapper),它利用了Java 5的一些語言特性,比如Varargs和Autoboxing。對(duì)那些用慣了Java 5的程序員,這些新的語言特性還是很好用的。

    SimpleJdbcTemplate 類利用Java 5的語法特性帶來的好處可以通過一個(gè)例子來說明。在下面的代碼片斷中我們首先使用標(biāo)準(zhǔn)的JdbcTemplate進(jìn)行數(shù)據(jù)訪問,接下來使用SimpleJdbcTemplate做同樣的事情。

    // classic JdbcTemplate-style...
    public Actor findActor(long id) {
    String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
    RowMapper mapper = new RowMapper() {
    public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
    Actor actor = new Actor();
    actor.setId(rs.getLong(Long.valueOf(rs.getLong("id"))));
    actor.setFirstName(rs.getString("first_name"));
    actor.setLastName(rs.getString("last_name"));
    return actor;
    }
    };
    // normally this would be dependency injected of course...
    JdbcTemplate jdbcTemplate = new JdbcTemplate(this.getDataSource());
    // notice the cast, and the wrapping up of the 'id' argument
    // in an array, and the boxing of the 'id' argument as a reference type
    return (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)});
    }

    下面是同一方法的另一種實(shí)現(xiàn),惟一不同之處是我們使用了SimpleJdbcTemplate,這樣代碼顯得更加清晰。

    // SimpleJdbcTemplate-style...
    public Actor findActor(long id) {
    String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
    ParameterizedRowMapper<Actor> mapper = new ParameterizedRowMapper<Actor>() {
    // notice the return type with respect to Java 5 covariant return types
    public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
    Actor actor = new Actor();
    actor.setId(rs.getLong("id"));
    actor.setFirstName(rs.getString("first_name"));
    actor.setLastName(rs.getString("last_name"));
    return actor;
    }
    };
    // again, normally this would be dependency injected of course...
    SimpleJdbcTemplate simpleJdbcTemplate = new SimpleJdbcTemplate(this.getDataSource());
    return simpleJdbcTemplate.queryForObject(sql, mapper, id);
    }

    11.2.4. DataSource接口

    為了從數(shù)據(jù)庫中取得數(shù)據(jù),我們首先需要獲取一個(gè)數(shù)據(jù)庫連接。 Spring通過DataSource對(duì)象來完成這個(gè)工作。 DataSource是JDBC規(guī)范的一部分, 它被視為一個(gè)通用的數(shù)據(jù)庫連接工廠。通過使用DataSource, Container或Framework可以將連接池以及事務(wù)管理的細(xì)節(jié)從應(yīng)用代碼中分離出來。 作為一個(gè)開發(fā)人員,在開發(fā)和測(cè)試產(chǎn)品的過程中,你可能需要知道連接數(shù)據(jù)庫的細(xì)節(jié)。 但在產(chǎn)品實(shí)施時(shí),你不需要知道這些細(xì)節(jié)。通常數(shù)據(jù)庫管理員會(huì)幫你設(shè)置好數(shù)據(jù)源。

    在使用Spring JDBC時(shí),你既可以通過JNDI獲得數(shù)據(jù)源,也可以自行配置數(shù)據(jù)源( 使用Spring提供的DataSource實(shí)現(xiàn)類)。使用后者可以更方便的脫離Web容器來進(jìn)行單元測(cè)試。 這里我們將使用DriverManagerDataSource,不過DataSource有多種實(shí)現(xiàn), 后面我們會(huì)講到。使用DriverManagerDataSource和你以前獲取一個(gè)JDBC連接 的做法沒什么兩樣。你首先必須指定JDBC驅(qū)動(dòng)程序的全限定名,這樣DriverManager 才能加載JDBC驅(qū)動(dòng)類,接著你必須提供一個(gè)url(因JDBC驅(qū)動(dòng)而異,為了保證設(shè)置正確請(qǐng)參考相關(guān)JDBC驅(qū)動(dòng)的文檔), 最后你必須提供一個(gè)用戶連接數(shù)據(jù)庫的用戶名和密碼。下面我們將通過一個(gè)例子來說明如何配置一個(gè) DriverManagerDataSource

    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
    dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
    dataSource.setUsername("sa");
    dataSource.setPassword("");

    11.2.5. SQLExceptionTranslator接口

    SQLExceptionTranslator是一個(gè)接口,如果你需要在 SQLExceptionorg.springframework.dao.DataAccessException之間作轉(zhuǎn)換,那么必須實(shí)現(xiàn)該接口。

    轉(zhuǎn)換器類的實(shí)現(xiàn)可以采用一般通用的做法(比如使用JDBC的SQLState code),如果為了使轉(zhuǎn)換更準(zhǔn)確,也可以進(jìn)行定制(比如使用Oracle的error code)。

    SQLErrorCodeSQLExceptionTranslator是SQLExceptionTranslator的默認(rèn)實(shí)現(xiàn)。 該實(shí)現(xiàn)使用指定數(shù)據(jù)庫廠商的error code,比采用SQLState更精確。 轉(zhuǎn)換過程基于一個(gè)JavaBean(類型為SQLErrorCodes)中的error code。 這個(gè)JavaBean由SQLErrorCodesFactory工廠類創(chuàng)建,其中的內(nèi)容來自于 "sql-error-codes.xml"配置文件。該文件中的數(shù)據(jù)庫廠商代碼基于Database MetaData信息中的 DatabaseProductName,從而配合當(dāng)前數(shù)據(jù)庫的使用。

     

    SQLErrorCodeSQLExceptionTranslator使用以下的匹配規(guī)則:

     

    • 首先檢查是否存在完成定制轉(zhuǎn)換的子類實(shí)現(xiàn)。通常SQLErrorCodeSQLExceptionTranslator 這個(gè)類可以作為一個(gè)具體類使用,不需要進(jìn)行定制,那么這個(gè)規(guī)則將不適用。

    • 接著將SQLException的error code與錯(cuò)誤代碼集中的error code進(jìn)行匹配。 默認(rèn)情況下錯(cuò)誤代碼集將從SQLErrorCodesFactory取得。 錯(cuò)誤代碼集來自classpath下的sql-error-codes.xml文件, 它們將與數(shù)據(jù)庫metadata信息中的database name進(jìn)行映射。

    • 如果仍然無法匹配,最后將調(diào)用fallbackTranslator屬性的translate方法,SQLStateSQLExceptionTranslator類實(shí)例是默認(rèn)的fallbackTranslator。

     

    SQLErrorCodeSQLExceptionTranslator可以采用下面的方式進(jìn)行擴(kuò)展:

    public class MySQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
    protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
    if (sqlex.getErrorCode() == -12345) {
    return new DeadlockLoserDataAccessException(task, sqlex);
    }
    return null;
    }
    }

    在上面的這個(gè)例子中,error code為'-12345'的SQLException 將采用該轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換,而其他的error code將由默認(rèn)的轉(zhuǎn)換器進(jìn)行轉(zhuǎn)換。 為了使用該轉(zhuǎn)換器,必須將其作為參數(shù)傳遞給JdbcTemplate類 的setExceptionTranslator方法,并在需要使用這個(gè)轉(zhuǎn)換器器的數(shù)據(jù) 存取操作中使用該JdbcTemplate。 下面的例子演示了如何使用該定制轉(zhuǎn)換器:

    // create a JdbcTemplate and set data source
    JdbcTemplate jt = new JdbcTemplate();
    jt.setDataSource(dataSource);
    // create a custom translator and set the DataSource for the default translation lookup
    MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator();
    tr.setDataSource(dataSource);
    jt.setExceptionTranslator(tr);
    // use the JdbcTemplate for this SqlUpdate
    SqlUpdate su = new SqlUpdate();
    su.setJdbcTemplate(jt);
    su.setSql("update orders set shipping_charge = shipping_charge * 1.05");
    su.compile();
    su.update();

    在上面的定制轉(zhuǎn)換器中,我們給它注入了一個(gè)數(shù)據(jù)源,因?yàn)槲覀內(nèi)匀恍枰?使用默認(rèn)的轉(zhuǎn)換器從sql-error-codes.xml中獲取錯(cuò)誤代碼集。

    11.2.6. 執(zhí)行SQL語句

    我們僅需要非常少的代碼就可以達(dá)到執(zhí)行SQL語句的目的,一旦獲得一個(gè) DataSource和一個(gè)JdbcTemplate, 我們就可以使用JdbcTemplate提供的豐富功能實(shí)現(xiàn)我們的操作。 下面的例子使用了極少的代碼完成創(chuàng)建一張表的工作。

    import javax.sql.DataSource;
    import org.springframework.jdbc.core.JdbcTemplate;
    public class ExecuteAStatement {
    private JdbcTemplate jt;
    private DataSource dataSource;
    public void doExecute() {
    jt = new JdbcTemplate(dataSource);
    jt.execute("create table mytable (id integer, name varchar(100))");
    }
    public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
    }
    }

    11.2.7. 執(zhí)行查詢

    除了execute方法之外,JdbcTemplate還提供了大量的查詢方法。 在這些查詢方法中,有很大一部分是用來查詢單值的。比如返回一個(gè)匯總(count)結(jié)果 或者從返回行結(jié)果中取得指定列的值。這時(shí)我們可以使用queryForInt(..)queryForLong(..)或者queryForObject(..)方法。 queryForObject方法用來將返回的JDBC類型對(duì)象轉(zhuǎn)換成指定的Java對(duì)象,如果類型轉(zhuǎn)換失敗將拋出 InvalidDataAccessApiUsageException異常。 下面的例子演示了兩個(gè)查詢的用法,一個(gè)返回int值,另一個(gè)返回 String

    import javax.sql.DataSource;
    import org.springframework.jdbc.core.JdbcTemplate;
    public class RunAQuery {
    private JdbcTemplate jt;
    private DataSource dataSource;
    public int getCount() {
    jt = new JdbcTemplate(dataSource);
    int count = jt.queryForInt("select count(*) from mytable");
    return count;
    }
    public String getName() {
    jt = new JdbcTemplate(dataSource);
    String name = (String) jt.queryForObject("select name from mytable", String.class);
    return name;
    }
    public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
    }
    }

    除了返回單值的查詢方法,JdbcTemplate還提供了一組返回List結(jié)果 的方法。List中的每一項(xiàng)對(duì)應(yīng)查詢返回結(jié)果中的一行。其中最簡(jiǎn)單的是queryForList方法, 該方法將返回一個(gè)List,該List中的每一條 記錄是一個(gè)Map對(duì)象,對(duì)應(yīng)應(yīng)數(shù)據(jù)庫中某一行;而該Map 中的每一項(xiàng)對(duì)應(yīng)該數(shù)據(jù)庫行中的某一列值。下面的代碼片斷接著上面的例子演示了如何用該方法返回表中 所有記錄:

    public List getList() {
    jt = new JdbcTemplate(dataSource);
    List rows = jt.queryForList("select * from mytable");
    return rows;
    }

    返回的結(jié)果集類似下面這種形式:

    [{name=Bob, id=1}, {name=Mary, id=2}]

    11.2.8. 更新數(shù)據(jù)庫

    JdbcTemplate還提供了一些更新數(shù)據(jù)庫的方法。 在下面的例子中,我們根據(jù)給定的主鍵值對(duì)指定的列進(jìn)行更新。 例子中的SQL語句中使用了“?”占位符來接受參數(shù)(這種做法在更新和查詢SQL語句中很常見)。 傳遞的參數(shù)值位于一個(gè)對(duì)象數(shù)組中(基本類型需要被包裝成其對(duì)應(yīng)的對(duì)象類型)。

    import javax.sql.DataSource;
    import org.springframework.jdbc.core.JdbcTemplate;
    public class ExecuteAnUpdate {
    private JdbcTemplate jt;
    private DataSource dataSource;
    public void setName(int id, String name) {
    jt = new JdbcTemplate(dataSource);
    jt.update("update mytable set name = ? where id = ?", new Object[] {name, new Integer(id)});
    }
    public void setDataSource(DataSource dataSource) {
    this.dataSource = dataSource;
    }
    }

    11.3. 控制數(shù)據(jù)庫連接

    11.3.1. DataSourceUtils

    DataSourceUtils作為一個(gè)幫助類提供易用且強(qiáng)大的數(shù)據(jù)庫訪問能力, 我們可以使用該類提供的靜態(tài)方法從JNDI獲取數(shù)據(jù)庫連接以及在必要的時(shí)候關(guān)閉之。 它提供支持線程綁定的數(shù)據(jù)庫連接(比如使用DataSourceTransactionManager 的時(shí)候,將把數(shù)據(jù)庫連接綁定到當(dāng)前的線程上)。

    注:getDataSourceFromJndi(..)方法主要用于那些沒有使用bean factory 或者application context的場(chǎng)合。如果使用application context,那么最好是在 JndiObjectFactoryBean中配置bean或者直接使用 JdbcTemplate實(shí)例。JndiObjectFactoryBean 能夠通過JNDI獲取DataSource并將 DataSource作為引用參數(shù)傳遞給其他bean。 這樣,在不同的DataSource之間切換只需要修改配置文件即可, 甚至我們可以用一個(gè)非JNDI的DataSource來替換 FactoryBean定義!

    11.3.2. SmartDataSource接口

    SmartDataSourceDataSource 接口的一個(gè)擴(kuò)展,用來提供數(shù)據(jù)庫連接。使用該接口的類在指定的操作之后可以檢查是否需要關(guān)閉連接。 該接口在某些情況下非常有用,比如有些情況需要重用數(shù)據(jù)庫連接。

    11.3.3. AbstractDataSource

    AbstractDataSource是一個(gè)實(shí)現(xiàn)了DataSource 接口的abstract基類。它實(shí)現(xiàn)了DataSource接口的 一些無關(guān)痛癢的方法,如果你需要實(shí)現(xiàn)自己的DataSource,那么繼承 該類是個(gè)好主意。

    11.3.4. SingleConnectionDataSource

    SingleConnectionDataSourceSmartDataSource接口 的一個(gè)實(shí)現(xiàn),其內(nèi)部包裝了一個(gè)單連接。該連接在使用之后將不會(huì)關(guān)閉,很顯然它不能在多線程 的環(huán)境下使用。

    當(dāng)客戶端代碼調(diào)用close方法的時(shí)候,如果它總是假設(shè)數(shù)據(jù)庫連接來自連接池(就像使用持久化工具時(shí)一樣), 你應(yīng)該將suppressClose設(shè)置為true。 這樣,通過該類獲取的將是代理連接(禁止關(guān)閉)而不是原有的物理連接。 需要注意的是,我們不能把使用該類獲取的數(shù)據(jù)庫連接造型(cast)為Oracle Connection之類的本地?cái)?shù)據(jù)庫連接。

    SingleConnectionDataSource主要在測(cè)試的時(shí)候使用。 它使得測(cè)試代碼很容易脫離應(yīng)用服務(wù)器而在一個(gè)簡(jiǎn)單的JNDI環(huán)境下運(yùn)行。 與DriverManagerDataSource不同的是,它始終只會(huì)使用同一個(gè)數(shù)據(jù)庫連接, 從而避免每次建立物理連接的開銷。

    11.3.5. DriverManagerDataSource

    DriverManagerDataSource類實(shí)現(xiàn)了 SmartDataSource接口。在applicationContext.xml中可以使用 bean properties來設(shè)置JDBC Driver屬性,該類每次返回的都是一個(gè)新的連接。

    該類主要在測(cè)試以及脫離J2EE容器的獨(dú)立環(huán)境中使用。它既可以用來在application context中作為一個(gè) DataSource bean,也可以在簡(jiǎn)單的JNDI環(huán)境下使用。 由于Connection.close()僅僅只是簡(jiǎn)單的關(guān)閉數(shù)據(jù)庫連接,因此任何能夠獲取 DataSource的持久化代碼都能很好的工作。不過使用JavaBean風(fēng)格的連接池 (比如commons-dbcp)也并非難事。即使是在測(cè)試環(huán)境下,使用連接池也是一種比使用 DriverManagerDataSource更好的做法。

    11.3.6. TransactionAwareDataSourceProxy

    TransactionAwareDataSourceProxy作為目標(biāo)DataSource的一個(gè)代理, 在對(duì)目標(biāo)DataSource包裝的同時(shí),還增加了Spring的事務(wù)管理能力, 在這一點(diǎn)上,這個(gè)類的功能非常像J2EE服務(wù)器所提供的事務(wù)化的JNDI DataSource

    [Note] Note

    該類幾乎很少被用到,除非現(xiàn)有代碼在被調(diào)用的時(shí)候需要一個(gè)標(biāo)準(zhǔn)的 JDBC DataSource接口實(shí)現(xiàn)作為參數(shù)。 這種情況下,這個(gè)類可以使現(xiàn)有代碼參與Spring的事務(wù)管理。通常最好的做法是使用更高層的抽象 來對(duì)數(shù)據(jù)源進(jìn)行管理,比如JdbcTemplateDataSourceUtils等等。

    如果需要更詳細(xì)的資料,請(qǐng)參考TransactionAwareDataSourceProxy JavaDoc 。

    11.3.7. DataSourceTransactionManager

    DataSourceTransactionManager類是 PlatformTransactionManager接口的一個(gè)實(shí)現(xiàn),用于處理單JDBC數(shù)據(jù)源。 它將從指定DataSource取得的JDBC連接綁定到當(dāng)前線程,因此它也支持了每個(gè)數(shù)據(jù)源對(duì)應(yīng)到一個(gè)線程。

    我們推薦在應(yīng)用代碼中使用DataSourceUtils.getConnection(DataSource)來獲取 JDBC連接,而不是使用J2EE標(biāo)準(zhǔn)的DataSource.getConnection。因?yàn)榍罢邔伋?unchecked的org.springframework.dao異常,而不是checked的 SQLException異常。Spring Framework中所有的類(比如 JdbcTemplate)都采用這種做法。如果不需要和這個(gè) DataSourceTransactionManager類一起使用,DataSourceUtils 提供的功能跟一般的數(shù)據(jù)庫連接策略沒有什么兩樣,因此它可以在任何場(chǎng)景下使用。

    DataSourceTransactionManager類支持定制隔離級(jí)別,以及對(duì)SQL語句查詢超時(shí)的設(shè)定。 為了支持后者,應(yīng)用代碼必須使用JdbcTemplate或者在每次創(chuàng)建SQL語句時(shí)調(diào)用 DataSourceUtils.applyTransactionTimeout方法。

    在使用單個(gè)數(shù)據(jù)源的情形下,你可以用DataSourceTransactionManager來替代JtaTransactionManager, 因?yàn)?tt class="classname">DataSourceTransactionManager不需要容器支持JTA。如果你使用DataSourceUtils.getConnection(DataSource)來獲取 JDBC連接,二者之間的切換只需要更改一些配置。最后需要注意的一點(diǎn)就是JtaTransactionManager不支持隔離級(jí)別的定制!

    11.4. 用Java對(duì)象來表達(dá)JDBC操作

    org.springframework.jdbc.object包下的類允許用戶以更加 面向?qū)ο蟮姆绞饺ピL問數(shù)據(jù)庫。比如說,用戶可以執(zhí)行查詢并返回一個(gè)list, 該list作為一個(gè)結(jié)果集將把從數(shù)據(jù)庫中取出的列數(shù)據(jù)映射到業(yè)務(wù)對(duì)象的屬性上。 用戶也可以執(zhí)行存儲(chǔ)過程,以及運(yùn)行更新、刪除以及插入SQL語句。

    [Note] Note

    在許多Spring開發(fā)人員中間存在有一種觀點(diǎn),那就是下面將要提到的各種RDBMS操作類 (StoredProcedure類除外) 通常也可以直接使用JdbcTemplate相關(guān)的方法來替換。 相對(duì)于把一個(gè)查詢操作封裝成一個(gè)類而言,直接調(diào)用JdbcTemplate方法將更簡(jiǎn)單 而且更容易理解。

    必須說明的一點(diǎn)就是,這僅僅只是一種觀點(diǎn)而已, 如果你認(rèn)為你可以從直接使用RDBMS操作類中獲取一些額外的好處, 你不妨根據(jù)自己的需要和喜好進(jìn)行不同的選擇。

    11.4.1. SqlQuery

    SqlQuery是一個(gè)可重用、線程安全的類,它封裝了一個(gè)SQL查詢。 其子類必須實(shí)現(xiàn)newResultReader()方法,該方法用來在遍歷 ResultSet的時(shí)候能使用一個(gè)類來保存結(jié)果。 我們很少需要直接使用SqlQuery,因?yàn)槠渥宇?MappingSqlQuery作為一個(gè)更加易用的實(shí)現(xiàn)能夠?qū)⒔Y(jié)果集中的行映射為Java對(duì)象。 SqlQuery還有另外兩個(gè)擴(kuò)展分別是 MappingSqlQueryWithParametersUpdatableSqlQuery

    11.4.2. MappingSqlQuery

    MappingSqlQuery是一個(gè)可重用的查詢抽象類,其具體類必須實(shí)現(xiàn) mapRow(ResultSet, int)抽象方法來將結(jié)果集中的每一行轉(zhuǎn)換成Java對(duì)象。

    SqlQuery的各種實(shí)現(xiàn)中, MappingSqlQuery是最常用也是最容易使用的一個(gè)。

    下面這個(gè)例子演示了一個(gè)定制查詢,它將從客戶表中取得的數(shù)據(jù)映射到一個(gè) Customer類實(shí)例。

    private class CustomerMappingQuery extends MappingSqlQuery {
    public CustomerMappingQuery(DataSource ds) {
    super(ds, "SELECT id, name FROM customer WHERE id = ?");
    super.declareParameter(new SqlParameter("id", Types.INTEGER));
    compile();
    }
    public Object mapRow(ResultSet rs, int rowNumber) throws SQLException {
    Customer cust = new Customer();
    cust.setId((Integer) rs.getObject("id"));
    cust.setName(rs.getString("name"));
    return cust;
    }
    }

    在上面的例子中,我們?yōu)橛脩舨樵兲峁┝艘粋€(gè)構(gòu)造函數(shù)并為構(gòu)造函數(shù)傳遞了一個(gè) DataSource參數(shù)。在構(gòu)造函數(shù)里面我們把 DataSource和一個(gè)用來返回查詢結(jié)果的SQL語句作為參數(shù) 調(diào)用父類的構(gòu)造函數(shù)。SQL語句將被用于生成一個(gè)PreparedStatement對(duì)象, 因此它可以包含占位符來傳遞參數(shù)。而每一個(gè)SQL語句的參數(shù)必須通過調(diào)用 declareParameter方法來進(jìn)行聲明,該方法需要一個(gè) SqlParameter(封裝了一個(gè)字段名字和一個(gè) java.sql.Types中定義的JDBC類型)對(duì)象作為參數(shù)。 所有參數(shù)定義完之后,我們調(diào)用compile()方法來對(duì)SQL語句進(jìn)行預(yù)編譯。

    下面讓我們看看該定制查詢初始化并執(zhí)行的代碼:

    public Customer getCustomer(Integer id) {
    CustomerMappingQuery custQry = new CustomerMappingQuery(dataSource);
    Object[] parms = new Object[1];
    parms[0] = id;
    List customers = custQry.execute(parms);
    if (customers.size() > 0) {
    return (Customer) customers.get(0);
    }
    else {
    return null;
    }
    }

    在上面的例子中,getCustomer方法通過傳遞惟一參數(shù)id來返回一個(gè)客戶對(duì)象。 該方法內(nèi)部在創(chuàng)建CustomerMappingQuery實(shí)例之后, 我們創(chuàng)建了一個(gè)對(duì)象數(shù)組用來包含要傳遞的查詢參數(shù)。這里我們只有唯一的一個(gè) Integer參數(shù)。執(zhí)行CustomerMappingQuery的 execute方法之后,我們得到了一個(gè)List,該List中包含一個(gè) Customer對(duì)象,如果有對(duì)象滿足查詢條件的話。

    11.4.3. SqlUpdate

    SqlUpdate類封裝了一個(gè)可重復(fù)使用的SQL更新操作。 跟所有RdbmsOperation類一樣,SqlUpdate可以在SQL中定義參數(shù)。

    該類提供了一系列update()方法,就像SqlQuery提供的一系列execute()方法一樣。

    SqlUpdate是一個(gè)具體的類。通過在SQL語句中定義參數(shù),這個(gè)類可以支持 不同的更新方法,我們一般不需要通過繼承來實(shí)現(xiàn)定制。

    import java.sql.Types;
    import javax.sql.DataSource;
    import org.springframework.jdbc.core.SqlParameter;
    import org.springframework.jdbc.object.SqlUpdate;
    public class UpdateCreditRating extends SqlUpdate {
    public UpdateCreditRating(DataSource ds) {
    setDataSource(ds);
    setSql("update customer set credit_rating = ? where id = ?");
    declareParameter(new SqlParameter(Types.NUMERIC));
    declareParameter(new SqlParameter(Types.NUMERIC));
    compile();
    }
    /**
    * @param id for the Customer to be updated
    * @param rating the new value for credit rating
    * @return number of rows updated
    */
    public int run(int id, int rating) {
    Object[] params =
    new Object[] {
    new Integer(rating),
    new Integer(id)};
    return update(params);
    }
    }

    11.4.4. StoredProcedure

    StoredProcedure類是一個(gè)抽象基類,它是對(duì)RDBMS存儲(chǔ)過程的一種抽象。 該類提供了多種execute(..)方法,不過這些方法的訪問類型都是protected的。

    從父類繼承的sql屬性用來指定RDBMS存儲(chǔ)過程的名字。 盡管該類提供了許多必須在JDBC3.0下使用的功能,但是我們更關(guān)注的是JDBC 3.0中引入的命名參數(shù)特性。

    下面的程序演示了如何調(diào)用Oracle中的sysdate()函數(shù)。 這里我們創(chuàng)建了一個(gè)繼承StoredProcedure的子類,雖然它沒有輸入?yún)?shù), 但是我必須通過使用SqlOutParameter來聲明一個(gè)日期類型的輸出參數(shù)。 execute()方法將返回一個(gè)map,map中的每個(gè)entry是一個(gè)用參數(shù)名作key, 以輸出參數(shù)為value的名值對(duì)。

    import java.sql.Types;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import javax.sql.DataSource;
    import org.springframework.jdbc.core.SqlOutParameter;
    import org.springframework.jdbc.datasource.*;
    import org.springframework.jdbc.object.StoredProcedure;
    public class TestStoredProcedure {
    public static void main(String[] args)  {
    TestStoredProcedure t = new TestStoredProcedure();
    t.test();
    System.out.println("Done!");
    }
    void test() {
    DriverManagerDataSource ds = new DriverManagerDataSource();
    ds.setDriverClassName("oracle.jdbc.OracleDriver");
    ds.setUrl("jdbc:oracle:thin:@localhost:1521:mydb");
    ds.setUsername("scott");
    ds.setPassword("tiger");
    MyStoredProcedure sproc = new MyStoredProcedure(ds);
    Map results = sproc.execute();
    printMap(results);
    }
    private class MyStoredProcedure extends StoredProcedure {
    private static final String SQL = "sysdate";
    public MyStoredProcedure(DataSource ds) {
    setDataSource(ds);
    setFunction(true);
    setSql(SQL);
    declareParameter(new SqlOutParameter("date", Types.DATE));
    compile();
    }
    public Map execute() {
    // the 'sysdate' sproc has no input parameters, so an empty Map is supplied...
    return execute(new HashMap());
    }
    }
    private static void printMap(Map results) {
    for (Iterator it = results.entrySet().iterator(); it.hasNext(); ) {
    System.out.println(it.next());
    }
    }
    }

    下面是StoredProcedure的另一個(gè)例子,它使用了兩個(gè)Oracle游標(biāo)類型的輸出參數(shù)。

    import oracle.jdbc.driver.OracleTypes;
    import org.springframework.jdbc.core.SqlOutParameter;
    import org.springframework.jdbc.object.StoredProcedure;
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    public class TitlesAndGenresStoredProcedure extends StoredProcedure {
    private static final String SPROC_NAME = "AllTitlesAndGenres";
    public TitlesAndGenresStoredProcedure(DataSource dataSource) {
    super(dataSource, SPROC_NAME);
    declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
    declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper()));
    compile();
    }
    public Map execute() {
    // again, this sproc has no input parameters, so an empty Map is supplied...
    return super.execute(new HashMap());
    }
    }

    值得注意的是TitlesAndGenresStoredProcedure構(gòu)造函數(shù)中 declareParameter(..)SqlOutParameter參數(shù), 該參數(shù)使用了RowMapper接口的實(shí)現(xiàn)。 這是一種非常方便而強(qiáng)大的重用方式。 下面我們來看一下RowMapper的兩個(gè)具體實(shí)現(xiàn)。

    首先是TitleMapper類,它簡(jiǎn)單的把ResultSet中的每一行映射為一個(gè)Title Domain Object。

    import com.foo.sprocs.domain.Title;
    import org.springframework.jdbc.core.RowMapper;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    public final class TitleMapper implements RowMapper {
    public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
    Title title = new Title();
    title.setId(rs.getLong("id"));
    title.setName(rs.getString("name"));
    return title;
    }
    }

    另一個(gè)是GenreMapper類,也是非常簡(jiǎn)單的將ResultSet中的每一行映射為一個(gè)Genre Domain Object。

    import org.springframework.jdbc.core.RowMapper;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import com.foo.domain.Genre;
    public final class GenreMapper implements RowMapper {
    public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
    return new Genre(rs.getString("name"));
    }
    }

    如果你需要給存儲(chǔ)過程傳輸入?yún)?shù)(這些輸入?yún)?shù)是在RDBMS存儲(chǔ)過程中定義好了的), 則需要提供一個(gè)指定類型的execute(..)方法, 該方法將調(diào)用基類的protected execute(Map parameters)方法。 例如:

    import oracle.jdbc.driver.OracleTypes;
    import org.springframework.jdbc.core.SqlOutParameter;
    import org.springframework.jdbc.object.StoredProcedure;
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    public class TitlesAfterDateStoredProcedure extends StoredProcedure {
    private static final String SPROC_NAME = "TitlesAfterDate";
    private static final String CUTOFF_DATE_PARAM = "cutoffDate";
    public TitlesAfterDateStoredProcedure(DataSource dataSource) {
    super(dataSource, SPROC_NAME);
    declaraParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE);
    declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
    compile();
    }
    public Map execute(Date cutoffDate) {
    Map inputs = new HashMap();
    inputs.put(CUTOFF_DATE_PARAM, cutoffDate);
    return super.execute(inputs);
    }
    }

    11.4.5. SqlFunction

    SqlFunction RDBMS操作類封裝了一個(gè)SQL“函數(shù)”包裝器(wrapper), 該包裝器適用于查詢并返回一個(gè)單行結(jié)果集。默認(rèn)返回的是一個(gè)int值, 不過我們可以采用類似JdbcTemplate中的queryForXxx 做法自己實(shí)現(xiàn)來返回其它類型。SqlFunction優(yōu)勢(shì)在于我們不必創(chuàng)建 JdbcTemplate,這些它都在內(nèi)部替我們做了。

    該類的主要用途是調(diào)用SQL函數(shù)來返回一個(gè)單值的結(jié)果集,比如類似“select user()”、 “select sysdate from dual”的查詢。如果需要調(diào)用更復(fù)雜的存儲(chǔ)函數(shù), 可以使用StoredProcedureSqlCall

    SqlFunction是一個(gè)具體類,通常我們不需要它的子類。 其用法是創(chuàng)建該類的實(shí)例,然后聲明SQL語句以及參數(shù)就可以調(diào)用相關(guān)的run方法去多次執(zhí)行函數(shù)。 下面的例子用來返回指定表的記錄行數(shù):

    public int countRows() {
    SqlFunction sf = new SqlFunction(dataSource, "select count(*) from mytable");
    sf.compile();
    return sf.run();
    }
    posted @ 2008-08-12 15:31 飛飛 閱讀(3951) | 評(píng)論 (2)編輯 收藏

    僅列出標(biāo)題
    共12頁: 上一頁 1 2 3 4 5 6 7 8 9 下一頁 Last 
    主站蜘蛛池模板: 久久久久久久久久免免费精品| 亚洲高清无码在线观看| 成人一区二区免费视频| 亚洲成av人无码亚洲成av人| 亚洲一区二区三区日本久久九| 亚洲w码欧洲s码免费| 免费人成视频在线| 无人在线观看免费高清| 一级做a爰性色毛片免费| 亚洲AV无码一区二区三区久久精品 | 久久精品亚洲一区二区三区浴池 | 亚洲色偷精品一区二区三区| 日韩亚洲欧洲在线com91tv| 亚洲国产精品嫩草影院久久 | 色婷婷综合缴情综免费观看| 春暖花开亚洲性无区一区二区| 91亚洲视频在线观看| 91亚洲一区二区在线观看不卡| 亚洲AV无码一区二区三区DV| 亚洲一区二区三区免费| 亚洲国产黄在线观看| 免费h成人黄漫画嘿咻破解版| 日美韩电影免费看| 巨胸喷奶水视频www网免费| 免费黄色大片网站| 成年女人毛片免费播放视频m| 一个人免费观看视频www| 国产免费的野战视频| 丁香花免费高清视频完整版| 国产福利视精品永久免费| 69成人免费视频| 国产精品69白浆在线观看免费| 波多野结衣免费在线| 九九精品免费视频| 成年人视频在线观看免费| 成年人性生活免费视频| 男女啪啪永久免费观看网站| 国产免费久久精品| 国产成人亚洲综合无码| 亚洲精品~无码抽插| 日本久久久久亚洲中字幕|