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

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

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

    Workflow Project 目前狀態
    版本 0.11

    已經完成
    1。完成了接口1 和接口2 的方法
    2。完成接口3的默認實現
    3。完成事務回滾的實現方法-等待測試

    未完成
    1。接口3的注冊與實例化解決方案
    2。應用的并發訪問問題以及解決數據的臟讀問題
    3。與具體的某個應用掛接并測試


    -事務的回滾
    OSWorkFlow的事務回滾是依靠WorkflowContext這個接口來實現的,在New出某個WorkFlow的時候需要聲明WorkflowContext的實現類,一般會采用uper.context = new GearWheelWorkFlowContext(_caller);方法
    比如這樣實現:

    public GearWheelWorkFlow(String _caller)
    {
    super.context = new GearWheelWorkFlowContext(_caller);
    }

    但OSWorkFlow的WorkflowContext的默認實現BasicWorkFlowContext中根本沒有實現setRollbackOnly方法,也就沒有了參考的可能

    再看看這個接口的其他實現類也都是建立在JTA這樣的跨Session的事務服務上,比如它的EJB的實現也是要調用容器提供的JTA實現才行!而JTA的實現比如要JNDI到數據庫池,此時的應用光JTA+JNDI就已經宣布 -這樣的例子必須生存在應用服務器的環境下!!

    可是,我不死心,我記得Hibernate可以實現本地事務,也就是依靠JDBC本身的事務處理能力,而要實現這樣的功能就需要在數據庫連接的獲取上下一些功夫,也就是要保證回滾的數據庫連接必須是獲取時的那個連接,而存儲連接就成了一個需要首先解決的問題。

    解決數據庫連接的存儲問題
    目前存儲數據庫連接除了依靠靜態類外,還有一個通用的方法ThreadLocal類,這樣獲取數據庫連接的方法寫成了如下的形式:

    package com.company.common;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    import org.apache.log4j.Logger;
    public class DB2ConnectFactory
    {
    /**
    * Logger for this class
    */
    private static final Logger logger = Logger.getLogger(DB2ConnectFactory.class);
    private static ThreadLocal threadLocal = new ThreadLocal();
    //~
    private Connection connect = null;
    private Statement state = null;
    private ResultSet result = null;
    private boolean closeConnWhenDone = false;
    //~
    private String url = "jdbc:db2:WORKFLOW";
    private String user = "";
    private String password = "";
    private String driverClassName = "COM.ibm.db2.jdbc.app.DB2Driver";

    public DB2ConnectFactory() throws SQLException
    {
    this.init();
    }

    /**
    * 獲取數據庫連接
    * @return
    * @throws SQLException
    */
    public Connection getConn() throws SQLException
    {
    return (Connection)threadLocal.get();
    }

    /**
    * 初始化數據庫,并在緩沖中注冊數據庫連接
    * @throws SQLException
    */
    private void init() throws SQLException
    {
    try
    {
    // Get connect object
    Class.forName(driverClassName);
    closeConnWhenDone = true;

    connect = DriverManager.getConnection(url, user, password);
    state = connect.createStatement();

    //Register the connection object in the threadlocal
    threadLocal.set(connect);
    }
    catch (Exception e)
    {
    e.printStackTrace();
    throw new SQLException(e.getMessage());
    }
    }

    }

    解決事務回滾
    剛才說了需要實現WorkflowContext接口
    package com.company.engine.workflow;
    import java.sql.Connection;
    import java.sql.SQLException;
    import org.apache.log4j.Logger;
    import com.company.common.DB2ConnectFactory;
    import com.opensymphony.workflow.WorkflowContext;
    public class GearWheelWorkFlowContext implements WorkflowContext
    {
    /**
    * Logger for this class
    */
    private static final Logger logger = Logger.getLogger(GearWheelWorkFlowContext.class);

    private static ThreadLocal threadLocal = new ThreadLocal();

    // ~ Instance fields
    // ////////////////////////////////////////////////////////
    private String caller;
    // ~ Constructors
    // ///////////////////////////////////////////////////////////
    public GearWheelWorkFlowContext(String caller)
    {
    this.caller = caller;
    }
    // ~ Methods
    // ////////////////////////////////////////////////////////////////
    public String getCaller()
    {
    return this.caller;
    }
    /**
    * Tranaction : Set Roll back
    * @throws SQLException
    */
    public void setRollbackOnly()
    {
    Connection connect = null;
    try
    {
    DB2ConnectFactory factory = new DB2ConnectFactory();
    connect = factory.getConn();
    if(connect != null) connect.rollback();
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    finally
    {
    this.clostConnection(connect);
    }
    }

    private void clostConnection(Connection connect)
    {
    try
    {
    if(connect != null) connect.close();
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    }
    }

    總結
    1。我們可以看到由于接口中setRollbackOnly沒有異常的聲明,方法中即使拋出了異常也要自己"忍了"!看來良好的接口聲明其實是非常重要的。

    2。而且需要重載原來JDBCWorkflow 中的cleanup方法,將其中的代碼屏蔽掉!數據庫的關閉放在了setRollbackOnly訪訪的finally中,原因就是由于我們要統一的管理數據庫連接所引發的,我們不能夠在WorkFlowStore的每一個方法執行完畢后就關閉連接,因為這樣的話你根本沒有了事務回滾的可能,所以此時的連接需要在WorkflowContext中來處理。


    感觸
    OSWorkFlow的實現方法并不是像網上所說的那樣的優秀和文雅,更像是一個未完成任務的"半成品",Heni被網上鼓吹為大牛,但一個不寫注釋和文檔的人,根本稱不上什么大牛!
    OSWorkFlow更多的是實現了一個微內核,而它的用戶模式是與OSUser這樣的框架耦合的(偶已經將這樣的耦合打開了,也就是接口3的定義),它的相關數據是與PropertySet框架耦合的(也就是接口2的定義),而且采用OSWorkFlow要經過很原始的修改(比如我實現了DB2下的WorkFlowStore的實現)。

    不過也好即使以后不采用OSWorkFlow,自己實現一個這樣的引擎也應該沒有什么問題的,有時間了我倒是很想看看別的工作流的產品。

    posted @ 2006-03-02 21:03 killvin| 編輯 收藏

    workflow接口劃分

    1。應用接口 Application Interface
    --interface1 工作流自身提供的服務接口
    --interface2 工作流與應用之間的接口(主要是提供相關數據的調用接口)

    2。擴展接口 PlugIn Interface
    --interface3 工作流與組織機構之間的接口
    --interface4 工作流與其他工作流之間的接口

    將接口劃分成應用接口與擴展接口主要是依據工作流與相關應用的調用關系,比如工作流與組織機構之間,是工作流調用組織機構中的人員信息,所以主動者是WORKFLOW、被動方是組織機構,所以應該采用擴展接口來實現

    在擴展接口上應該采用Adapter模式,從而使工作流不局限于某個特定的實現

    目前的進展
    0。Application Interface接口已經基本實現了
    PlugIn Interface接口目前基本完工,但OSWorkflow的實現實在是非常的丑陋,需要更改的地方太多,而且對于Interface3不可以使用它采用的User / Group模型(而且它使用了OSUser這個框架,對于多數的應用程序基本可以說不適合,而且它的User類竟然是Final ?!而且我發現它的很多類的屬性都是Protected!也就是說除了他們自己根本沒有辦法擴展,即使擴展也是很丑陋的方式)

    1。現在最大的問題是它的WorkStore接口的擴展,我采用DB2的方式實現了它的接口,但這樣的方式會與DB2綁定在一起,如果自己寫實現就要根據不同的DB采用不同的SQL語言-也就是不同的方言策略?!而且考慮到性能估計不是什么好主意,看來明天需要更換成HibernateWorkStore的形式,這樣工作流的持久層接口將工作在Hibernate之上,看來很完美的解決了這個問題。

    2。而且我擴展了它的PropertySet,使其不再依靠JNDI尋找DataSource,而是通過嵌入在程序內部采用JDBC的形式尋找數據庫連接,這樣我就不必為了驗證一個問題去建立那該死的數據庫緩沖池了(而且JNDI的形式也就不可避免的要用到容器,太重了!)

    3。我編寫了UserGroupCondition的實現類,這個類的作用就是調用Interface3的方法,從而判斷某個用戶是否屬于某個組(現在的做法是讓WorkStore實現Interface3的偷懶辦法,但很亂,看來還是要寫一個Adapter去實現interface3才對!)

    4。目前工作流引擎的工廠類已經實現完工并測試通過。


    用了近一個月的時間完成了這些工作,看起來很少但是基本上大量的時間花費在熟悉工作流規范、WFMC標準、以及學習和擴展OSWorkflow接口上,不過對OSWorkflow的實現基本上掌握了,如果拋開OSWorkflow自己也可以采用自己的方式去實現,或者會考慮使用Spring的方式(Interface3的Adapter不行就采用Spring實現)。

    BTW:
    OSWorkflow的實現其實比較的丑陋!而且編碼根本沒有什么規范,接口的定義也是天馬行空,看來Heni除了他的大嘴外應該好好的提高自己的技術修養。-實在不敢恭維這位"大師"的編碼水平!

    posted @ 2006-03-02 21:03 killvin| 編輯 收藏

    在Workflow事務回滾中遇到了問題,是這樣的

    DB2ConnectFactory 中getConn方法
    /**
    * 獲取數據庫連接
    * @return
    * @throws SQLException
    */
    public Connection getConn() throws SQLException
    {
    Object obj = threadLocal.get();
    if(obj == null)
    {
    this.initFactoryStack();
    }else
    {
    connect = (Connection)obj;
    }
    connect.setAutoCommit(false); //事務的回滾必須建立在將Commit狀態為False下,默認是true
    logger.debug("Get connect from factory - " + connect.hashCode());
    return connect;
    }

    AbstractWorkflow 的doAction()方法


    try {
    //transition the workflow, if it wasn't explicitly finished, check for an implicit finish
    if (!transitionWorkflow(entry, currentSteps, store, wf, action, transientVars, inputs, ps)) {
    checkImplicitFinish(id);
    }
    } catch (WorkflowException e) {
    context.setRollbackOnly(); // 這里調用WorkContext對象的setRollbackOnly()方法,執行事務的回滾
    throw e;
    }

    GearWheelWorkFlowContext 的setRollbackOnly方法
    /**
    * Tranaction : Set Roll back
    * @throws SQLException
    */
    public void setRollbackOnly()
    {
    logger.debug("Context execute setRollbackOnly() !!");
    Connection connect = null;
    try
    {
    DB2ConnectFactory factory = new DB2ConnectFactory();
    connect = factory.getConn();
    logger.debug("Context get connect " + connect.hashCode());

    if(connect != null) connect.rollback();
    }
    catch (Exception e)
    {
    e.printStackTrace();
    }
    finally
    {
    this.clostConnection(connect); //這里將關閉數據庫連接
    }
    }


    可是這是"異常"情況下的處理流程,如果正常執行呢?
    剛開始我想寫在CleanUp()方法里,但又一想不行,因為正常執行的流程需要做兩個工作
    1。將Commit狀態更新為true,并提交連接
    2。關閉數據庫連接


    關鍵就是關閉數據庫的連接在哪里寫?!現在寫在CleanUp()不合適,因為每一個WorkStore的方法都要默認(程序已經寫死了,我可不想重載它的所有的方法!!)的關閉數據庫連接!
    仔細的分析了一下,其實有兩個方法可以做到
    1。編寫Proxy類
    2。重載所有AbstractWorkflow中設計到事務的方法,(本來可以重載transitionWorkflow但是方法的類型卻為private?!)在它的方法下增加一個"提交"的方法。比如這樣

    try {
    //transition the workflow, if it wasn't explicitly finished, check for an implicit finish
    if (!transitionWorkflow(entry, currentSteps, store, wf, action, transientVars, inputs, ps))
    {
    checkImplicitFinish(id);
    }

    dosubmit();

    } catch (WorkflowException e) {
    context.setRollbackOnly(); // 這里調用WorkContext對象的setRollbackOnly()方法,執行事務的回滾
    throw e;
    }

    可以看到方法2比較"爛",看來下一步即使編寫方法1的實現


    posted @ 2006-03-02 21:02 killvin| 編輯 收藏

    早上的時間被該死的WorkflwoStore里的主鍵生成策略("主鍵生成策略"來源于Hibernate文檔),該死的Sequence,從文檔資料上看到DB2是支持Sequence的,按照db2的文檔我執行了如下的語句:
    create sequence seq_os_wfentry start with 10 increment by 10;
    create sequence seq_os_currentsteps;
    執行-ok

    可是我以前不太了解Sequence的概念,這片資料倒是很有價值
    http://www-128.ibm.com/developerworks/db2/library/techarticle/dm-0407zhang/
    不過我以為查詢Sequence就只需要執行SELECT NEXT VALUE FOR seq_os_wfentry 就ok了,可是誰知道總是報錯?!在比較仔細的看了這片文章之后發現,其實根本就無法執行這條SQL!而需要這樣
    INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME,
    SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'Martin', 'Wong', 1000.00)

    可是看看JDBCWorkflowStore的實現,這里是JDBCWorkflowStore的主鍵生成策略!
    protected long getNextEntrySequence(Connection c) throws SQLException {
    if (log.isDebugEnabled()) {
    log.debug("Executing SQL statement: " + entrySequence);
    }

    PreparedStatement stmt = null;
    ResultSet rset = null;

    try {
    stmt = c.prepareStatement(entrySequence);
    rset = stmt.executeQuery();
    rset.next();

    long id = rset.getLong(1);

    return id;
    } finally {
    cleanup(null, stmt, rset);
    }
    }

    c.prepareStatement(entrySequence) - 其實執行了一條SQL語句,所以看來JDBCWorkflow根本不支持Sequence生成策略!!

    該死的實現方式,看來我要重載其實現方式,不過說真的JDBCWorkflow的編碼人員其實水平不匝地!

    posted @ 2006-03-02 21:01 killvin| 編輯 收藏

    OSPropertySet的最新版本是1.3 Date: 9/22/2003 ,不要使用OSWorkflow中自帶的OSPropertySet.jar ,主要是因為它的版本為propertyset-1.3-21Apr04,甚至里面的接口PropertySet竟然私自更換了(這個版本的remove()方法是抽象的,而新版本已經將這個方法命名為Public!!)

    posted @ 2006-03-02 21:00 killvin| 編輯 收藏

    J2EE(即Java 2 平臺企業版)是由Sun公司主持推出的一項中間件技術。從CORBA、IDL到面向消息的系統,中間件技術已經走過了很長的一段路程,如今J2EE作為中間件技術史上的一塊具有決定意義的里程碑,正受到業界越來越廣泛的重視和采納。

    J2EE,一方面有著一套相當龐大的標準體系和數個不同版本,另一方面,由于市場上應用服務器品種多樣,各家開發商使用的術語又不盡相同,因此,圍繞著J2EE,常常有不少被人誤解的地方。本文將深入探討J2EE究竟是什么,它到底能做什么。
    什么是J2EE?
    在試圖給J2EE 下一個明確的定義之前,我們首先要了解J2EE 并不簡單地只是一門語言、一種工具或一套服務。

    · J2EE——Java 2 平臺企業版
    簡單地說,J2EE是一個標準中間件體系結構,旨在簡化和規范多層分布式企業應用系統的開發和部署。J2EE方案的實施可顯著地提高系統的可移植性、安全性、可伸縮性、負載平衡和可重用性。

    J2EE技術出現之前,幾家主要的中間件開發商的產品各自為陣,彼此之間缺乏兼容性,可移植性差,難以實現互操作,沒有一個被普遍認可的行業標準。J2EE的出現標志著中間件技術在經歷了多年的不斷摸索和經驗總結后,正逐步走向成熟。

    J2EE的核心是一組規范和指南,定義了一個使用Java語言開發多層分布式企業應用系統的標準平臺。開發人員在這些規范和指南的基礎上開發企業級應用,同時由J2EE供應商確保不同的J2EE平臺之間的兼容性。由于基于規范的各J2EE平臺之間具有良好的兼容性, 因此J2EE應用系統可以部署在不同的應用服務器上,無需或只需進行少量的代碼修改。


    · J2EE視點
    下面我們將從幾個不同的側面來考察J2EE,以期讀者能對J2EE有個更全面清晰的印象。

    (1)J2EE:多層、分布式中間件語法
    采用多層分布式應用模型,J2EE將應用開發劃分為多個不同的層,并在每一個層上定義組件。各個應用組件根據他們所在的層分布在同一個或不同的服務器上,共同組成基于組件的多層分布式系統。典型的J2EE四層結構包括客戶層、表示邏輯層(Web層)、商業邏輯層和企業信息系統層。

    有了J2EE,分布式系統的開發變得簡單了,部署的速度也可以加快。J2EE組件的分布與服務器環境無關,所有的資源都可通過分布式目錄進行訪問。這意味著開發人員不再需要為組件和資源的分布問題耗費精力,從而可以有更多的時間專注于業務邏輯的實現,提高開發效率。

    (2)J2EE:企業級應用系統開發平臺
    J2EE本身是一個標準,一個為企業分布式應用的開發提供的標準平臺。而J2EE的實施,則具體表現為諸如BEA Web logic或IBM Web sphere之類的特定Web服務器產品。利用J2EE應用-編程模型開發的企業應用系統,可以部署在不同廠商生產的、但相互兼容的J2EE 應用服務器上。

    目前,市場上基于J2EE的Web服務器品種繁多,性能特點各有千秋,每家廠商的產品都有精心設計的獨到之處。但與產品個性無關的是,所有的J2EE應用服務器都為企業級應用系統的開發和部署提供了一個共同的基礎。

    (3)J2EE:電子化應用開發模型
    J2EE應用很容易發布到Web、掌上電腦或移動電話等手持設備上。換言之,應用組件可以很輕松地實現電子化。J2EE的應用-編程模型保證組件在向不同類型的客戶端移植過程中,商業邏輯和后端系統保持不變。

    此外,J2EE平臺的其他主要優點還有:自動負載平衡、可伸縮、容錯和具有故障排除等功能。部署在J2EE環境中的組件將自動獲得上述特性,而不必增加額外的代碼開銷。

    J2EE所有這些特性對于需要構建全天候網絡門戶的企業來說顯得尤為重要。

    (4)J2EE:Web應用服務器上廣泛采用的標準
    可以說,J2EE是首個獲得業界廣泛認可和采納的中間件標準。目前幾乎所有的一流Web應用服務器,如BEA的Web logic、IBM的Web sphere、HP的應用服務器、Sun的iPlanet和Macromedia的Jrun等,都是基于J2EE的。迄今為止,還沒有哪個其他標準能獲得如此眾多的中間件供應商的一致支持。

    而且,有了J2EE,企業的應用開發對于某個特定的開發商或應用服務供應商的依賴性更小。應用組件只要符合J2EE規范,完全可以部署在不同的應用服務器上。為了確保不同廠商的J2EE應用服務器的兼容性和一致性,Sun公司發布了J2EE兼容性測試包。

    · J2EE究竟是什么
    至此,我們可以試著用一句話來概括J2EE,那就是:J2EE是一個中間件基礎架構,有了它,開發者只需要集中精力編寫代碼來表達企業應用的商業邏輯和表示邏輯,至于其他系統問題,如內存管理,多線程,資源分布和垃圾收集等,都將由J2EE自動完成。

    J2EE如何應對挑戰?
    在這一部分里,我們將探討J2EE是如何應對企業開發過程中所面臨的問題,以及如何為企業未來發展之需要提供空間。

    · 獨立于硬件配置和操作系統
    J2EE運行在Java虛擬機(JVM)上,利用Java本身的跨平臺特性,獨立于硬件配置和操作系統。Java運行環境(JRE)——JVM的可安裝版本加上其他一些重要組件——幾乎可以運行于所有的硬件/OS組合。因此,通過采用Java,J2EE使企業免于高昂的硬件設備和操作系統的再投資,保護已有的IT資源。在很多情況下,J2EE還可以直接運行在EIS服務器環境中,從而節約網絡帶寬,提高性能。


    · 堅持面向對象的設計原則
    作為一門完全面向對象的語言,Java幾乎支持所有的面向對象的程序設計特征。面向對象和基于組件的設計原則構成了J2EE應用編程模型的基礎。

    J2EE多層結構的每一層都有多種組件模型。因此,開發人員所要做的就是為應用項目選擇適當的組件模型組合,靈活地開發和裝配組件,這樣不僅有助于提高應用系統的可擴展性,還能有效地提高開發速度,縮短開發周期。此外,基于J2EE的應用還具有結構良好,模塊化,靈活和高度可重用性等優點。

    · 靈活性、可移植性和互操作性
    利用Java的跨平臺特性,J2EE組件可以很方便地移植到不同的應用服務器環境中。這意味著企業不必再拘泥于單一的開發平臺。

    J2EE的應用系統可以部署在不同的應用服務器上,在全異構環境下,J2EE組件仍可彼此協同工作。這一特征使得裝配應用組件首次獲得空前的互操作性。例如,安裝在IBM Websphere環境下的EJB,一方面可以直接與Websphere環境下的CICS直接交互,另一方面也可以通過安裝在別處的BEA Weblogic 服務器上的EJB進行訪問。

    · 輕松的企業信息系統集成
    J2EE技術出臺后不久,很快就將JDBC、 JMS和 JCA等一批標準納歸自身體系之下,這大大簡化了企業信息系統整合的工作量,方便企業將諸如legacy system(早期投資系統),ERP和數據庫等多個不同的信息系統進行無縫集成。

    由于幾乎所有的關系型數據庫系統都支持JDBC,因此只需借助必要的JDBC驅動程序,J2EE應用就可以和所有主流數據庫系統進行通信。類似的,目前業界正冒出一批基于Java連接器體系標準的EI適配器,也用于提供各類legacy system和ERP/CRM的無縫集成。

    · 引進面向服務的體系結構
    隨著Web服務以及SOAP等開放標準的出現,企業異構系統之間的互操作性成為可能。J2EE,作為一個可擴展平臺,很自然需要加入Web服務特性。為此,Sun公司發布了一整套稱為“JAX包”的API,支持從XML語法分析、XML綁定、SOAP消息發送、注冊表查尋、XML RPC到XML消息傳遞等所有各種Web服務需求。

    雖然J2EE平臺的出現早于Web服務技術,但它的可擴展能力使它能很好地適應技術的最新發展。我們有理由相信,在未來,J2EE將引入更多的技術進步而不會動搖它的核心框架和應用-編程模型。

    結束語
    作為一個被業界廣泛采用的中間件標準,J2EE是開發可伸縮的、具有負載平衡能力的多層分布式跨平臺企業應用的理想平臺。J2EE的首要任務在于提供一個標準中間件基礎架構,由該基礎架構負責處理企業開發中所涉及的所有系統級問題,從而使得開發人員可以集中精力重視商業邏輯的設計和應用的表示,提高開發工作的效率。

    J2EE有效地滿足了行業需求,提供獨立于操作系統的開發環境。基于J2EE的應用系統靈活且易于移植和重用,可運行在不同廠家的Web服務器上。更為重要的是,J2EE是一個開放體系,完全有能力適應未來技術的進步和發展。

    posted @ 2006-03-02 20:59 killvin| 編輯 收藏

    該文轉自guty

    O-R Mapping

    J2EE的標準是CMP Entity Bean,而實際應用中受到詬病最多的也是它。我們化了整整半年時間研究CMP2.0的開發方法,目前總算能夠將代碼量減少到70%,并且有希望減少到 90%。我曾經很滿足現有的成績,但是當我真正地閱讀了hibernate后,對CMP2.0的信心徹底動搖了。

    hibernate至少比CMP2.0有以下優點:
    1. 兼容性。 規范一模一樣,實現各有不同,這是CMP的現狀。用第三方O-R Mapping工具可以解決這個問題。
    2. 保護智力投資。在了解了Orion, Weblogic, JBoss的CMP實現后,我不愿意再去學習Websphere 或者Resin的實現了。
    3. 性能。
    a. local v.s. remote, hibernate、JDO、Castor都是本地調用,CMP2.0雖然也有Local接口,但是Web層還是需要通過Remote接口訪問EJB層的數據,序列化、網絡調用、創建大量的對象,都是性能降低的原因。
    b. transaction,J2EE提出了一個全新的事務模型(method-based descriptor),對程序員的開發確實是個“簡化”,記得一本教程建議所有的EJB方法都用Required。但這樣的結果是什么?性能極度降低!互鎖!沒有辦法,我們只有再去調節各個方法的Transaction屬性,然后又出現 新的互鎖...
    新的事務模型是不成功的。它試圖簡化問題,卻引入了更為嚴重的問題。各家廠商的Transaction實現也不盡相同,有的支持Optimistic Lock,有的在VM中同步Entity對象,又是兼容性的一大敵。
    hibernate沒有試圖創造一個更新的模式,相反,它沿用了傳統數據庫的Transaction編程模式,在對J2EE的Transaction傷透腦筋后看到它,真是十分親切,感覺自己確實在編程,而不是碰運氣填代碼了。
    4. 動態Query。
    Entity Bean很難實現動態Query,這是因為它基于代碼自動生成技術,即最終的執行代碼是在部署編譯時生成的。hibernate則有根本的改變,它基于 reflection機制,運行時動態Query是很自然的事。另外,hibernate幾乎支持所有的SQL語法,傳統數據庫可以做的它就可以做。
    5. 發展速度。
    I have a dream, 有一天Entity Bean會變得很好。但至少目前來看,Entity Bean是一個不完善的產品,它是大公司政治斗爭和妥協的產品,而且習慣性將一些問題“無限期擱置”,典型的例子就是Query(之所以不提其他問題,是因為其他都是Entity Bean的致命傷:))
    形成強烈反差的是,hibernate的核心程序員只有一人,但它改進的速度確是Entity Bean無法企及的。
    6. 繼承和多態。
    OO語言的精華在Entity Bean這里是行不通的,我曾經自我安慰將Entity Bean看做一個“內存中的數據表”,才找到了一點平衡。
    但當我看到hibernate時,又開始不平衡了。

    另外,CMP2.0也有一些缺點是可以彌補的。
    1. 代碼維護。
    大量的接口文件和配置文件,開發和維護的工作量很大。
    解決途徑:采用xdoclet,可以自動產生眾多的接口和配置文件,甚至facade, delegate等高級模式。

    至少目前來看,hibernate的缺點有:
    1. 代碼維護
    hibernate提供了自動生成mapping文件“框架”的工具,但還需要手工調節。而這類開發,能想到的最佳模式就是xdoclet的(代碼+注釋)的模式了。幸好,hibernate的程序員已經向xdoclet項目增加了hibernate的模塊。現在需要的是等待xdoclet的下一個 release。

    結論:
    hibernate至少從文檔上超越了Entity Bean很多,我要學習hibernate。

    以下是robbin的觀點

    如果說不使用Session Facade模式的話,我認為EB還是一個很有意義的的東西,因為EB是唯一直接支持跨RMI的持久化方案。但是由于EB的效率和減少跨RMI的網絡調用的原因,EB已經完全被封裝到SB的后面,EB的分布式調用的功能,EB的安全驗證功能,EB的容器事務功能完全被前面的SB給做了,結果EB就只剩下了唯一的ORM功能了,單就ORM這一點來說EB實在是一個非常非常糟糕的東西。那么EB還有什么功能值得我非去用它不可呢?

    用 Session Bean + DAO + Hibernate 來取代 Session Bean + Entity Bean,不但能夠極大降低軟件設計難度,軟件開發難度,軟件調試難度和軟件部署難度,而且還可以提高允許效率,降低硬件要求。

    不要把EB直接拿來和Hibernate做比較,兩者不是一個范疇的東西,而應該整體比較兩種方案:
    Session Bean + DAO + Hibernate
    Session Bean + Entity Bean
    我找不出來第二方案有哪怕一點方面能夠比第一方案好的。

    CMP可以使用CMR來表示多表之間通過外鍵關聯的關系。但是你仍然會遇到即使沒有鍵關聯的表仍然需要連接查詢的情況,這是一個非常普遍的現象。

    如果是Hibernate,可以在HSQL里面定義outer join,BMP也可以寫JDBC,而CMP沒有任何辦法來解決該問題,除非你把需要的連接查詢都定義為CMR,但那樣的話,凡是有需要連接查詢,或者有鍵關聯的表都必須打在一個包里面。你如果不打在一個jar包里面,如果能夠建立CMR?不是我想放在一個jar里面,而是不得不放在一個jar里面。基本上CMP還是非常笨拙的。

    CMP的另一大缺點是不能動態SQL,guty已經提到了,一個SQL就要定義一個EJBFinder方法,在編譯的時候就確定死了。在實際應用中,經常會遇到不確定查詢條件的查詢,比如說用戶在頁面上用下拉列表來選擇查詢的條件,用戶既有可能什么限制條件都不選,也有可能選擇某幾個條件。這時候你怎么辦?假設有n個查詢條件,你要寫 C1n + C2n + C3n +...+ Cnn(C是組合公式的符合,n是下標,1...n是上標)個EJBFinder方法才行,很恐怖吧。

    其實JDBC的PrepareStatement也不能很好的解決這個問題,因為它是按照1,2這樣的次序來set參數的。用Statement是肯定不行的,會嚴重影響數據庫,甚至會導致數據庫down掉(我的實際經驗)。但是Hibernate就解決的不錯,因為它可以按照 :name 這樣的形式來設定SQL中的Placeholder,這樣set參數就可以按照參數名稱傳遞,因為次序不是死的,在程序里面就很容易根據用戶選擇的查詢條件,動態的產生SQL,動態的set參數了。

    CMP2.0還有一個大問題是不支持order by,當然你可以在Java里面對取出來的集合排序,但是速度和數據庫里面就排好序速度不在一個數量級了。Hibernate不但可以order by,還可以group by,having,子查詢,真是沒有辦法比下去了。

    其實對于動態SQL和排序問題,特定的App Server也可以做,但那不是CMP2.0的規范罷了,所以為了可移植性,也不敢隨便去用。

    在項目開發時, 開發和運行效率以及靈活性是非常重要的指標。由于Entity Bean天生是一種粗粒度的使用方式,這就必定使它在裝載的時候有較長的響應時間,也不能自如的支持懶裝入的方式,使用成細粒度會使程序變得復雜,以及遠程調用細粒度的entity bean是一種非常可怕的行為, 太慢了.

    Hibernate正好滿足開發和運行效率以及靈活性,說來說去,它可以稱做一個OO化的JDBC, 這樣大家就不會對Hibernate產生誤解及恐懼心理。它支持粗細兩種粒度方式,運用起來靈活自如,前提是你必知道如何使用,一個entity bean 實現要N種重復的方法, such as ejbRemove,ejbstore,ejb...., 光類也有一大堆,象Home Interface, Romote Interface..., Primary class if necessary. Hibernate只需要一個就行了。

    CMP在進行O/R Mapping方面只是做了最基礎的工作而已,完全用CMP做數據層,會發現你在把數據庫應該做的工作全部都搬到App Server里面來重新實現一遍,有這必要嗎?

    CMP是把EJBQL寫死在ejb-jar.xml里面的,所以n個條件就需要(c0n+c1n+...cnn )2的n次方個EJBFinder方法,簡直沒有辦法說。

    JDBC實現PrepareStatement的動態SQL構造不是不能夠,而是非常麻煩,需要寫一個非常非常大的if elseif else嵌套的判斷。

    Hibernate實現起來特別簡單,(其實OJB也實現了PrepareStatement的動態SQL構造)這本身并不復雜,但是需要你多寫些代碼而已,由于CMP把EJBQL寫死在配置文件里面了,你連選擇的余地都沒有。

    posted @ 2006-03-02 20:58 killvin| 編輯 收藏

    A reader asked a question via a comment a couple months ago that I didn't really have an answer for (and had always kind of wondered the same thing). In the original post (which showed how to use JDBC with ColdFusion), I used the following snippet of code:

    Class.forName("jdbc.DriverXYZ");
    Connection con = DriverManager.getConnection(url,
      "myLogin", "myPassword");

    and the reader wanted to know what the Class.forName(..) method did. The most common answer you'll hear is that it loads the database driver, which, while technically true, is shallow. Where does it get loaded? How does it happen? And why?

    To answer the question I started with the JavaDoc for the Class.forName() method. According to the documentation, the method:

    ... attempts to locate, load, and link the class or interface
    I wasn't perfectly clear on what "locate, load, and link" meant, so I did a little digging through the Java Language Specification. According to chapter 12 of the JLS:
    Loading refers to the process of finding the binary form of a class or interface type with a particular name, perhaps by computing it on the fly, but more typically by retrieving a binary representation previously computed from source code by a compiler, and constructing, from that binary form, a Class object to represent the class or interface.
    Next, again according to the JLS, it must be transformed from it's binary representation to something the Java virtual machine can use, this process is called linking. Finally, the class is initialized, which is the process that executes the static initializer and the initializers for static fields declared in the class.

    So then back to the original problem, when Class.forName() is called with an argument like this:

    Class.forName("org.gjt.mm.mysql.Driver");

    the classloader attempts to load and link the Driver class in the "org.gjt.mm.mysql" package and if successful, the static initializer is run. The MySQL Driver (download the source code) static initializer looks like this:

    static {
      try {
        java.sql.DriverManager.registerDriver(new Driver());
      } catch (SQLException E) {
        throw new RuntimeException("Can't register driver!");
      }
    }

    So it calls a static method in the java.sql.DriverManager class which apparently registers a copy of itself when it loads.

    So now I understand the where and the how, what about why? To understand the why you have to look at the next line in the initial code example:

    Connection con = DriverManager.getConnection(url,
      "myLogin", "myPassword");

    The DriverManager class (view DriverManager source here) returns a database connection given a JDBC URL string, a username and a password. In order to create that connection, the DriverManager class has to know which database driver you want to use. It does that by iterating over the array (internally a Vector) of drivers that have registered with it (ie: the registerDriver(Driver driver) method illustrated above) and calls the acceptsURL(url)) method on each driver in the array, effectively asking the driver to tell it whether or not it can handle the JDBC URL.

    So there you have it. Class.forName explained.

    posted @ 2006-03-02 20:57 killvin| 編輯 收藏

    原文: http://forum.javaeye.com/viewtopic.php?t=17912


    摟主的問題問的含含糊糊:flyjie給出了非常詳細的解釋,
    不過就是沒有解釋String實例化的特殊方面以及Intern()方法的含義

    -------
    ---------------------------------------------------------------------------------
    ---------------------------------------------------------------------------------
    java代碼:
    String str ;

    這樣聲明str它只支是一個對象的reference,不會產生實際的對象。如果沒有初始化str,編譯時便會發生錯誤。
    java代碼:
    String str1=new String("test");
    String str2 = "test";

    str1是一個新的對象。new關鍵字的意思就是創建某個新的對象。而str2是一個對象的引用。 它們的內容相同,但內存地址是不一樣的。 java中對象的引用存在Stack(棧)中,而對象由Heap(堆)分配空間。

    3、引用==變量?  不一定
    java代碼:

      public class TestString {
    public static void main(String[] args) {
    String s1 = "test";
    String s2 = new String("test");
    if (s1 == s2)
    System.out.println("s1 == s2");
    else
    System.out.println("s1 != s2");
    if (s1.equals(s2))
    System.out.println("s1 equals s2");
    else System.out.println("s1 not equals s2");
    }
    }

    我們將 s2 用 new 操作符創建程序輸出:s1 != s2 s1 equals s2.
    java代碼:

    s2 = s2.intern();

    在你加上這句話后,上面的程序輸入:s1 == s2 s1 equals s2

    而String a = "test" ; String b = "test" ; a == b 會返回true; 這里a="test"時創建一個在棧中的reference, b=test時jvm發現棧中已存在名為"test"的字符串,直接引用。結論:String 是個對象,要對比兩個不同的String對象的值是否相同明顯的要用到 equals() 這個方法. 而== 比較的是內存地址的值。

    4、private final String a = "test", 這個a屬于常量,存放在常量存儲空間(CS)中。

    5、建議你看看<<深入淺出java虛擬機>>一書。

    -------
    ---------------------------------------------------------------------------------
    ---------------------------------------------------------------------------------



    總結

    1. 在學習JAVA的時候就知道==比較的是內存地址.而equals比較的是內存地址對應的值!(可是還是有很多的人問來問去的,真不知道他們JAVA的基礎課程是怎么學的?!)

    2. JAVA所有的對象都是存放在堆中的!你獲取的"對象"僅僅只是對象的引用而已

    3. String是比較特殊的對象,特殊在
    3.1 > String a = new String("test") -此時你是在堆中實例化了一個字符串對象
    3.2 > String b = "test"-此時JVM會先去堆中尋找這樣的對象;如果有就返回此對象的引用;如果沒有就重新實例化一個這樣的對象!基于這樣的一個過程所以JAVA要求String不可以更改值的。

    3.3 >intern()方法就是試圖完成這樣的一個尋找過程
    When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

    這里有一份詳細的參考資料:

    關于Java棧與堆的思考 http://www.javafan.net/article/20051123115654293.html


    posted @ 2006-03-02 20:56 killvin| 編輯 收藏

    URL : http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

    網上的一些關于內部類的概念是不完整的,還是看看SUN的文檔上的標準答案。
    ...

    Like other members, a nested class can be declared static (or not). A static nested class is called just that: a static nested class. A nonstatic nested class is called an inner class.

    - Nested class分為靜態Static nested class 的和非靜態的 inner class, 在SUN的眼里只有Nested Class!!


    As with static methods and variables, which we call class methods and variables, a static nested class is associated with its enclosing class. And like class methods, a static nested class cannot refer directly to instance variables or methods defined in its enclosing class — it can use them only through an object reference.

    - 靜態的Static nested class是不可以直接調用它的外部類enclosing class的,但是可以通過外部類的引用來調用,就像你在一個類中寫了main方法一樣。
    ...

    As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object's instance variables and methods. Also, because an inner class is associated with an instance, it cannot define any static members itself.

    -非靜態類inner class 可以自由的引用外部類的屬性和方法,但是它與一個實例綁定在了以其,不可以定義靜態的屬性、方法(這點不是很理解,可能需要看JVM的類實現)

    ...
    class EnclosingClass {
    ...
    class InnerClass {
    ...
    }
    }


    The interesting feature about the relationship between these two classes is not that InnerClass is syntactically defined within EnclosingClass. Rather, it's that an instance of InnerClass can exist only within an instance of EnclosingClass and that it has direct access to the instance variables and methods of its enclosing instance. The next figure illustrates this idea.


    -圖形化的嵌入類與外部類的關系

    posted @ 2006-03-02 20:55 killvin| 編輯 收藏

    僅列出標題
    共5頁: 上一頁 1 2 3 4 5 下一頁 
    主站蜘蛛池模板: 久久精品国产亚洲AV网站| 最新69国产成人精品免费视频动漫 | 在线免费观看国产| 亚洲AV无码国产精品麻豆天美| 三根一起会坏掉的好痛免费三级全黄的视频在线观看 | 免费不卡视频一卡二卡| 亚洲电影免费观看| 在线精品一卡乱码免费| 亚洲欧洲日本天天堂在线观看| 97青青草原国产免费观看| 亚洲一区二区影院| 18禁美女裸体免费网站 | 亚洲一区二区久久| 成人免费视频软件网站| 亚洲av无码专区在线观看下载| 在线观看亚洲免费视频| 青娱乐在线免费观看视频| 亚洲综合色自拍一区| 无码日韩精品一区二区三区免费| 亚洲视频在线观看| 免费无码A片一区二三区| 自拍偷自拍亚洲精品播放| 亚洲国产人成中文幕一级二级| 国产人成网在线播放VA免费| 亚洲A∨无码一区二区三区| 五月婷婷在线免费观看| 亚洲乱码国产乱码精华| 亚洲美女在线国产| 亚洲视频在线免费观看| 亚洲 日韩经典 中文字幕| 免费在线不卡视频| 无码人妻一区二区三区免费n鬼沢 无码人妻一区二区三区免费看 | 国拍在线精品视频免费观看| 亚洲av日韩av永久在线观看| 亚洲人成网站18禁止一区| 91久久精品国产免费一区| 亚洲av无码成人精品国产 | 国产亚洲午夜高清国产拍精品| 一区二区三区观看免费中文视频在线播放| 亚洲最大在线观看| 亚洲国产精品尤物YW在线观看 |