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

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

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

    JAVA涂鴉
    關于JAVA的點點滴滴
    posts - 50,  comments - 689,  trackbacks - 0

    這一章包括:

    n????? 使用 DAO 模式創建抽象層。

    n????? 使用層的父型模式簡化 resource cleanup (資源清除)代碼

    n????? spring 組織你的項目

    ?

    理解 Hibernate 的底層將需要很長一段時間才能在你的項目上使用。但是在 Hibernate 庫的基礎之外,如 SessionFactory (會話工廠), Session (會話), mapping files ( 映射文件) hibernate Query Language(HQL) Hibernate 查詢語言),在一定高度組織一個應用程序并不是總是那么清楚的。 你能在你的項目中應用一些模式和好的經驗。一些好的經驗來自于社區;而另外一些來自應用于持久化的Java企業模式。這一章的目的就是帶給你這些。

    ??? 寫程序有點像用小朋友的 alphabet blocks (譯者注:一種表面有字母的積木)建塔。如果你是建個小塔,那么你不需要仔細關心是怎么堆的。但如果你建一個大的塔,那么你可能要站得更高,你需要稍微不同的技術設置。多一點計劃,多一點怎樣堆的技術和可能使用一些超強的粘合劑。

    ??? 無論是建立玩具塔還是寫軟件都要使用好的工具和技術。因此這一章就是怎樣建立一座高塔。我們將討論一些普通的模式: Data Access Object(DAO) 封裝底層數據操作 )和層的父型模式。另外, spring , 流行的開源項目將為簡化你的代碼提供組織的工具??傊?,這一章將給這個工具一個總的看法和怎樣簡化你的 Hibernate 項目。

    ?

    章節目標:

    在這一章,你將完成一些幾點:

    n???????? 創建抽象層,使用 DAO 模式集中 SQL 語句,因此簡化客戶端對象使用。

    n???????? 使用層的父型模式改善 DAO 對象,簡化 resource cleanup (資源清除)代碼。

    n???????? 使用 spring 組織和簡化你的 DAO 代碼。

    ?

    前提條件:

    假設你已經完成以下幾點:

    n???????? 熟悉模式的概念。

    n???????? 理解 session (會話)和 transaction (事務)的工作原理,特別是怎樣使用持久化被打開 session (會話)鏈接的對象。

    n???????? 你正在尋找一種能組織大型項目的技術。也就是說,使用好的框架代碼將利于項目的以后的擴展。

    ?

    7.1 無處不在的 DAO 模式

    大多數 Java/J2EE 開發者都或多或少熟悉 DAO 模式。它是 Sun 公司特地在 Java 藍圖中強調的核心模式之一,在許多 Java 書籍中都提到過。它是在使用持久化數據存儲的應用程序中的采取的第一個首要模式。一般使用在使用 SQL 的應用程序中,同樣也適用于 Hibernate 的應用程序。

    ?

    7.1.1 集中 HQL

    DAO 模式的目的能在在一個簡單問題的答案中找到:你的數據操作代碼放在哪里?如果你面對一個遺留程序而苦惱,因為這個程序中 SQL 代碼就像鳥槍發射那樣四處都是,而你又沒有這方面的經驗。其實這并沒什么。需要從新命名表中的列嗎?準備好手中的槍,在整個程序中修改,以確保你沒有遺漏任何 SQL 語句。

    DAO 模式鼓勵開發者集中 SQL 語句。那么怎樣才是好的 SQL HQL 呢?在應用程序中將 HQL 放在一個地方以便以后更容易維護和修改。新手可能不能很好的確定將 HQL 放在哪?他們將 HQL 放在DAO中。 Figure 7.1 顯示了 Event EventDao 怎樣和數據庫互動。

    springdao1.jpg

    Figure 7.1 Event EventDao 與數據庫互動的圖

    ?

    通過前面章節知道,一個健壯的對象查詢語句需要有錯誤處理管理,事務和 resource cleanup (資源清除)。在余下的程序中最好隱藏以上那些。例如接下來的 Hibernate 程序,使程序更容易改變 ORM object/relational mapping 對象關系映射 )實現(例如:改變成 JDO 1。更重要的是,這個策略簡化了客戶端怎樣和持久化層互動;他們不需要知道 session (會話), transaction (事務)的邊界,或者在他們使用之后是否被清除。

    ?

    DAO 也有類型

    ??? 你能使用 DAO 兩種基本類型中的一種:

    n???????? 應用級 DAO(DAO per application) :在一個應用中有一個中心的 DAO 對所有的實體對象添、刪、改、查。

    n???????? 類級 DAO(DAO per class) :每個實體類都有自己的 DAO ,用于自身實例的添、刪、改、查。 Event 對象由 EventDao 負責。

    你也可以應用其他鏡像變量,例如使用模塊級 DAO(DAO per module) ,但是最終的選擇還是依賴于你有少個持久化對象。在有許多類的情況下,我們偏愛使用類級DAO策略。應用級 DAO 策略會出現“ bloatware ”類。第二,與類級 DAO 命名對稱能更好的記憶。如果你需要找到 Event 類,你就能夠記得起它的 DAO EventDao 。最后,它滿足開-關原則,原則規定類將因為擴充而打開,因為修改而關閉。你能夠添加一個新的持久化類,而不需要修改中心的 DAO 。因此,讓我們在示例中使用類級 DAO 。

    ?

    簡單的 DAO

    ??? Listing 7.1 顯示了一個簡單的 DAO ,它能處理滿足大多數實體需要的基本的CRUD(添、刪、改、查)業務。除了這些功能,它還有一些功能,因此客戶端對象不用擔心。

    n???????? 包括業務級 session session per operation ;每個 session (會話)都有添、刪、改、查方法。

    n???????? 提供一個業務級 transaction (事務) (transaction per operation) ??蛻舳藢ο蟛挥脫拈_始和提交事務。

    n???????? Hibernate2.1 中,處理捕獲和 Hibernate 拋出的預期異常,將它們變為非預期異常,將不會使客戶端代碼混亂。如果你使用 Hibernate3.x ,你能夠讓沒有從新拋出的非預期異常通過。( In Hibernate 2.x, handles catching and handling the checked exceptions that Hibernate throws, turning them into unchecked exceptions, which won’t clutter up client code. If you use Hibernate 3.x, you can just let the unchecked HibernateException s go without rethrowing .

    n???????? 輸入特性鮮明和明確的 DAO ; EventDat 僅為 Event 工作,意思就是客戶端代碼不必執行手動的操作。( Features strongly typed and explicit DAOs; the EventDao only works with Event s, meaning client code doesn’t have to perform manual casting.

    ?

    Listing 7.1 一個簡單的有 添、刪、改、查方法的 EventDao

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

    package ?com.manning.hq.ch07;
    ?
    import ?org.hibernate.HibernateException;
    import ?org.hibernate.Session;
    import ?org.hibernate.Transaction;
    import ?org.apache.commons.logging.Log;
    import ?org.apache.commons.logging.LogFactory;
    import ?com.manning.hq.ch07.Event;
    import ?com.manning.hq.ch07.HibernateFactory;
    import ?java.util.List;

    /**
    *?DAO管理持久Evnet
    */
    public ? class ?SimpleEventDao?{

    ??? Log?log?
    = ?LogFactory.getLog(SimpleEventDao. class );
    ??? private ?Session?session;
    ??? private ?Transaction?tx;

    ??? public ?SimpleEventDao()?{
    ??? ??? HibernateFactory.buildIfNeeded();
    // 初始化SessionFactory。
    ??? }
    ?
    ??? public ? void ?create(Event?event)? throws ?DataAccessLayerException?{
    ??? ??? try ?{
    ??? ??? ??? startOperation();
    // 打開Session和開始transaction
    ??? ??? ??? session.save(event);
    // 保存Event。
    ??? ??? ??? tx.commit();
    ??? ??? }?
    catch ?(HibernateException?e)?{
    ??? ??? ??? handleException(e);Rolls?back和拋出異常。
    ??? ??? }?
    finally ?{
    ??? ??? ??? HibernateFactory.close(session);
    ??? ??? }
    ??? }

    ??? public ? void ?delete(Event?event)? throws ?DataAccessLayerException?{
    ??? ??? try ?{
    ??? ??? ??? startOperation();
    ??? ??? ??? session.delete(event);
    ??? ??? ??? tx.commit();
    ??? ??? }?
    catch ?(HibernateException?e)?{
    ??? ??? ??? handleException(e);
    ??? ??? }?
    finally ?{
    ??? ??? ??? HibernateFactory.close(session);
    ??? ??? }
    ??? }

    ??? public ?Event?find(Long?id)? throws ?DataAccessLayerException{

    ??? ??? Event?event?
    = ? null ;
    ??? ??? try ?{
    ??? ??? ??? startOperation();
    ??? ??? ??? event?
    = ?(Event)?session.load(Event. class ,?id);
    ??? ??? ??? tx.commit();
    ??? ??? }?
    catch ?(HibernateException?e)?{
    ??? ??? ??? handleException(e);
    ??? ??? }?
    finally ?{
    ??? ??? ??? HibernateFactory.close(session);
    ??? ??? }
    ??? ??? return ?event;
    ??? }

    ??? public ? void ?update(Event?event)? throws ?DataAccessLayerException?{
    ??? ??? try ?{
    ??? ??? ??? startOperation();
    ??? ??? ??? session.update(event);
    ??? ??? ??? tx.commit();
    ??? ??? }?
    catch ?(HibernateException?e)?{
    ??? ??? ??? handleException(e);
    ??? ??? }?
    finally ?{
    ??? ??? ??? HibernateFactory.close(session);
    ??? ??? }
    ??? }

    ??? private ? void ?handleException(HibernateException?e)? throws ?DataAccessLayerException?{
    ??? ??? HibernateFactory.rollback(tx);
    ??? ??? throw ? new ?DataAccessLayerException(e);
    ??? ??? // ?二選一,你能夠從新拋出,就像…
    ??? ??? // ?throw?e;
    ??? }

    ??? private ? void ?startOperation()? throws ?HibernateException?{
    ??? ??? session?
    = ?HibernateFactory.openSession();
    ??? ??? tx?
    = ?session.beginTransaction();
    ???? }
    }


    package ?com.manning.hq.ch07;

    public ? class ?DataAccessLayerException? extends ?RuntimeException?{
    // 其他的構造函數省略。
    ??? public ?DataAccessLayerException(Throwable?cause)?{
    ??? ??? super (cause);
    ??? }
    }

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

    ??? SimpleEventDao 是個極其簡單的 DAO ,它有添,刪,改,查四種方法。每種方法在一個 transaction (事務),打開和關閉一個 session (會話)范圍內處理業務。它是明確的,也就是說每個 Event 都有獨有的業務處理,因此客戶端類不需要再處理這些了。當這個實現是簡單的(以后可能隨著擴展會變大),客戶端代碼在運行 event 時代碼就會變得很短。( While this implementation is simple (perhaps overly so, as we will explore here later), it greatly shortens the client code that works with events. )因此創建和查詢一個event就象下面的一樣簡單:---------------------------------------------------------------------------------------------

    Event?event? = ? new ?Event();
    event.setName(
    " A?new?Event " );
    EventDao?eventDao?
    = ? new ?EventDao();
    eventDao.create(event);
    Event?foundEvent?
    = ?eventDao.find(event.getId());

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

    正如你看到的,不需要處理凌亂的異常,這里也不需要處理 resource cleanup (資源清除)。現在讓我們討論更多一些這個實現出現的問題,并且我們該怎樣提高它。

    ?

    7.2 分析 DAO

    ??? 我們在前面章節練習的簡單的DAO實現出現了一些問題,有些你或許已經遇到。讓我們看一看。

    ?

    7.2.1 boilerplate code 樣板代碼)

    Listing 7.1 包括許多資源管理和異常處理代碼。每個方法都有打開 session (會話),開始事務,執行自己的商務業務,提交事務,事務回滾,最后關閉 session (會話)。每個方法基本就像下面那樣:

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

    ? try ?{
    ???? startOperation();
    ???? session.save(event);
    ???? tx.commit();
    ?}?catch?(HibernateException?e)?{
    ???? handleException(e);
    ?}?finally?{
    ???? HibernateFactory.close(session);
    ?}

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

    Session.save(event) 這一行是每個方法中唯一改變的。甚至 refactoring 幾個便利的方法(譯者注:找了不少詞典,都沒有找到“ refactor ”),如 startOperation () handleException () ,都不能完全擺脫你的樣板代碼。( Even refactoring out a few convenience methods,such as startOperation() and handleException() , doesn’t completely rid you of boilerplate code )一個 potential solution (潛在的解決方案)就是在7.3節討論的層的父型模式。

    ?

    7.2.2 P otential duplication 潛在的復制)

    ??? 增加一個新的 DAO 變得很簡單,只需要復制和粘貼。如果你需要其他的 DAO ,如 LocationDao , 我們將復制和粘貼 EventDao ,然后在每個方法中改變關聯的少許幾行代碼。因為我們知道復制是所有程序的惡魔之首,顯然需要做點事情。建立層的父型模式是非常有幫助的。

    ?

    7.2.3 游離對象

    ??? 因為每個方法都是與單一的 session (會話)和 transaction (事務)運作的,與 DAO 運作的所有的 Event 是嚴格的游離對象。這個行為也許是美好的,但它不能夠利用 Hibernate 的自動臟對象檢查和 session-level object cache (會話級對象緩存)。例如,假如客戶端這樣寫:

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

    Event?foundEvent? = ?eventDao.find(event.getId());
    foundEvent.setDuration(
    30 );
    eventDao.update(foundEvent);

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

    Find 運行在一個 session (會話)中, update 運行在另一個 session (會話)中。它一定會運行,但如果 find update 能以某種方法共享一個 session (會話)那才是真正的好。并且,它能更好的避免在 session (會話)周圍的凌亂的方法署名。當它運作時,它是難看的,因此我們不想看到下面這樣:

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


    Session?session?
    = ?HibernateFactory.openSession();
    Event?foundEvent?
    = ?eventDao.find(event.getId(),?session);
    foundEvent.setDuration(
    30 );
    eventDao.update(foundEvent,?session);
    ?

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

    ??? 給方法增加 session (會話)參數,強制責任,管理和在客戶端代碼共享 session (會話)。這樣會帶來重復,復雜和潛在的錯誤。

    ??? 這個問題的一個潛在的解決方案是眾所周知的 Thread Local Session 模式。這個模式將在第八章介紹,因此我們不會在這里直接介紹。我們改用另外一種框架練習, spring ,它使用 Thread Local Session 模式,在下面會有介紹。

    ?

    7.3 層的父型模式

    ??? 常規的 J2EE 智者認為應用程序被劃分成不同的層。假設你的應用程序已經有這些層了,當然,依賴于你所讀的書。一些普遍的層的選擇如下:

    n???????? 表示層,這里是所有用戶交互和表示的代碼。

    n???????? 領域層,這里是邏輯的商務代碼。

    n???????? 持久層,這里是數據存儲操作的代碼。

    ??? 不管你的應用程序是否有這些層,在層中的每個對象都有某些共同的代碼能夠被固定到一個類中,這一點是非常普通的。這個原因出現了層的父型模式,每一層都有“一種類型,這種類型作為所有的層中的類型的父型”2。你能夠使用父型模式簡化你的 DAO 。

    ??? Figure7.2 用一個簡單的層次顯示了層的父型 AbstractDao ,它提供了 protected 方法,在子類中可以覆蓋和改為 public 。

    springdao2.JPG

    ??? Figure 7.2 層的父型 AbstractDao 的圖

    ??? 下一步就是創建 AbstractDao ,下一節將介紹。

    ?

    7.3.1 創建 AbstractDao

    ??? 第一步是創建你的父型, AbstractDao ,所有的 DAO 最后將擴展。 Listing 7.2 顯示了類的內容。

    Listing 7.2 層的父型實現, AbstractDao ,它有所有 DAO 的共同的業務。

    -----------------------------------------------------------------------------------------
    ??

    package?com.manning.hq.ch07;

    import?org.hibernate.HibernateException;
    import?org.hibernate.Query;import?org.hibernate.Session;
    import?org.hibernate.Transaction;
    import?java.util.List;
    ??
    /**
    *?層的父型處理所有DAO共同的運作
    */

    public?abstract?class?AbstractDao{

    ??? private?Session?session;
    ??? private
    ?Transaction?tx;

    ??? public?AbstractDao()?{
    ??? ??? HibernateFactory.buildIfNeeded();
    ??? }

    ??? protected?void?saveOrUpdate(Object?obj){//運作是普通的,而不是特指域對象
    ??? ??? try?{
    ??? ??? ??? startOperation();
    ??? ??? ??? session.saveOrUpdate(obj);
    ??? ??? ??? tx.commit();
    ??? ??? }?
    catch?(HibernateException?e)?{
    ??? ??? ??? handleException(e);
    ??? ??? }?
    finally?{
    ??? ??? ??? HibernateFactory.close(session);
    ??????? }
    ??? }

    ???
    protected?void?delete(Object?obj)?{
    ??? ??? try?{
    ??? ??? ??? startOperation();
    ??? ??? ??? session.delete(obj);
    ??? ??? ??? tx.commit();
    ??? ??? }?
    catch?(HibernateException?e)?{
    ??? ??? ??? handleException(e);
    ??? ??? }?
    finally?{
    ??? ??? ??? HibernateFactory.close(session);
    ??? ??? }
    ??? }

    ??? protected
    ?Object?find(Class?clazz,?Long?id){//查找基于類和id的持久化對象

    ??? ??? Object?obj?
    =?null;

    ??? ??? try?{
    ??? ??? ??? startOperation();
    ??? ??? ??? obj?
    =?session.load(clazz,?id);
    ??? ??? ??? tx.commit();
    ??? ??? }?
    catch?(HibernateException?e)?{
    ??? ??? ??? handleException(e);
    ??? ??? }?
    finally?{
    ??? ??? ??? HibernateFactory.close(session);
    ??? ??? }
    ??? ??? return?obj;
    ??? }

    ??? protected?List?findAll(Class?clazz)?{

    ??? ??? List?objects?
    =?null;

    ??? ??? try?{
    ??? ??? ??? startOperation();
    ??? ??? ??? Query?query?
    =?session.createQuery("from?"?+?clazz.getName());
    ??? ??? ??? objects?
    =?query.list();
    ??? ??? ??? tx.commit();
    ??? ??? }?
    catch?(HibernateException?e)?{
    ??? ??? ??? handleException(e);
    ??? ??? }?
    finally?{
    ??? ??? ??? HibernateFactory.close(session);
    ??? ??? }
    ??? ??? return?objects;
    ??? }

    ??? protected?void?handleException(HibernateException?e)??throws?DataAccessLayerException?{
    ??? ??? HibernateFactory.rollback(tx);
    ? ?? ? throw?new?DataAccessLayerException(e);
    ??? }

    ??? protected?void?startOperation()?throws?HibernateException?{
    ??? ??? session?
    =?HibernateFactory.openSession();
    ??? ??? tx?
    =?session.beginTransaction();
    ??? }
    }


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

    ????在這個Listing中,你看到了共同的CRUD方法,包括save,?find和delete方法都放入AbstractDao類中。他們是 generic和protected,因此子類能夠調用它們。這樣你的EventDao將簡化。這里是一個簡單的簡化了的方法:

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

    public?????class???ImprovedEventDao???extends???AbstractDao?{
    //??其他的方法省略
    ??? public???void??create(Event?event)??throws??DataAccessLayerException?{
    ??? ??? saveOrUpdate(event);
    ??? }

    ??? public??Event?find(Long?id)??throws??DataAccessLayerException?{
    ??? ??? return??(Event)?find(Event.?class?,?id);
    ??? }
    }
    --------------------------------------------------------------------------------------------
    ??? ImprovedEventDao的僅有的責任就是執行業務和委托調用父類。樣板代碼和潛在的復制的雙重問題得到解決。當我們增加一個新的實體對象時,如:Locations或者Speakers,添加新的DAO
    --使用AbstractDao作為層的父型,將是非常迅速的。
    --------------------------------------------------------------------------------------------

    public?classImprovedLocationDao extendsAbstractDao?{
    //其他的方法省略
    ??? publicvoid create(Location?location) throws??DataAccessLayerException?{
    ??? ??? saveOrUpdate(location);
    ??? }

    ??? publicLocation?find(Long?id)??throws??DataAccessLayerException?{
    ??? ??? return??(Location)?find(Location.?class?,?id);
    ??? }
    }
    ---------------------------------------------------------------------------------------------

    通過層的父型的介紹,剩余的問題得到解決, DAO 與游離對象運作。我們想在我們的方法中共享會話,甚至通過不同的 DAO 。為了做到這一點,我們將學習一個新的流行框架, Spring .

    ?

    7.4 Spring 框架

    ?????? 我們已經注意到已經定義的 DAO 實現中的一些缺陷。復制資源管理代碼和使用業務級的 session session per operation )使解決方案會比我們需要的更加復雜,而且也沒有我們喜歡的靈活。我們一定能過編寫更好的更健壯的解決方案。幸運的是,我們不需要擔心 --- 一個優秀的開源解決方案, Spring 框架已經提供給我們了。

    ?????? Spring 解決了比幫助我們解決 Hibernate 更多的問題。這是事實,“一個輕量級的容器,允許開發者連接商務對象, DAO 和資源就像 JDBC DataSources Hibernate SessionFactories 。” 3?? 它使用中心 XML 配置文件去管理資源,甚至它自身的 web MVC 框架( Model-View-Controller )。 Spring 是普通的框架,就是說它可以使用在許多不同的位置中。如果你不熟悉 Spring ,你也許會想我該怎樣使用它,你也許認為他僅僅是個框架,假設用來提供某種編譯。因此我們將在這里展示。

    ?????? Spring 已經被考慮成熟的分割為穩固的集中的若干模塊,包括我們先前提到的 MVC web 框架, JDBC 支持, aspect-oriented programming AOP )( 面向剖面編程 )和 ORM 模塊。這樣就允許你使用其中你需要的,而不用學習或者考慮其他的。為了我們的目的,我們將僅僅學習怎樣簡化你的 Hibernate 代碼,也就是 ORM 模塊。最佳的開始的地方是模板。

    ?????? 首先,你需要獲得 Spring 框架的副本,你可以在 www.springframework.org 找到。解壓到 applications 目錄下的 Hibernate 旁邊。 Spring 有許多可選擇的包,但為了簡單,你只需要考慮一個 JAE 包,它就是 applications\spring-framework-1.2-rc2\dist\spring.jar 文件。將它加入到 build.xml 文件的 classpath 中。

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

    < property?name = " spring.version " ?value = " 1.2-rc2 " />
    < property?name = " spring.lib.dir "
    ??? value
    = " ${applications.dir}/spring-framework-${spring.version} " />
    < path?id = " spring.lib.path " >
    < fileset?dir = " ${spring.lib.dir}\dist " >
    < include?name = " **/spring.jar " />
    </ fileset >
    </ path >
    < path?id = " runtime.classpath " >
    // ?其他的?paths?省略。
    < path?refid = " spring.lib.path " />
    < path >

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

    ?????? 這個代碼配置了 Spring 使它能過在我們的示例項目中使用。 Hibernate3 最近已經發布(在出版的時候),其他支持項目如 Spring 也跟上提供支持了。這里我們使用最新的版本。另外,因為 Spring 不得不支持 Hibernate 2 Hibernate 3, 一個新的包, org.springframework.orm hibernate3 已經將它加入到 Hibernate3 的項目中了。接下來,讓我們看看 Spring 怎樣被使用來簡化我們的示例項目。

    ?

    7.4.1 在模板中是什么

    ??? Spring 給我們提供了 Hibernate 業務的模板。模板有什么,我們為什么需要它?答案就在我們的 SimpleEventDao 中的 create 方法中。

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

    protected ? void ?create(Event?event)?{
    ??? try ?{
    ??? ??? startOperation();
    ??? ??? session.save?(event);
    ??? ??? tx.commit();
    ??? }?
    catch ?(HibernateException?e)?{
    ??? ??? handleException(e);
    ??? }?
    finally ?{
    ??? ??? HibernateFactory.close(session);
    ??? }
    }

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

    ??? 如果你注意了,方法中只調用一個真正的事件是: save ()。其他的每一行,我們喜歡稱為“ excise (稅)”。 excise (稅)是我們為了工作不得不去做的額外的事情,這些事情不是真正的直接重要。它就像當你開車去上班,駕駛的實際行動才是重要的事情;開車庫門和倒車都是 excise (稅)任務,它可以看成同等重要,也可以忽略或者自動完成。

    ?????? 在程序中, excise (稅)是框架或者語言安全需要,你不得不編寫的代碼。一個典型的 excise (稅)例子就是 Java 除去了內存管理。 Spring 能夠除去 Hibernate JDBC 要求之下的資源管理 excise (稅)的一部分。

    ?????? 一般情況下,當你復制代碼,你能夠 refactor 一個方法或類。這里,因為復制代碼是商務方法周圍的 resource? cleanup (資源清理),使它更加復雜?,F在有模板可用。導入 Spring 提供的類: org.springframework.orm.hibernate3.HibernateTemplate 。它包含了所有資源處理代碼以致于你僅只要寫一個重要方法就可以了。我們的 create() 方法能夠這樣寫:

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

    import ?org.hibernate.Hibernate;
    import ?org.hibernate.SessionFactory;
    import ?org.springframework.orm.hibernate3.HibernateTemplate;

    protected ? void ?create(Event?event)?{
    ??? SessionFactory?sf?
    = ?HibernateFactory.getSessionFactory();
    ??? HibernateTemplate?template?
    = ? new ?HibernateTemplate(sf);
    ??? template.saveOrUpdate(event);
    }

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

    注意我們沒有做什么:

    n???????? SessionFactory 獲得 session

    n???????? 開始事物

    n???????? 捕獲預期異常和將他轉變為非預期異常( 3.x 版本不需要, 2.x 則需要)

    n???????? 提交事物

    n???????? 將改變輸入數據庫

    n???????? 關閉 session

    這里有許多事情我們不需要擔心,因為 HibernateTemplate 已經照顧到了。你或許注意到 HibernateTemplate 看上去像包裹 Session 的周圍。實際上,可以把 HibernateTemplate 理解為一個“聰明”的 Session ,它知道怎樣打開,關閉和在運行完清除。在之前默認的情況下,它使用的是方法級事務( transaction per method )模式。這是非常簡單的,但你將在后面看到你也能夠改事務的范圍。這里有兩個基本的方式和 Hibernatetemplate 互動:通過 convenience mehod (便利的方法)和重要的 callback (回調)。

    ?

    Convenience methods (便利的方法)

    ??? 簡單的事情將變得容易。在許多案例中,你想用 Session (會話)做什么是非常重要的:執行保存,更新有力對象,或者運行 HQL 查詢。沒有一個需要禮儀來獲得完成。 HibernateTemplate 類提供了基本的方法,因此一行代碼就可以簡單的調用業務。這里有一些方法的簡單樣例:

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

    import ?com.manning.hq.ch07.Event;
    import ?com.manning.hq.ch07.HibernateFactory;
    import ?org.springframework.orm.hibernate3.HibernateTemplate;
    import ?java.util.List;

    SessionFactory?sessionFactory?
    = HibernateFactory.getSessionFactory();
    // ?創建連接?SessionFactory?的模板
    HibernateTemplate?template?
    = new ?HibernateTemplate(sessionFactory);
    Event?event1?
    = ? new ?Event();
    event1.setName(
    " Event?1 " );
    Event?event2?
    = ? new ?Event();
    event2.setName(
    " Event?2 " );

    ??? try ?{
    ??? ??? template.save?(event1);???
    // ?在一個事務中保存?event
    ??? ??? template.save?(event2);
    // ?加載一個?event
    ??? ??? Event?obj?
    = ?(Event)?template.load(Event. class ,?event1.getId());
    ??? ??? System.out.println(
    " Loaded?the?event " ? + ?obj.getName()); // ?找到所有的?event
    ??? ??? List?events?
    = ?(List)?template.find( " from?Event " );
    ??? ??? System.out.println(
    " #?of?Events? " ? + ?events.size());
    ??? }?
    finally ?{
    ??? ??? template.delete(event1);?
    // ?刪除一個?event
    ??? ??? template.delete(event2);
    ??? }

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

    convenience methods 便利的方法)是有代表性的正確的命名,就像 Session (會話)中的方法一樣。他們能被 session (會話)作為 one-for-one (一對一)復位直接調用,沒有任何雜亂的 resource cleanup (資源清除)代碼障礙。

    ?

    Callback (回調)

    ??? 復雜的事情可能有好處。不是所有的在單個 transaction ( 事務)中的單個 query (查詢)能夠輕松的減少業務。在這些業務中, spring 提供了 Callback 接口。它允許你在將要執行的模板中編寫 callback 方法。例如,如果你想構建一個復雜的 query (查詢),更新一些數據,然后保存,所有的都在一個業務中:

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

    import ?org.hibernate.HibernateException;
    import ?org.springframework.orm.hibernate3.HibernateCallback;
    import ?java.sql.SQLException;
    import ?org.hibernate.Query;
    import ?java.util.List;
    import ?java.util.Iterator;
    import ?com.manning.hq.ch07.Event;?

    template.execute(
    new ?HibernateCallback()?{
    ??? public ?Object?doInHibernate(Session?session)? throws ?HibernateException,?SQLException?{
    ?? ??? Query?query?
    = ?session.createQuery( " from?Event " );
    ??? ??? query.setMaxResults(
    2 );
    ??? ??? List?events?
    = ?query.list();
    ??? ??? for ?(Iterator?it? = ?events.iterator();?it.hasNext();)?{
    ??? ??? ??? Event?event?
    = ?(Event)?it.next();
    ??? ??? ??? event.setDuration(
    60 );
    ??? ??? }
    ??? ??? return ? null ;
    ??? }
    })?;

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

    這里, Callback 接口使用了匿名內部類, HibernateCallback ,定義了一個單一的方法, doInHibernate() 。你可以編寫方法主體,然后提交 HibernateCallback 對象給模板,然后執行。模板處理資源管理代碼,讓你只需要編寫任務的 query (查詢)邏輯。

    ?

    7.4.2 Beans 和 它們的 factories (工廠)

    你已經看到能夠程序化使用 Spring 去減少 resource cleanup (資源清除)代碼。另外i,它能過更好的組織項目。 Spring 的慣例的說法是“輕量級”容器。它勝任執行和配置簡單的 JavaBeans 。它基本扮演構建和配置你應用程序中的 beans 的工廠角色。意思是它能夠被使用去配置大多數現存的 architectures (框架)和庫,包括 Hibernate 。

    ?

    中心的配置文件

    ??? 在這一點上,你將通過組合使用 hibernate.cfg.xml 文件( declaratively 聲明性的 ))和使用靈活的編程方式,如HibernateFactory來配置HibernateSpring提供了另外一個方法整體聲明配置Hibernate。使用Spring的最大好處就是你能夠減少編程配置需要的元素。

    ?

    Spring 能夠讀用一般配置格式編寫的 XML 文件。 XML 指定了怎樣連接不同對象,包括 DataSource (數據源), SessionFactory 和所有的 DAOs 。一旦你配置了文件,你就能夠將它作為查找 DAOs 的中心“票據交換所”使用。例如,在 classpath root (根部)創建一個叫 applicationContext.xml 文件。就像 listing7.3 所顯示那樣。

    ?

    listing7.3 ApplicationContext.xml ,定義了 DataSource (數據源), SessionFactory DAO

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

    ?? <? xml?version = " 1.0 " ?encoding = " UTF-8 " ?>

    ? <! DOCTYPE?beans?PUBLIC

    ??? ? " -//SPRING//DTD?BEAN//EN "

    ??? ? " http://www.springframework.org/dtd/spring-beans.dtd " >

    ??? <
    beans >
    ??? ??? < bean?id = " dataSource " class = " org.apache.commons.dbcp.BasicDataSource " ????? ??? destroy - method = " close " > ???①
    ??? ??? < property?name = " driverClassName " >
    ??? ??? ??? < value > com.mysql.jdbc.Driver </ value >
    ??? ??? </ property >
    ??? ??? < property?name = " url " >
    ??? ??? ??? < value > jdbc:mysql: // localhost/events_calendar</value>
    ??? ??? </ property >
    ??? ??? < property?name = " username " >
    ??? ??? ??? < value > root </ value >
    ??? ??? </ property >
    ??? ??? < property?name = " password " >
    ??? ??? ??? < value ></ value >
    ??? ??? </ property >
    ??? ??? </ bean >

    ??? ??? < bean?id = " factory " class = " org.springframework.orm.hibernate3.LocalSessionFactoryBean " > ?②
    ??? ??? < property?name = " mappingResources " >
    ??? ??? ??? < list >
    ??? ??? ??? ??? < value > com / manning / hq / ch07 / Event.hbm.xml </ value >
    ??? ??? ??? ??? < value > com / manning / hq / ch07 / Location.hbm.xml </ value >
    ??? ??? ??? </ list >
    ??? ??? </ property >
    ??? ??? < property?name = " hibernateProperties " >
    ??? ??? ??? < props >
    ??? ??? ??? ??? < prop?key = " hibernate.dialect " > org.hibernate.dialect.MySQLDialect </ prop >
    ??? ??? ??? ??? < prop?key = " hibernate.show_sql " > false </ prop >
    ??? ??? ??? </ props >
    ??? ??? </ property >
    ??? ??? < property?name = " dataSource " > ???③
    ??? ??? ??? < ref?bean = " dataSource " />
    ??? ??? </ property >
    ??? ??? </ bean >

    ??? ??? < bean?id = " eventDao " class = " com.manning.hq.ch07.EventSpringDao " > ????④
    ??? ??? < property?name = " sessionFactory " > ?????⑤
    ??? ??? ??? < ref?bean = " factory " ? />
    ??? ??? </ property >
    ??? ??? </ bean >
    ??? </ beans >

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

    listing7.3 解釋

    ??? 配置一個基本的數據源,它使用由 Hibernate 發布的 Apache Commons database connection pool (DBCP)

    ?????? 配置一個 SessionFactory ,構建在 Spring SessionFactory wrapper, LocalSessionFactoryBean 。當 Spring 讀取這個文件時,它就構建一個 SessionFactory SessionFactory 存儲在 Key factory

    ??????? 連接 SessionFactory 和數據源。

    ??????? 配置你的 EventSpringDao eventDao 。

    ??????? 連接 DAO session factory 。他允許 DAO 打開 session 和發布 queries

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

    Listing 7.3 XML 配置文件列舉了所有能夠經常改變詳細內容。它完成了許多與 hibernate.cfg.xml 所作的相同的事情,也為我們構建了 SessionFactory ,這一點將在下一節看到。

    ?

    構建 AppicationContext

    ??? 你剛剛創建的 applicationContext.xml 詳細的描述了怎樣構建 session 工廠。它基本上可以一對一的替換你所看到的 HibernateFactory 使用的 hibernate.cfg.xml 。它定義了通常在 hibernate.cfg.xml 中的屬性和映射文件。在我們先前的示例代碼中,你需要構建 SessionFactory ,或者通過你的 EventDao 對象連接 SessionFactory 。 Spring 顛覆了這個概念。 Spring 為你構建了 EventDao ,你需要詢問 EventDao 的首選項,就像:

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

    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import ?com.manning.hq.ch07.Event;

    ClassPathXmlApplicationContext?ctx?
    = ? new
    ClassPathXmlApplicationContext(
    " applicationContext.xml " );
    EventSpringDao?eventDao?
    = (EventSpringDao)?ctx.getBean( " eventDao " ,?EventSpringDao. class );
    Event?event?
    = ? new ?Event();
    eventDao.saveOrUpdate(event);
    --------------------------------------------------------------------------------------------

    ClasspathXmlApplicationContext 看上去 classpath 中,因為配置文件的名字在 instruction 中已提供( The ClasspathXmlApplicationContext looks in the classpath for the name of the configuration file provided in the instructions. )在這個案例中, applicationContext.xml classpath root (根)。你能夠從 application context 通過名字請求 bean 。 getBean ()方法有兩個參數: bean 的名字( eventDao ),你期望的類的類型( EventSpringDao )。

    在此之下, Spring 構建了 SessionFactory 并將所有的 Bean 連接在一起。我們更早的認識到 Spring Javabean 一起工作。所有的在 applicationContext.xml 文件中的 <bean> 元素都需要 JavaBean 。這包括了象下面的 EventSpringDao 。

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

    public ? class ?EventSpringDao? extends ?AbstractSpringDao{
    ??? public ?EventSpringDao(){}
    ??? public ?Event?find(Long?id){
    ??? ??? return ?(Event)? super .find(Event. class ,?id);
    ??? }
    ??? // ?Other?methods?excluded
    }

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

    ???????? 另外更早的認識到的好處是, Spring 提供了 org.springframework.orm.hibernate3.support.HibernateDaoSupport , 應用 Dao 層的父型。她管理了 SessionFactory 和一些有用的方法去處理 Session , Logging HibernateTemplate 。這有這些方法的示例:

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

    public ? abstract ? class ?HibernateDaoSupport? implements ?InitializingBean?{
    ??? protected ? final ?Log?logger;
    ??? private ?HibernateTemplate?hibernateTemplate;
    ??? public ? final ? void ?setSessionFactory(SessionFactory?sessionFactory);
    ??? public ? final ?SessionFactory?getSessionFactory();
    ??? public ? final ? void ?setHibernateTemplate(HibernateTemplate?hibernateTemplate);
    ??? public ? final ?HibernateTemplate?getHibernateTemplate(); ? ??? ??? ????? ??? ??? ??? ??? ??? ?? ??? ??? protected ? final ?Session?getSession()? throws? ??? DataAccessResourceFailureException,?IllegalStateException;
    ??? protected?final?void?closeSessionIfNecessary(Session?session);
    }

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

    他提供了一些基本的方法,但我們選擇重寫 HibernateDaoSupport 對象,為了 提供更多的便利方法。 Listing7.4 顯示了改變的類。

    Listing 7.4 你的應用的層的父型 DAOs

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

    package ?com.manning.hq.ch07;

    import ?java.util.List;
    import ?org.springframework.orm.hibernate3.support.HibernateDaoSupport;

    public ? abstract ? class ?AbstractSpringDao? extends ?HibernateDaoSupport{
    ??? public ?AbstractSpringDao()?{?}
    ??? protected ? void ?saveOrUpdate(Object?obj)?{
    ??? ??? getHibernateTemplate().saveOrUpdate(obj);
    ??? }

    ??? protected ? void ?delete(Object?obj)?{
    ??? ??? getHibernateTemplate().delete(obj);
    ??? }
    ??? protected ?Object?find(Class?clazz,?Long?id)?{
    ??? ??? return ?getHibernateTemplate().load(clazz,?id);
    ??? }

    ??? protected ?List?findAll(Class?clazz)?{
    ??? ??? return ?getHibernateTemplate().find( " from? " ? + ?clazz.getName());
    ??? }
    }

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

    重點注意的是 AbstractSpringDao 使用它的父類 sessionFactory 成員。 HibernateDaoSupport 提供了 getter setter 方法, Spring 使用 setter 方法去連接 SessionFactory 。從 applicationContext .xml 文件中調用這些行:

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

    < bean?id = " eventDao " ? class = " com.manning.hq.ch07.EventSpringDao>
    ??? < property?name = " sessionFactory " >
    ??? ??? < ref?bean = " factory " ? />
    ??? </ property >
    </ bean >

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

    這是調用 setSessionFactory () 的片斷,通過我們配置的 SessionFactory ,我們叫它為工廠。你所看到的 AbstractSpringDao 是由 AbstractDao 進化而來,你可以從 find() 方法中完整的除去大多數資源管理代碼。 HibernateTemplate HibernateDaoSupport 替代了被控制的一切。

    ?

    創建注冊( Creating a registry )

    我們最終是為了將所有集中到一起并創建中心的注冊,開發者能夠使用它直接獲得參數給 DAO SessionFactory 。通過單一的類, CalendarRegistry ,你能確保將來開發有一個顯示的,單一的,強壯類型的類來使用,不需要知道類的詳細內部機制。 Figure 7.3 顯示了怎樣將所有集中到一起。

    使用 Spring ,你獲得了配置的好處,允許你輕松的交換數據資源,數據庫和添加新的對象成員。 Listing7.5 就是 CalendarRegistry 。

    springdao3.JPG

    ?

    Figure 7.3 CalendarRegistry 圖表,獲得參數到 EventDao

    ?

    Listing 7.5 CalendarRegistry , 組織 Dao 的中心的類

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

    package ?com.manning.hq.ch07;

    import ?org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import ?org.hibernate.SessionFactory;

    public ? class ?CalendarRegistry?{
    ??? private ? static ?ApplicationContext?ctx;
    ??? static ?{
    ??? ??? ctx?
    = ? new ?ClassPathXmlApplicationContext( " applicationContext.xml " );
    ??? }
    ??? private ?CalendarRegistry()?{}
    ??? public ? static ?SessionFactory?getSessionFactory()?{
    ??? ??? return ?(SessionFactory)?ctx.getBean( " factory " ,?SessionFactory. class );
    ??? }
    ??? public ? static ?EventSpringDao?getEventDao()?{
    ??? ??? return ?(EventSpringDao)ctx.getBean( " eventDao " ,?EventSpringDao. class );
    ??? }
    }

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

    正如你所見, CalendarRegistry 是單例模式,但是因為它背后是 Spring ,你能夠輕松的實現底層的交換。它從 classpath 中裝載單一的靜態的 ApplicationContext ,然后使用它獲取參數?,F在客戶端對象能夠在項目中任何地方獲得參數給 EventDao ,不需要知道關于 Spring 做了什么。

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

    EventSpringDao?eventDao? = ?CalendarRegistry.getEventDao();
    eventDao.saveOrUpdate(event);

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

    ?

    更多的 Spring 工具

    就像你所見,使用 Spring 能夠最大限度的簡化 Hibernate 的資源管理代碼。我們顯示了兩個你能夠使用的包含的級別。 HibernateTemplate 能夠被嵌入你的 DAO 中,或者使用 Spring 的輕量級容器去管理 DAO

    ??? Spring is a fairly straightforward framework, but we haven’t scratched the surface of what it can do here. Spring 還算一個直接的框架,但我們沒有就它能在這做什么過多的糾纏) Spring 也支持事務 API 管理框架, AOP 框架,一個 RuntimeException 框架,能夠攔截數據庫發出的大多數遲鈍的 SQLException ,將它轉變成更顯而易見和包羅萬象的 exception 。更多的信息,請查看實在又全面的 Spring 文檔。

    ?

    7.5  總結

    這一章的焦點在于提高組織你的應用程序代碼。我們集中了 HQL ,使用了 DAO 模式。我們通過加入其它的模式更提高了初始實現,層的父型,它允許增加更多的DAO,不需要復制過多的代碼到項目中新添加的對象成員。

    ??? 這一章也探索了怎樣使用 Spring ,另一個流行的開源項目,管理 Hibernate 需要的 boilerplate (樣板)資源管理代碼。 Spring 提供了標題選項, HibernateTemplate , pluggable , ApplicationContext 。增加一個 CalendarRegistry , 它提供了方法使用 Spring 獲得項目中的參數,不再需要去包含一個“單塊集成電路”了。

    ?

    1 Hibernate 如此優秀,為什么還要用別的?在現實中,轉變 ORM 實現并不是微不足道的, DAO 是容易產生漏洞的,提取那樣做的話就會有問題,將不能從應用程序中完全隱藏 Hibernate 。( DAOs are leaky enough abstractions that doing so probably won’t completely hide Hibernate from the application. )。 因此不需要放太多的精力去密封 DAO 層,在以后為了可能的某種目的要轉變 ORM 實現。

    ?

    2 選自 Patterns of Enterprise Application Architecture , 作者: Martin Fowler(Addision-Wesley 專家, 2003 )

    ?

    3 選自在線文章“ Data Access with Spring Framework ”,作者: Juergen Hoeller , 2003 7 月; http://hibernate.bluemars.net/110.html

    posted on 2005-12-21 22:21 千山鳥飛絕 閱讀(10515) 評論(6)  編輯  收藏 所屬分類: Hibernate

    FeedBack:
    # re: 用spring組織DAO
    2005-12-23 15:40 | xmlspy
    在這一點上,你將通過組合使用hibernate.cfg.xml文件(declaratively(公告的))和一些活躍的(programmatic)標題,如HibernateFactory來配置Hibernate。Spring提供了另外一個選擇去整體declaratively(公告的)配置Hibernate。使用Spring的最大好處就是你能夠減少(programmatic)標題配置需要的元素。

    -----------------
    翻譯得很變態 :(

    declaratively ----聲明性的

    一些活躍的(programmatic)---- 使用靈活的編程方式

    上面兩個是相對應的:
    聲明性的(指用配置文件) -- 編程方式的、硬編碼的(指寫java代碼)
      回復  更多評論
      
    # re: 用spring組織DAO
    2005-12-27 22:34 | 千山鳥飛絕
    謝謝xmlspy的指正  回復  更多評論
      
    # re: 用spring組織DAO
    2006-03-11 17:24 | skdk
    很通俗易懂的文章,在網上搜索了好久關于Spring DAO 的話題,這篇是我看到最好的, 比較全面的,比較清晰的一篇文章,希望,,在多發,為我們初學Spring 的,提供一個良好的平臺,謝謝??!
      回復  更多評論
      
    # re: 用spring組織DAO
    2007-05-20 19:40 | zhou
    樓主寫得不錯,樓主說的開發模式,我一直用,哈哈,有空大家研究一下  回復  更多評論
      
    # re: 用spring組織DAO
    2007-08-24 10:15 | chelson
    這本書名叫什么? 感覺作者的寫作思維相當流暢, 讀起來一個字---爽
    refactor---------------重構  回復  更多評論
      
    # re: 用spring組織DAO
    2007-08-24 16:39 | 千山鳥飛絕
    @chelson
    這本書叫做hibernate quickly,現在有中文版賣,售價也不高。

    適合hibernate入門時使用。  回復  更多評論
      
    正在閱讀:



    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    常用鏈接

    留言簿(35)

    隨筆檔案

    文章分類

    文章檔案

    好友的blog

    我的其他blog

    老婆的Blog

    搜索

    •  

    積分與排名

    • 積分 - 775111
    • 排名 - 56

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 日韩吃奶摸下AA片免费观看| 久久夜色精品国产亚洲| 98精品全国免费观看视频| 美国毛片亚洲社区在线观看| 亚洲美免无码中文字幕在线| 中文字幕日韩亚洲| 国产免费av一区二区三区| 日韩版码免费福利视频| 污污网站18禁在线永久免费观看| 又粗又长又爽又长黄免费视频 | 亚洲人配人种jizz| 亚洲国产成人久久精品影视| 久久久久国产亚洲AV麻豆| 日本不卡视频免费| 91在线视频免费91| 香蕉97超级碰碰碰免费公| 99re在线免费视频| 免费视频成人片在线观看| 99久久成人国产精品免费| 一级做性色a爰片久久毛片免费| 国产成人亚洲综合a∨| 亚洲精品美女久久久久久久| 亚洲sss综合天堂久久久| 亚洲国产成人va在线观看网址| 亚洲高清不卡视频| 亚洲精品成人久久| 亚洲成人免费在线观看| 亚洲欧洲精品久久| 亚洲不卡1卡2卡三卡2021麻豆| 久久精品国产亚洲av麻豆小说| 久久精品国产亚洲av四虎| 亚洲AV日韩AV天堂久久| 亚洲v高清理论电影| 色噜噜综合亚洲av中文无码| 亚洲精品在线观看视频| 中文字幕亚洲精品资源网| 亚洲精品影院久久久久久| 亚洲一级免费毛片| 亚洲精品自偷自拍无码| 国产亚洲女在线线精品| 一级女性全黄久久生活片免费|