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

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

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

    空間站

    北極心空

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks
    ??????????????????????????????????????????? 事務管理最佳實踐全面解析
    ?
    前言
    寫作這篇文章的起因,是前一段時間,我使用Jbpm工作流引擎開發工作流管理系統的過程中,使用編程方式管理事務時遇到的問題。
    由于之前很長一段時間,我一直都在使用Spring和EJB容器的聲明式事務管理,因此,咋一遇到Jbpm這樣的編程方式管理事務的情況,一下子搞不定了!經過幾天的研究,我重新思考了怎樣進行事務管理這個問題,并且發明了一種非常好的編程范式,或者說是事務管理的最佳實踐。不敢獨享,拿出來與諸君共賞。請大家批評指正。
    前幾個月,我對C++和Java編程方式進行了比較和研究。并且總結了一些C++編程中管理對象的最佳實踐。
    但由于那一段時間工作較忙,沒有及時把文章寫出來。后來想寫文章時,卻找不到當初為了說明寫的Java和C++演示代碼了。因此,該文一直未成,頗為遺憾!因此,這篇事務管理的文章,我不敢拖得太久。由于時間倉促,寫得不好,請大家見諒!
    ?
    事務管理
    企業級應用,或者叫“信息管理系統”。這類軟件通過數據庫持久化保存、處理的信息。它們工作的核心,就是數據庫。這類應用,是目前市場上最主流的商業應用。
    事務管理,這個概念出自于數據庫管理系統中。事務是一個單元的工作,要么全做,要么全不做。
    事務管理對于維持數據庫系統內部保存的數據邏輯上的一致性、完整性,起著至關重要的作用。如:一個銀行應用軟件中,轉帳的操作中,需要先在A用戶帳戶中減去資金,然后再在B用戶帳戶中增加相應的資金。如果完成A帳戶操作后,由于系統故障或者網絡故障,沒有能夠完成接下來的操作,那么A帳戶中的資金就白白流失了。顯然,客戶是無法接受這樣的結果的!
    如果我們把一個A和B帳戶的操作放在一個事務單元中,那么如果遇到上述異常情況,A帳戶減少資金的操作會回滾。A帳戶的資金不會減少。
    ?
    事務管理和數據庫連接的關系
    事務管理的工作,需要在數據庫連接上進行。如果沒有數據庫連接,事務管理是無法實施的。
    因此,一個事務單元,應該小于或者等于一個數據庫連接的生命周期。
    ?
    事務管理最佳模式
    數據庫連接管理最佳模式
    數據庫連接,是一種很寶貴也很昂貴的資源。一個數據庫可以提供的數據庫連接總數是有限的。而且,獲取一次數據庫連接也是非常昂貴的操作。需要建立網絡連接。因此,我們應當盡可能的重用數據庫連接,讓數據庫連接維持的時間盡可能的長。
    但是,我們也不能把數據庫連接維持的太久。因為,上文已經說過了,一個數據庫可以提供的數據庫連接總數是有限的。如果數據庫連接的時間很長,那么其他需要數據庫連接的工作就無法得到所需的數據庫連接。
    因此,最佳的數據庫連接模式,是“每次請求,一次數據庫連接”這樣的使用模式。
    因為,多次請求之間的時間間隔是無法預料的,可能長達幾小時、甚至幾天。數據庫連接顯然不能白白的等待在那里。而應該返回給數據庫,或者數據庫連接緩沖池,讓其他程序和組件有機會使用數據庫連接。
    另外,如果一次數據庫連接,小于一次用戶請求,那么,數據庫連接的得到和關閉次數又太頻繁了。因為,得到一次數據庫連接是非常消耗資源的。一次用戶請求,是一個短時、瞬間的操作,完全沒有必要使用多個數據庫連接。
    另外,上文中說過,事務是依托在數據庫連接之上的。多個數據庫連接之間,是無法使用同一個事務的。(實際上,JTA分布式事務是可以在一個事務中使用多個數據庫連接的)
    因此,我們更應該讓數據庫連接的生命周期盡可能的延長。
    ?
    事務管理最佳模式
    最佳的數據庫連接模式,是“每次請求,一次數據庫連接”這樣的使用模式。事務,與之相仿。最佳的事務管理模式,也是“每次請求,一次數據庫連接,一次事務”。
    一次用戶請求,是用戶對軟件系統功能的一次獨立調用。用戶當然不希望他的一次操作,系統只執行一部分這種情況的發生。因此,對一次用戶請求的響應,使用一次事務,是非常和正確的。
    對于一次單純的查詢操作,不更改持久化數據庫中記錄,那么我們不需要使用事務。在數據庫操作發生錯誤時,拋出異常,讓用戶界面顯示出問題即可。而對于更改數據庫記錄的操作,并且涉及到多次數據庫操作的,則必須使用事務,以保證數據庫中記錄的完整性和真實性。
    ?
    數據庫連接和事務管理的反模式
    數據庫連接和事務管理,在應用中有一些反模式。我們應該避免這樣做,否則會死得很慘!
    一、數據庫連接和事務管理跨越一個客戶的多次請求
    這樣的數據庫連接和事務,其持續時間是無法估量的。這樣嚴重影響軟件和數據庫的性能。這是絕對不可取的。
    二、每個數據庫操作,一次數據庫連接和事務
    這是一種非常常見的反模式。在采用DAO設計模式進行O-R映射中,DAO接口的一個數據庫訪問方法,就執行一次數據庫連接的獲取和釋放,并且執行一次或者多次事務。
    如,下面的代碼:
    /*
    ????4,刪除單條消息
    ????
    */
    ???publicvoid?deleteMessage(String?id){
    ???????Connection?conn
    =DB.getConnection();
    ???????Statement?stmt?
    =null;
    ???????ResultSet?rst
    =null;
    ??????
    try?{
    ???????stmt?
    =?conn.createStatement();
    ???????
    //拼裝SQL
    ???????String?sql="delete?from?message?where?id='"+id+"'";
    ??????stmt.executeUpdate(sql);
    ??????}
    ??????
    catch?(SQLException?ex)?{
    ????????ex.printStackTrace();
    ????????thrownew?DataAccessException();
    ??????}
    finally{
    ??????DB.freeDbResource(conn,stmt,rst);
    ??????}
    ?
    ???}


    這是典型的反模式。
    數據庫連接在Dao中得到和釋放。如果一次用戶請求需要用到多個Dao方法,那么就需要多次得到和釋放數據庫連接。造成了極大的浪費。而且,也無法對多個Dao方法實施事務管理。
    另外,JDBC中,默認的事務管理方式是自動提交。上面的代碼只有一個SQL執行語句。所有只有一次事務。如果Dao方法中有多個SQL語句,那么就會在一個Dao方法中使用多個事務,多次提交到數據庫中,這也是極端錯誤的!
    當然,上面這個簡單的Dao方法,并不會造成任何實際的損害,這里僅僅說明這種使用方式是一種反模式。
    ?
    ?
    事務管理的最佳設計模式
    最佳的事務管理模式,是“每次請求,一次數據庫連接,一次事務”。那么,根據這個原則,具體我們應該怎樣編寫程序呢?
    一、事務管理的分層
    企業級應用軟件中的代碼部分,可以分為以下幾個層次:
    ??? (一)控制器Controller層
    這是表現層的業務委派。它處理用戶的請求,完成用戶要求的功能。它接收用戶傳來的參數,然后調用業務層的服務方法,完成所需的功能。
    根據“每次請求,一次數據庫連接,一次事務”的原則。似乎,這里是最好的得到和關閉數據庫連接,管理事務的地方。因為,Controller層中的每一個方法,對應著用戶的一次請求。
    但是,我認為,這里決不應該“得到和關閉數據庫連接,管理事務”。因為,首先,控制器層,作為表現層技術的一部分,它的作用,僅僅是委派操作給業務層的服務方法,應該盡可能的小。不應該包括這些代碼。
    其次,管理數據庫連接和事務,這是業務層的邏輯,應該在業務層處理,而不是在表現層處理。
    更實際一點來說,Struts這種技術中,我們一般不使用Spring來管理Struts的控制器Action類。這樣,如果“得到和關閉數據庫連接,管理事務”放在Struts的控制器層—Action類中,那么Spring自動管理數據庫連接和事務的聲明式事務管理機制就無法使用了!(當然,Struts的Action也可以配置成Spring管理。)
    因此,我們應該堅決地拒絕在控制器層中處理數據庫連接和事務的誘惑!
    (二)業務服務Service層
    業務服務層,是業務邏輯的實際存放地。它們提供的服務分為2種:
    1,為控制器層提供服務,處理用戶請求。
    2,為其他類(不僅僅是控制器層,可能是其他Service方法等)提供服務。
    傳統上,大家都不區分這兩類服務方法。統稱為Service。
    而在我的方法中,我把它們區分開來。我把Service層的服務方法分為3類:
    1,直接為控制器層提供服務,并且需要使用到數據庫操作,從而需要處理數據庫連接和事務的,我把它們成為Transaction方法。用*Transaction后綴標識。
    這樣的方法,我仍然把它們放在Service接口中。當你需要實現這樣的方法時,看到后綴,你就知道,你需要在這里調用Dao方法,并且“得到和關閉數據庫連接,管理事務”。
    如果你不在這里進行“得到和關閉數據庫連接,管理事務”的操作,那么系統一定會出現數據庫訪問故障!
    2,為其他類(可以是控制器層,也可能是其他Service方法等)提供服務,并且不需要訪問數據庫的方法。我稱它們為Service方法。使用*Service后綴,或者不使用后綴來標識它們。
    這樣的方法,你可以無所顧忌的使用,既可以在控制器層中調用,也可以在任何代碼中調用!
    3,需要使用到數據庫操作,并且不可以直接被控制器層調用的方法。我稱它們為Dao方法。使用*Dao后綴來標識它們。
    它們不是Dao接口中的方法,而是Service業務邏輯接口中的方法。我稱它們為Dao方法,并不是說,它們是Dao接口的方法,而是表示它們是Service層中需要使用Dao接口操縱數據庫的服務方法。并且,它們本身不含有“得到和關閉數據庫連接,管理事務”的代碼。因此,所有需要調用它們的方法,需要注意了,“得到和關閉數據庫連接,管理事務”這些任務還沒有做。如果直接在控制器層調用它們,那么一定會出現數據庫和事務的錯誤!
    (三)DAO數據訪問層
    DAO數據訪問模式,是目前在數據訪問層中用得最多的模式。在DAO中,使用各類數據庫訪問技術(如,JDBC,iBatis,Hibernate等)操作數據庫,實現O-R映射。
    其中的方法,大都滿足“需要使用到數據庫操作,并且不可以直接被控制器層調用的方法”這樣一種情況。我們可以使用*Dao后綴來標識這些方法,也可以不使用后綴。因為Dao接口的方法,大抵都是這類方法。
    ?
    二、數據庫連接和事務管理最佳模式
    在我們的編程范式中,是這樣工作的:
    控制器層,接收用戶請求參數,并委派給業務層的Service接口執行業務邏輯。它可以直接調用Service接口的*Transaction方法和*Service方法或者沒有后綴的一般方法。
    其中,*Transaction方法需要用到數據庫。其中必然調用了業務層的Dao方法,或者DAO層的數據庫訪問方法。其實現方法中必然有處理“得到和關閉數據庫連接,管理事務”的代碼。
    而*Service方法或者沒有后綴的一般方法,則沒有使用數據庫。
    在DAO數據訪問層,執行數據庫操作的DAO方法,并不需要創建和關閉數據庫連接,也不需要處理事務。它們之需要得到數據庫連接,然后使用這個連接即可。(數據庫連接,可以通過參數從外部得到,也可以從本地線程變量中得到。后者是目前主流的技術)
    這就是我提出的“事務管理最佳實踐”的工作情況。
    在Service業務層和DAO數據訪問層中,我們都使用了“接口—實現類”相分離的設計模式。
    一、編程方式的數據庫連接和事務管理
    假設,現在我們使用多種數據庫訪問技術,來進行O-R映射。看看我們這個架構的適應能力。
    我們的系統,分別使用JDBC,iBatis,Hibernate這三種數據庫訪問技術,使用編程方式手工管理數據庫連接和事務,不使用Spring這樣的IOC容器進行管理??纯次覀冃枰鍪裁矗?/div>
    (一)JDBC編程方式管理數據庫連接和事務
    首先,開發一個JDBCUtil類,得到數據庫連接,并且把它們放在一個線程變量中,以便一個線程重用一個數據庫連接。
    然后,開發DAO接口的實現類。實現DAO方法。從本地線程變量中得到數據庫連接,使用它。不需要關閉這個連接,也不需要管理事務。
    接著,開發Serivce層的*Dao后綴命名的方法。它們只需要調用DAO接口的方法即可。不需要和數據庫連接、事務打交道。
    最后,開發Service層的*Transaction后綴命名的方法。它們調用JDBCUtil類的方法,創建一個數據庫連接,并把它放在JDBCUtil類的本地線程變量中,設置conn.setAutoCommit(false);等待DAO接口的方法去取這個已經設為不自動提交的數據庫連接。
    然后,在Try塊中,調用Dao方法(Service接口或者DAO接口的Dao方法)。調用結束之后,提交事務,并在異常處理模塊中,設置回滾。最后,在finally塊中關閉數據庫連接,清除本地線程變量的值。
    (二)iBatis編程方式管理數據庫連接和事務
    iBatis本身就是使用本地線程變量來管理數據庫連接的。
    1,DAO接口的實現方法中,調用iBatis代碼,執行數據庫操作。
    2,Service層的Dao方法,不需要任何更改。
    3,Service層的Transaction方法,需要使用iBatis的事務管理代碼。

    private?SqlMapClient?sqlMap?=?XmlSqlMapBuilder.buildSqlMap(reader);?
    public?updateItemDescriptionTransaction?(String?itemId,?String?newDescription)?throws?SQLException?{?
    try?{?
    sqlMap.startTransaction?();?
    dao方法調用;
    sqlMap.commitTransaction?();?
    }?
    finally?{?
    sqlMap.endTransaction?();?
    }?
    }?

    iBatis處理事務的代碼,也處理得數據庫連接。并且,事務的回滾也被iBatis搞定了。
    也就是說,換了一種數據庫訪問技術,只需要改變Service層中*Transaction方法的實現和DAO層的實現。
    (三)Hibernate編程方式管理數據庫連接和事務
    Hibernate也是如此。
    下面是Hibernate的助手類:
    publicclass?HibernateSessionFactoryFromJbpm?{
    ????
    ????privatestaticfinal?ThreadLocal?threadLocal?
    =?new?ThreadLocal();
    ????privatestatic?org.hibernate.SessionFactory?sessionFactory;
    ????
    /**
    ?????*ReturnstheThreadLocalSessioninstance.?Lazyinitialize
    ?????*the<code>SessionFactory</code>ifneeded.
    ?????*
    ?????*?@returnSession
    ?????*?@throwsHibernateException
    ?????
    */
    ????publicstatic?Session?getSession()?
    throws?HibernateException?{
    ????????Session?session?
    =?(Session)?threadLocal.get();
    ?
    ???????
    if?(session?==?null?||?!session.isOpen())?{
    ???????????
    if?(sessionFactory?==?null)?{
    ??????????????rebuildSessionFactory();
    ???????????}
    ???????????session?
    =?(sessionFactory?!=?null)???sessionFactory.openSession()
    ??????????????????:?
    null;
    ???????????threadLocal.set(session);
    ???????}
    ?
    ????????
    return?session;
    ????}
    ????
    /**
    ?????*?Rebuildhibernatesessionfactory
    ?????*
    ?????
    */
    ????publicstaticvoid?rebuildSessionFactory()?{
    ???????
    try?{
    ???????
    //?configuration.configure(configFile);
    ???????????
    //sessionFactory?=?configuration.buildSessionFactory();
    ???????????sessionFactory?=HibernateHelper.createSessionFactory();
    ???????}?
    catch?(Exception?e)?{
    ???????????System.err
    ??????????????????.println(
    "%%%%?Error?Creating?SessionFactory?%%%%");
    ???????????e.printStackTrace();
    ???????}
    ????}
    ????
    /**
    ?????*?Closethesinglehibernatesessioninstance.
    ?????*
    ?????*?@throwsHibernateException
    ?????
    */
    ????publicstaticvoid?closeSession()?
    throws?HibernateException?{
    ????????Session?session?
    =?(Session)?threadLocal.get();
    ????????threadLocal.set(
    null);
    ?
    ????????
    if?(session?!=?null)?{
    ????????????session.close();
    ????????}
    ????}
    }
    Hibernate的Session,是對JDBC?Connection的封裝。Hibernate不同于JDBC和iBatis。它默認就把自動提交設為false。也就是說,如果你不顯式的使用Hiberante事務,那么根本不會操作數據庫!這點需要注意。
    (四)Jbpm對Hiberante的封裝
    另外,再說一下Jbpm對Hiberante所作的封裝。Jbpm使用的是Hiberante3的數據庫訪問技術。但是,它對Hibernate進行了封裝。
    使用Jbpm,事務管理更加簡單。
    如:
    public?List?getAllCanSeenTaskInstancesTransaction?(PageModule?view,String?userId)?throws?Exception?{
    ???????JbpmContext?jbpmContext?
    =?JbpmConfiguration.getInstance().createJbpmContext();
    ????????
    try?{
    ???????returnthis.getAllCanSeenTaskInstances(view,?userId);
    ????????}
    finally{
    ????????jbpmContext.close();
    ????????}
    ????}

    jbpmContext.close();方法執行時,自動提交事務。如果發生異常,自動回滾。并且,最后會關閉Hiberante本地線程中的Session,并清空該線程變量。
    ?
    二、聲明方式的數據庫連接和事務管理
    Spring容器管理業務代碼和DAO數據訪問代碼,是現在非常常用的一種方式。使用Spring時,我們一般使用Spring聲明式事務來管理數據庫連接和事務。
    另外,還有EJB容器也有聲明式事務管理的機制,兩者的使用方法大體相同,我就不再論述,這里只說Spring。
    Spring管理下的JDBC,iBatis,Hibernate數據庫訪問方法。我們在DAO接口的實現類中,可以使用Spring提供的助手類的便利方法,進行數據庫操作。也可以使用Spring提供的助手類,得到Connection,Session等進行數據庫操作?;蛘呤褂肧pring助手類的execute()方法調用數據庫操作代碼。
    如果你原先使用自己的助手類得到Connection,Session。那么你完全可以修改該助手類的實現方法,改為從Spring得到Connection,Session。這樣就不需要修改DAO接口的實現類!
    Service層中的Dao方法,仍然無需修改。
    對于Service層中的Transaction方法。我們需要去除“得到和關閉數據庫連接,管理事務”的代碼。然后,在Spring的配置文件中,對其應用聲明式事務管理。運行時,Spring會通過SpringAOP技術,自動得到數據庫連接,管理事務。
    可見,使用聲明式事務管理,我們只需要修改得到數據庫連接或者會話的Util助手類,以及Transaction方法即可。
    綜上所述,可以看到,我提出的這一套事務管理最佳實踐是一套非常靈活、強大、簡潔的管理事務的最佳實踐。具有極其強大的適應能力。采用這套編程范式,你可以很容易地徹底擺脫事務管理帶來的困擾!
    使用它,即使是編程方式管理事務,也是非常簡單而可愛的。
    ?
    ?
    ?
    ?
    ?
    ?

    Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1415958

    posted on 2006-12-02 17:07 蘆葦 閱讀(276) 評論(0)  編輯  收藏 所屬分類: Spring
    主站蜘蛛池模板: 精品在线免费视频| 亚洲人成在线免费观看| 水蜜桃视频在线观看免费| 日本最新免费不卡二区在线| 小说专区亚洲春色校园| 一区国严二区亚洲三区| 一个人免费观看www视频| 亚洲精品成人无限看| 一区二区三区四区免费视频 | 四虎1515hh永久久免费| 亚洲免费一级视频| 在线播放免费人成视频在线观看| 含羞草国产亚洲精品岁国产精品| 国产免费牲交视频| 久青草视频在线观看免费| 亚洲欧洲在线观看| 国产va精品免费观看| 美女视频黄频a免费观看| 亚洲区小说区激情区图片区| 性xxxx视频免费播放直播| 亚洲一卡二卡三卡| 亚洲av午夜精品一区二区三区| 中文字幕不卡高清免费| 久久精品国产亚洲AV电影| 亚洲人成网站免费播放| 人成午夜免费大片在线观看| 亚洲AV无码1区2区久久| 毛片免费在线观看网址| 一级毛片在线播放免费| 亚洲成a人片在线观看中文!!!| 国产美女精品视频免费观看| 国产又黄又爽又大的免费视频 | 国产在亚洲线视频观看| 亚洲va久久久噜噜噜久久狠狠| 国产在线观看片a免费观看| 男人免费视频一区二区在线观看| 香蕉蕉亚亚洲aav综合| 日韩高清在线免费观看| 成人精品一区二区三区不卡免费看| 亚洲国产午夜电影在线入口| 红杏亚洲影院一区二区三区|