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

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

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

    Java, Only Java!

    統(tǒng)計(jì)

    留言簿(20)

    積分與排名

    好友空間

    文檔技巧

    閱讀排行榜

    評論排行榜

    金蝶Apusic應(yīng)用服務(wù)器的數(shù)據(jù)源管理(轉(zhuǎn))

    1.?????????? 前言

    在基于 J2EE 平臺的應(yīng)用開發(fā)中,大多數(shù)的應(yīng)用都需要跟數(shù)據(jù)庫打交道;而自從接觸 JDBC 起,我們便不止一次的被告之:數(shù)據(jù)庫資源是十分寶貴的系統(tǒng)資源,一定要謹(jǐn)慎使用。但令人遺憾的是,在筆者見過的大部分跟數(shù)據(jù)庫相關(guān)的應(yīng)用開發(fā)中,針對數(shù)據(jù)庫資源的使用總是充斥著這樣或者那樣的問題。在本文中,筆者針對常見的一些錯(cuò)誤或者不當(dāng)?shù)氖褂脭?shù)據(jù)庫資源的案例進(jìn)行介紹與分析,并闡述金蝶 Apusic 應(yīng)用服務(wù)器提供的一些增值特性,通過這些特性能夠有效的避免某些錯(cuò)誤的發(fā)生。

    2.?????????? 常見數(shù)據(jù)庫資源錯(cuò)誤/不當(dāng)用法的案例分析

    2.1.? 未正確的關(guān)閉數(shù)據(jù)庫連接

    申請了數(shù)據(jù)庫連接,卻沒有及時(shí)的關(guān)閉它,這幾乎是最常見的數(shù)據(jù)庫連接使用錯(cuò)誤。犯這種錯(cuò)誤的原因有很多,以下是常見的一種低級錯(cuò)誤:

    publicvoidfoo(){

    ?????? Connectionconn=getConnection();

    ?????? Statementstmt=null;

    ?????? try{

    ?????? conn=getConnection();

    ?????? stmt=conn.createStatement();

    ?????? }catch(Exceptione){

    ?????? }finally{

    ?????? close(stmt,conn);

    ?????? }

    }

    < 示例代碼一 >

    在上述案例中的第 2 行代碼中,作者已經(jīng)申請了一個(gè) Connection ,但在第 5 行代碼中,又申請了一個(gè)新的 Connection ,并且丟失了第一次申請的 connection 的引用,至此,當(dāng)程序每調(diào)一次 foo 方法,將導(dǎo)致申請一個(gè)新的 Connection 而沒有釋放它,如此一來,當(dāng)數(shù)據(jù)庫達(dá)到能夠承受的最大連接數(shù)時(shí),將導(dǎo)致整個(gè)應(yīng)用的運(yùn)行失敗。

    避免這種錯(cuò)誤的方法有很多,譬如,可采用類似于

    T_SEQUENCE

    NAME

    CURRENT_VAL

    CUSTOMER

    10

    ?

    T_CUSTOMER

    ID

    CUSTOMER_NAME

    9

    Kevin

    10

    Mary

    ?

    于是,在 Java 語言中,我們以面向?qū)ο蟮姆椒▉韺?shí)現(xiàn),可能會是這樣(常見寫法,未必是最優(yōu)實(shí)現(xiàn)):

    publicclassCustomer{

    publicvoidsequencePlus(){
    Connectionconn=null;
    Statementstmt=null;
    try{
    conn=getConnection();
    stmt=conn.createStatement();
    Stringsql="updateT_SEQUENCEsetCURRENT_VAL

    =CURRENT_VAL+1"
    +"whereNAME='CUSTOMER'";
    stmt.execute(sql);
    }catch(Exceptione){
    e.printStackTrace();
    }finally{
    DbUtils.closeQuietly(stmt);
    DbUtils.closeQuietly(conn);
    }
    }

    publicintgetSequenceCurrentVal(){
    Connectionconn=null;
    Statementstmt=null;
    ResultSetrset=null;
    intid=0;
    try{
    conn=getConnection();
    stmt=conn.createStatement();
    Stringsql="selectCURRENT_VALfromT_SEQUENCE

    whereNAME='CUSTOMER'";
    rset=stmt.executeQuery(sql);
    if(rset.next()){
    id=rset.getInt(1);
    }
    }catch(Exceptione){
    e.printStackTrace();
    }finally{
    DbUtils.closeQuietly(conn,stmt,rset);
    }
    returnid;
    }

    publicvoidaddCustomer(Stringname){
    Connectionconn=null;
    PreparedStatementstmt=null;
    ResultSetrset=null;
    try{
    sequencePlus();
    intid=getSequenceCurrentVal();
    conn=getConnection();
    stmt=conn.prepareStatement(
    "insertintoT_CUSTOMER(ID,CUSTOMER_NAME)values(?,?)");
    stmt.setInt(1,id);
    stmt.setString(2,name==null?"":name);
    stmt.execute();
    }catch(Exceptione){
    e.printStackTrace();
    }finally{
    DbUtils.closeQuietly(stmt);
    DbUtils.closeQuietly(conn);
    }
    }
    }

    < 示例代碼四 >

    針對這種應(yīng)用場景,我們首先需要認(rèn)識到:上述的三個(gè)方法應(yīng)該屬于同一個(gè)數(shù)據(jù)庫事務(wù),否則,在并發(fā)情況下,將出現(xiàn)由于主鍵重復(fù)而導(dǎo)致數(shù)據(jù)插入失敗的情況。但同時(shí),我們也需要看到:即便上述三個(gè)方法的執(zhí)行位于同一個(gè)事務(wù)中,但三個(gè)方法使用的是不同的數(shù)據(jù)庫連接,雖然在 sequencePlus 方法中將 T_SEQUENCE 表中的數(shù)據(jù)加 1 ,但在事務(wù)并未提交的情況下,由于 Connection 隔離級別的原因,在 getSequenceCurrentVal 方法中,是看不到 sequencePlus 方法中更新以后的數(shù)據(jù)的,這樣,也將導(dǎo)致數(shù)據(jù)插入失敗,因?yàn)橹麈I勢必跟舊有 ID 值重復(fù)。

    因此,傳統(tǒng)的編程方法中,為克服上述問題,只有在上述的方法中使用同一個(gè) Connection ,才能夠保證業(yè)務(wù)數(shù)據(jù)的正確。但這樣一來,將影響我們以 OO 方法分析問題時(shí)的“純潔”性,很容易讓人厭倦。

    2.3.? Connection作為成員變量

    另外一種常見的不當(dāng)編程模式是將 Connection 作為類的成員變量。一般來說,針對 Connection ,我們采取的策略是:用時(shí)再申請,用完立即釋放。而將 Connection 作為成員變量,將是對該規(guī)則的嚴(yán)重挑戰(zhàn),容易引起若干編程錯(cuò)誤。舉例而言:成員變量級的 Connection ,何時(shí)創(chuàng)建?何時(shí)釋放?倘若在每一個(gè)方法體內(nèi)進(jìn)行 Connection 的創(chuàng)建與釋放,那么將 Connection 作為成員變量又失去了意義;倘若在類的構(gòu)造期內(nèi)進(jìn)行 Connection 的創(chuàng)建,那么又在何時(shí)釋放它呢?因?yàn)樵?/span> Java 語言內(nèi),你是無法控制對象的生命周期的。

    Connection 作為成員變量還會產(chǎn)生另外一個(gè)問題:資源的閑置浪費(fèi)。因?yàn)樵谏暾堖B接以后,該資源將在這個(gè)對象的生命之期之內(nèi)一直有效,即使該對象處于非使用狀況,這無疑是一種資源的浪費(fèi)。更有甚者,倘若這種對象過多,將造成數(shù)據(jù)庫達(dá)到最大連接數(shù),造成應(yīng)用運(yùn)行失敗。

    3.?????????? 金蝶Apusic應(yīng)用服務(wù)器的數(shù)據(jù)源管理

    金蝶 Apusic 應(yīng)用服務(wù)器支持業(yè)界主流的各種數(shù)據(jù)庫,在 Apusic 應(yīng)用服務(wù)器之內(nèi)進(jìn)行數(shù)據(jù)源的配置與使用都非常簡單,同時(shí),它提供了許多增值特性,能夠?yàn)閼?yīng)用的正常運(yùn)行提供額外的保障。

    3.1.? 數(shù)據(jù)庫連接池的邏輯連接與物理連接

    我們注意到: java.sql.Connection 是一個(gè) Interface ,那么,真正實(shí)現(xiàn)這個(gè)接口的類是什么呢?

    我們可以做一個(gè)簡單的測試案例,在普通的 JavaApplication 中,調(diào)用如下方法:

    publicvoidshowConnection(){
    Connectionconn=null;
    try{
    Class.forName("oracle.jdbc.driver.OracleDriver");
    conn=DriverManager.getConnection(

    "jdbc:oracle:thin:@localhost:1521:KEVINORA",

    "system","manager");
    System.out.println("ConnectionClassis:"+conn.getClass().getName());
    }catch(Exceptione){
    e.printStackTrace();
    }finally{
    DbUtils.closeQuietly(conn);
    }
    }

    < 示例代碼五 >

    得到的輸出結(jié)果是: ConnectionClassis:

    oracle.jdbc.driver.T4CConnection

    而在 Apusic 應(yīng)用服務(wù)器中運(yùn)行如下方法:

    publicvoidshowConnection(){
    Connectionconn=null;
    try{
    Contextctx=newInitialContext();
    ds=(DataSource)ctx.lookup("jdbc/oracle");
    conn=ds.getConnection();
    System.out.println("ConnectionClassis:"+

    conn.getClass().getName());
    }catch(Exceptione){
    e.printStackTrace();
    }finally{
    DbUtils.closeQuietly(conn);
    }
    }

    < 示例代碼六 >

    得到的輸出結(jié)果是: ConnectionClassis:com.apusic.jdbc.adapter.ConnectionHandle

    明明用相同的 JDBCDriver 連接同一個(gè)數(shù)據(jù)庫,為什么取得的 Connection 卻是不同的類呢?事實(shí)上,通過 Apusic 應(yīng)用服務(wù)器獲得的數(shù)據(jù)庫連接其實(shí)只是一個(gè)邏輯連接,真正的物理連接隱藏在該邏輯連接之內(nèi),這是一個(gè)典型的 Delegate 模式,而恰恰是這個(gè)模式,通過 Apusic 應(yīng)用服務(wù)器對數(shù)據(jù)源進(jìn)行管理,將給我們的應(yīng)用開發(fā)帶來很多好處:

    3.2.? 當(dāng)事務(wù)結(jié)束以后,在該事務(wù)上下文中申請的物理連接,都將主動釋放

    我們以一個(gè)最簡單的 StatelessSessionBean 為例:

    publicclassSimpleBeanimplementsSessionBean{

    publicvoidfoo(){
    Connectionconn=null;
    try{
    Contextctx=newInitialContext();
    DataSourceds=(DataSource)ctx.lookup("jdbc/oracle");
    conn=ds.getConnection();
    System.out.println("notreleaseconnection");
    }catch(Exceptione){
    e.printStackTrace();
    }finally{
    //Notclosetheconnection
    //DbUtils.closeQuietly(conn);
    }
    }
    }

    < 示例代碼七 >

    SimpleBean 中的 foo 方法的事務(wù)屬性設(shè)置為 Required ,在該方法中,我們申請了一個(gè)數(shù)據(jù)庫連接,但并沒有釋放它,在運(yùn)行之前,我們通過 SQLPlus 觀察 Oracle 數(shù)據(jù)庫的 Session ,得到的結(jié)果是:

    SQL> select count(*) from v$session;

    ??COUNT(*)

    ----------

    ??????? 18

    ?

    < 圖一執(zhí)行方法之前的 OracleSession>

    而在執(zhí)行完 SimpleBean foo 方法之后,我們再次觀察 Oracle 數(shù)據(jù)庫的 Session ,得到的結(jié)果是:

    SQL> select count(*) from v$session;

    ?

    ??COUNT(*)

    ----------

    ??????? 18

    ?

    < 圖二:執(zhí)行方法之后的 OracleSession>

    由此,我們可以得知:即便由于程序的書寫錯(cuò)誤,沒能夠釋放申請的數(shù)據(jù)庫連接,但 Apusic 應(yīng)用服務(wù)器在事務(wù)完成之后,能夠把該事務(wù)上下文中申請的物理連接主動釋放,這對提升應(yīng)用的容錯(cuò)性帶來一定的好處。

    3.3.? 當(dāng)jsp/servlet運(yùn)行結(jié)束以后,在jsp/servlet中申請的物理連接,都將主動釋放

    同事務(wù)中申請的數(shù)據(jù)庫連接會主動釋放一樣,在 jsp/servlet 中申請的數(shù)據(jù)庫物理連接,當(dāng) jsp/servlet 運(yùn)行完畢以后,如果用戶沒有釋放這些連接, Apusic 應(yīng)用服務(wù)器也將予以主動釋放。讀者可以嘗試自己做一個(gè)案例:在 jsp 中申請一個(gè)連接,故意不釋放,在 jsp 執(zhí)行完畢以后,可以通過 SQLPlus 或者 Apusic 性能監(jiān)控工具,查看連接是否已經(jīng)被應(yīng)用服務(wù)器主動釋放。

    由上述兩節(jié)內(nèi)容我們可以看到, Apusic 應(yīng)用服務(wù)器能夠有效避免 2.1 節(jié)中所描述的問題。

    3.4.? ConnectionSharing:同一個(gè)事務(wù)上下文中申請的物理連接可以共享

    通過共享連接可以更有效地使用資源及提高性能,并且可以防止連接之間的資源鎖定問題。

    例如兩個(gè) EJB 組件 A B ,它們的事務(wù)屬性都設(shè)置為 Required 。在調(diào)用 EJBA 的方法時(shí)打開了一個(gè)數(shù)據(jù)庫連接,并對數(shù)據(jù)庫中的某個(gè)表進(jìn)行了更新操作,而在關(guān)閉連接之前 EJBA 調(diào)用了 EJBB 的某個(gè)方法,同樣 EJBB 打開同一個(gè)數(shù)據(jù)庫的連接,也對數(shù)據(jù)庫中同一個(gè)表進(jìn)行了更新操作。倘若沒有連接共享機(jī)制,這兩個(gè)連接指向的是兩個(gè)不同的物理連接,在其上執(zhí)行的數(shù)據(jù)庫操作將會互相鎖定,而這種死鎖狀態(tài)是無法恢復(fù)的。現(xiàn)在有了連接共享機(jī)制可以有效地解決這個(gè)問題。在 EJBA B 中所獲得的連接對象實(shí)際上都指向同一個(gè)物理連接。這一個(gè)過程可以簡單描述如下:

    con1=getConnection();
    Transaction.begin
    performdatabaseoperationoncon1
    con2=getConnection();
    performdatabaseoperationoncon2
    con2.close();
    Transaction.commit();
    con1.close();

    < 示例代碼八 >

    無論兩個(gè)連接是在事務(wù)邊界之內(nèi)或之外打開和關(guān)閉都沒有問題。只有在一個(gè)事務(wù)邊界之內(nèi)連接才會被共享,如果一個(gè)連接是在事務(wù)邊界之外打開的,那么在事務(wù)開始時(shí)會將此連接參與到事務(wù)中,并找到一個(gè)具有正確事務(wù)場景的物理連接和連接對象相關(guān)聯(lián)。在離開事務(wù)場景之后如果連接對象仍未關(guān)閉,則將其關(guān)聯(lián)到一個(gè)不具有事務(wù)場景的物理連接。

    可以在部署描述中指定一個(gè)資源引用的 res-sharing-scope 屬性來允許或禁止連接共享,屬性值 shareable 為允許共享, unshareable 為禁止共享,缺省情況下為允許共享。

    回到 2.2 節(jié)中 Customer 那個(gè)測試案例,我們已經(jīng)說過, Customer sequencePlus 方法、 getSequenceCurrentVal 方法、以及 addCustomer 方法,需要放在一個(gè)事務(wù)中處理。但在這三個(gè)方法中,使用的是不同的 Connection ,而由于 Connection 的隔離級別,將導(dǎo)致插入 T_CUSTOMER 表中的 ID 主鍵將重復(fù),最終導(dǎo)致事務(wù)回滾。利用 Apusic 應(yīng)用服務(wù)器連接共享特性,能夠很好的解決這個(gè)問題。也就是說:雖然這三個(gè)方法申請的邏輯連接是不同的,但邏輯連接內(nèi)部所使用的物理連接是同一個(gè),這樣,將保證不同方法中對數(shù)據(jù)庫的操作結(jié)果相見可見,從而保證事務(wù)的正常提交。

    舉例如下:假設(shè)在一個(gè) jsp 文件中,這樣調(diào)用:

    <%
    ???? InitialContext?ctx?=? new? InitialContext();
    ???? String?txName?=? "java:comp/UserTransaction" ;
    ???? UserTransaction?tx?=?(UserTransaction)ctx.lookup(txName);
    ???? tx.begin();
    ???? new? Customer().addCustomer( "eric" );
    ???? tx.commit();
    %>

    ?

    < 示例代碼九 >

    在上述代碼中,通過 UserTransaction 啟動一個(gè)事務(wù),然后在該事務(wù)上下文中,增加一筆 Customer 的記錄,我們發(fā)覺,在不需要更改 Customer 類的情況下,上述方法能夠正常完成。

    由此可以得知:在 Apusic 應(yīng)用服務(wù)器中進(jìn)行應(yīng)用的開發(fā),我們無需因?yàn)榭紤]數(shù)據(jù)庫 Connection 的隔離級別而影響我們對系統(tǒng)的面向?qū)ο蟮姆治龇椒ǎ?/span> Apusic 應(yīng)用服務(wù)器將替我們保證在同一事務(wù)上下文中,使用相同的物理連接。

    通過 Apusic 應(yīng)用服務(wù)器的這個(gè)特性,能夠有效的解決 2.2 節(jié)中描述的問題。

    3.5.? Lazy Connection Association Optimization:數(shù)據(jù)庫連接延遲關(guān)聯(lián)的優(yōu)化機(jī)制

    3.1 節(jié)中我們談到:通過 Apusic 應(yīng)用服務(wù)器管理的數(shù)據(jù)庫連接分邏輯連接與物理連接,物理連接隱藏在邏輯連接的背后。那么,邏輯連接何時(shí)與一個(gè)真正的物理連接相關(guān)聯(lián)的呢?在關(guān)聯(lián)的過程之中, Apusic 應(yīng)用服務(wù)器又提供了哪些優(yōu)化機(jī)制呢?舉例如下:

    J2EE 組件可能會將連接對象保存在其實(shí)例變量中從而可以在多個(gè)事務(wù)之間重復(fù)使用,但是如果這個(gè)組件在使用一次之后就很少再被用到,那么系統(tǒng)資源將會被組件白白占用而得不到釋放,當(dāng)連接池被占滿時(shí)就再也無法獲得新的連接。 Lazy Connection Association Optimization 是這樣一種機(jī)制,當(dāng) J2EE 組件方法調(diào)用完成時(shí),釋放連接對象所指向的物理連接以供其他組件使用,連接對象進(jìn)入一個(gè) Inactive 狀態(tài),在這個(gè)狀態(tài)下它不和任何物理連接相關(guān)聯(lián)。當(dāng) J2EE 組件需要使用該連接對象時(shí),容器將其激活,將其和一個(gè)實(shí)際的物理連接相關(guān)聯(lián)。這一過程對于應(yīng)用組件來說是完全透明的。 J2EE 程序員經(jīng)常犯的一個(gè)錯(cuò)誤是忘記關(guān)閉連接,特別是發(fā)生異常時(shí)沒有執(zhí)行正確的清理,過去我們解決這一問題是在方法調(diào)用完成時(shí)強(qiáng)制關(guān)閉所有的連接,現(xiàn)在有了 Lazy Connection Association Optimization 機(jī)制可以更完美地解決這一問題。

    ConnectionSharing Lazy Connection Association Optimization 是同時(shí)起作用的,例如,當(dāng)一個(gè)連接被激活時(shí),它將被包含在當(dāng)前事務(wù)場景中,并與同一事務(wù)場景中的其他邏輯連接共享同一個(gè)物理連接。

    我們在 2.3 節(jié)中強(qiáng)調(diào):將 Connection 作為成員變量是一種糟糕的設(shè)計(jì)模式,但同時(shí),我們也看到:哪怕用戶舊有系統(tǒng)中存在這樣的用法, Apusic 應(yīng)用服務(wù)器也能夠很好的解決由于這種糟糕的設(shè)計(jì)所帶來的缺陷。

    4.?????????? 總結(jié)

    本文首先與讀者分析了一些錯(cuò)誤或者不當(dāng)?shù)臄?shù)據(jù)庫資源使用方法,然后簡要介紹了金蝶 Apusic 應(yīng)用服務(wù)器在數(shù)據(jù)源管理上的一些特性。這些特性,對應(yīng)用的健壯性及容錯(cuò)性帶來一定的好處。但需要再次提醒的是:應(yīng)用服務(wù)器提供的一些增值特性,僅能夠當(dāng)作保障我們應(yīng)用正常運(yùn)行的最后一道屏障,我們切不可依賴于這些特性而忽視程序自身的編碼質(zhì)量。一個(gè) J2EE 應(yīng)用能否正常的運(yùn)行,程序自身的設(shè)計(jì)與編碼永遠(yuǎn)是主要因素。

    5.?????????? 參考資料

    2 CommonsDbUtils:ApacheJakarta 項(xiàng)目的 Commons 組件, http://jakarta.apache.org/commons/index.html

    3 :金蝶 Apusic 應(yīng)用服務(wù)器:國內(nèi)首家通過 J2EE1.4 認(rèn)證的應(yīng)用服務(wù)器,請參考 http://www.apusic.com/

    ?

    posted on 2007-06-07 13:35 zYx.Tom 閱讀(763) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 2020久久精品国产免费| 狼人大香伊蕉国产WWW亚洲| 亚洲AV日韩精品久久久久久| 亚洲午夜无码片在线观看影院猛| 亚洲成av人片一区二区三区 | 亚洲精品无码av片| 亚洲欧美日韩久久精品| 久久久久久亚洲精品影院| 国产成人亚洲综合一区| 亚洲中文字幕AV每天更新| 亚洲成AV人影片在线观看| 亚洲AV日韩AV无码污污网站| 国产亚洲精品美女2020久久| 特级毛片A级毛片免费播放| 国产精品视频全国免费观看| 日本道免费精品一区二区| 日本在线免费播放| 2020因为爱你带字幕免费观看全集| 4455永久在线观免费看| 在线观看无码AV网站永久免费| 国内外成人免费视频| 免费看少妇作爱视频| gogo全球高清大胆亚洲| 国产亚洲精品拍拍拍拍拍| 国产亚洲精久久久久久无码| 亚洲日韩区在线电影| 456亚洲人成在线播放网站| 色天使色婷婷在线影院亚洲| 日韩电影免费在线观看网址| a级黄色毛片免费播放视频| 最近中文字幕mv免费高清视频8| 亚洲免费综合色在线视频| 午夜国产羞羞视频免费网站| 丝袜熟女国偷自产中文字幕亚洲| 亚洲男人天堂av| 一本色道久久88亚洲精品综合| 色网站在线免费观看| 免费视频一区二区| 最新猫咪www免费人成| 亚洲国产天堂久久久久久| 亚洲成人在线网站|