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

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

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

    posts - 193,  comments - 520,  trackbacks - 0
     
    關(guān)于Domain Model的討論已經(jīng)非常多了,炒炒冷飯,這里是自己的一些做法。
    以Workitem(工作流里的工作項)作為例子

    最開始的做法:
    一個實體類叫做Workitem,指的是一個工作項或者稱為任務(wù)項
    一個DAO類叫做WorkitemDao
    一個業(yè)務(wù)邏輯類叫做WorkitemManager(或者叫做WorkitemService)

    主要看看WorkitemManager,因為主要邏輯集中在這里

    public class WorkitemManager {

            
    private WorkItemDAO workItemDAO;

        
    public void setWorkItemDAO(WorkItemDAO workItemDAO) {
            
    this.workItemDAO = workItemDAO;
        }
        
        
    /**
         * 提交工作項
         * 
    @param workitemId 工作項ID
         
    */
        
    public void commitWorkitem(String workitemId){
                WorkItem workitem 
    = workItemDAO.getWorkItem(workitemId);
                
    //當(dāng)前工作項結(jié)束
            workitem.complete();
            
    int sID = workitem.getSequenceId();
            
    //找到所對應(yīng)的節(jié)點
            InstActivity instActivity=workitem.getInstActivity();
            
    //查找是否存在下一工作項
            WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
            
    //如果不存在則觸發(fā)節(jié)點流轉(zhuǎn)
            if (sequenceWorkitem == null) {
                instActivity.signal();
            }
            
    //否則把下一工作項激活
            else {
                sequenceWorkitem.setExecutive();
            }
        }
        
    }


    Workitem類里有一些狀態(tài)轉(zhuǎn)換的邏輯,這樣避免直接調(diào)用get/set屬性方法

    public class Workitem{

            
    private int state = WorkitemInfo.PREPARE;

            
    /**
         * 委派工作項
         
    */
        
    public void commission() {
            
    if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED
                    
    && state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND)
                
    throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
            setState(WorkitemInfo.COMMISSIONED);
            setCommitted(
    new Timestamp(System.currentTimeMillis()));
        }

        
    /**
         * 完成工作項
         
    */
        
    public void complete() {
            
    if (state != WorkitemInfo.SIGNINED)
                
    throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
            setState(WorkitemInfo.COMPLETE);
            setCompleted(
    new Timestamp(System.currentTimeMillis()));
        }
    }


    接下來的做法:
    三個類不變,將WorkitemManager打平,將邏輯移動到Workitem

    public class WorkitemManager {

            
    private WorkItemDAO workItemDAO;

        
    public void setWorkItemDAO(WorkItemDAO workItemDAO) {
            
    this.workItemDAO = workItemDAO;
        }
        
        
    /**
         * 提交工作項
         * 
    @param workitemId 工作項ID
         
    */
        
    public void commitWorkitem(String workitemId){
                WorkItem workitem 
    = workItemDAO.getWorkItem(workitemId);
                
    //當(dāng)前工作項提交
            workitem.commit();
        }
        
    }

    實際上此時WorkitemManager的功能非常有限,僅僅是事務(wù)邊界和獲取workitem對象,甚至在一些情況下可以省略。

    通過一個Container類將spring的applicationContext進行封裝,然后通過getBean()的靜態(tài)方法即可訪問被spring所管理的bean。實際是將workItemDAO隱式注入了Workitem。

    public class Workitem{

            
    /**
         * 提交工作項
         
    */
        
    public void commit() {
            
    if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED
                    
    && state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND)
                
    throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
            setState(WorkitemInfo.COMMISSIONED);
            setCommitted(
    new Timestamp(System.currentTimeMillis()));
            
    int sID = workitem.getSequenceId();
            WorkItemDAO workItemDAO
    =(WorkItemDAO)Container.getBean("workItemDAO");
            
    //查找是否存在下一工作項
            WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
            
    //如果不存在則觸發(fā)節(jié)點流轉(zhuǎn)
            if (sequenceWorkitem == null) {
                instActivity.signal();
            }
            
    //否則把下一工作項激活
            else {
                sequenceWorkitem.setExecutive();
            }
        }

    }


    這樣帶來的好處是業(yè)務(wù)邏輯全部被封裝到Domain Model,Domain Model之間的交互變得非常的簡單,沒有頻繁的set/get,直接調(diào)用有業(yè)務(wù)語義的Domain Model的方法即可。問題在于單元測試時脫離不了spring的容器,workItemDAO需要stub。我覺得這個問題不大,問題是Domain Model開始變得臃腫,在業(yè)務(wù)邏輯復(fù)雜時代碼行急劇膨脹。

    現(xiàn)在的做法
    以上三個類保持不變,增加一個類WorkitemExecutor,將業(yè)務(wù)邏輯移步。

    public class Workitem{

            
    /**
         * 提交工作項
         
    */
        
    public void commit() {
            
    if (state != WorkitemInfo.EXECUTE && state != WorkitemInfo.SIGNINED
                    
    && state != WorkitemInfo.TOREAD&& state != WorkitemInfo.SUSPEND)
                
    throw new WorkflowException(Messages.CANNOT_ALTER_WORKITEM_STATE);
            setState(WorkitemInfo.COMMISSIONED);
            setCommitted(
    new Timestamp(System.currentTimeMillis()));
            WorkitemExecutor workitemExecutor
    =(WorkitemExecutor)Container.getBean("workitemExecutor");
            workitemExecutor.commitWorkitem(
    this);
        }

    }

    public class WorkitemExecutor {

            
    private WorkItemDAO workItemDAO;

        
    public void setWorkItemDAO(WorkItemDAO workItemDAO) {
            
    this.workItemDAO = workItemDAO;
        }
        
        
    /**
         * 提交工作項
         * 
    @param workitemId 工作項ID
         
    */
        
    public void commitWorkitem(Workitem workitem){
            
    int sID = workitem.getSequenceId();
            
    //找到所對應(yīng)的節(jié)點
            InstActivity instActivity=workitem.getInstActivity();
            
    //查找是否存在下一工作項
            WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
            
    //如果不存在則觸發(fā)節(jié)點流轉(zhuǎn)
            if (sequenceWorkitem == null) {
                instActivity.signal();
            }
            
    //否則把下一工作項激活
            else {
                sequenceWorkitem.setExecutive();
            }
        }
        
    }


    將業(yè)務(wù)邏輯拆分成兩部分,一部分在Workitem,另一部分委托給WorkitemExecutor。實際上是Domain Model將復(fù)雜邏輯的情況重新外包出去。調(diào)用的時候,面向的接口還是Domain Model的方法。注意到WorkitemExecutor和WorkitemManager的API是非常相似的。實際可以這樣認(rèn)為,傳統(tǒng)的方式
    Client->(Business Facade)->service(Business Logic 部分依賴Domain Model)->Data Access(DAO)。
    現(xiàn)在的方式
    Client->(Business Facade)->Domain Model->service->Data Access(DAO)。

    另外,在返回client端的查詢的時候還是傾向于直接調(diào)用DAO,而不是通過Domain Model。

    改進:
    注意到代碼中有這么一行
    WorkItemDAO workItemDAO=(WorkItemDAO)Container.getBean("workItemDAO");

    確實是一個bad smell.當(dāng)代碼中大量出現(xiàn)后,這種造型是很恐怖的。所以采取了一種處理方式:給所有Domain Model繼承一個父類,在父類里集中管理所有Domain Model所依賴的services,在父類里進行造型。


    posted @ 2008-07-03 18:23 ronghao 閱讀(2620) | 評論 (2)編輯 收藏

    回退(Rollback WorkItem)

    回退是工作流參與者對自己“待辦任務(wù)”(實際是對工作項)的一種操作,即參與者主動回退待辦任務(wù)列表中的任務(wù)到已經(jīng)執(zhí)行過的人工節(jié)點。

    為什么要回退?

    參與者接受任務(wù)后,發(fā)現(xiàn)不應(yīng)由自己辦理此任務(wù)或以前的執(zhí)行者辦理有錯誤等情況后,需要將此接受的任務(wù)回退給以前某個節(jié)點的執(zhí)行者重新辦理。

    回退模式

    回退的情況實際上是非常復(fù)雜的,其中包括了參與者的重新選擇以及回退的條件判斷等等。這里先列出常見的回退模式(其實也是我們支持的模式)。

    串行

       

    這種情況最為簡單,后續(xù)節(jié)點可以回退到前續(xù)任意人工節(jié)點。回退后,節(jié)點重走。

    分支

       

    這種情況也相對簡單,實際執(zhí)行的分支上的節(jié)點可以回退到前續(xù)任意人工節(jié)點(不區(qū)分主支和分支)。同樣,主支上的節(jié)點也可以回退到任意實際執(zhí)行的分支上的節(jié)點。

    可能的問題:多次回退后的回退節(jié)點選擇。例如:第一次流程經(jīng)過節(jié)點2、節(jié)點3到達(dá)節(jié)點5,節(jié)點5可以回退到節(jié)點1、節(jié)點2和節(jié)點3的任意一個,此時節(jié)點5回退到節(jié)點1,節(jié)點1重走,這一次流程改為經(jīng)過節(jié)點4到達(dá)節(jié)點5,節(jié)點5回退時如何選擇回退節(jié)點?此時的策略是以最近實際執(zhí)行的分支為準(zhǔn),即節(jié)點5只允許回退到節(jié)點4和節(jié)點1,不允許回退到節(jié)點2和節(jié)點3。(抹去記憶)

    并發(fā)

       

    對于并發(fā)的情況,分支節(jié)點只允許在分支的節(jié)點間回退。


    同理,主支節(jié)點也只允許在主支的節(jié)點間回退。

    多實例匯聚

       

    在這種情況下,節(jié)點5會產(chǎn)生2個實例,實際相當(dāng)于繼續(xù)并發(fā)。節(jié)點5根據(jù)具體哪個節(jié)點觸發(fā)的它而產(chǎn)生回退節(jié)點。同時不允許回退到節(jié)點1以及前續(xù)的節(jié)點去。

    子流程

       

    支持子流程到父流程的回退,也支持父流程到子流程節(jié)點的回退。需要注意的是子流程節(jié)點有可能產(chǎn)生多個子流程實例,在這種情況下不支持父子流程之間的相互回退。

    回退節(jié)點的參與者選擇

    默認(rèn)策略是由原先節(jié)點的實際參與者重新處理,比如節(jié)點2回退到節(jié)點1,則節(jié)點1的實際參與者重新處理該節(jié)點任務(wù)。這也符合大多數(shù)實際的業(yè)務(wù)場景。

    在節(jié)點任務(wù)競爭參與的情況下,提供另一種策略,即讓人員重新競爭。

    回退的條件判斷

    對于多人(或者多部門,用戶)參與的工作項,提供不同的回退策略

    任意人回退即回退,剩余工作項手工終止

    最后提交人回退才回退

       流程定義期定義該策略。

       另外流程定義時提供節(jié)點可回退列表,由用戶在定義期對可回退的節(jié)點進行限制。

    關(guān)于業(yè)務(wù)補償

    業(yè)務(wù)補償是一個很重要的概念,在回退的情況下需要相應(yīng)的回退部分業(yè)務(wù)操作。這里由引擎提供統(tǒng)一的接口,返回回退路徑,由客戶自定義代碼進行匹配處理。

     

    關(guān)于實現(xiàn)

    很多工作流引擎通過流程定義時繪出回退線來顯式的支持回退,這種實現(xiàn)在業(yè)務(wù)復(fù)雜的情況下會造成流程圖的異常煩瑣,但是比較清晰,實現(xiàn)比較容易。隱式實現(xiàn)相比而言優(yōu)點更多。

    posted @ 2008-06-24 09:12 ronghao 閱讀(2380) | 評論 (3)編輯 收藏
    收到這本書已經(jīng)好久,甚至讀完這本書都已經(jīng)好久,一直想著寫個書評,卻一直被這事那事拖著,直到今天。我只想說,這是一本好書。

    關(guān)于Hibernate似乎不必說太多。和朋友聊天,朋友說,你對Hibernate熟嗎?我說,還好,用了兩年了。朋友說,如果10分是滿 分,你給自己打幾分?我認(rèn)真想了想,6分吧。說實話還真沒有底氣,會用而已。在此之前,我就看過一本Hibernate的書籍,《深入淺出 Hibernate》,然后就是滿江紅翻譯的Hibernate中文手冊。我想,也許沒有必要再買一本Hibernate的書了,有問題查查 Hibernate中文手冊就好了。

    問題在于,我收到了這本書。

    花了兩個禮拜大概看過這本書。看的快是因為一些內(nèi)容本來就很熟悉,還因為把jpa這部分全部略過了。一口氣讀完的。原諒自己曾經(jīng)的淺薄吧,看 完這本書才發(fā)現(xiàn)自己對Hibernate的了解其實少的可憐,知道如何映射實體對象,知道對象的3種狀態(tài),知道save和 saveorupdate,get和load的區(qū)別,知道級聯(lián)和關(guān)系控制反轉(zhuǎn)。。。。知道這些就夠了嗎,實際差得還很遠(yuǎn)呢。這本書把以前許多零散的知識點 全部系統(tǒng)的串聯(lián)起來,很多時候,看得我暗自流汗,哎,原來是這樣的啊。

    實際這本書不僅僅局限于Hibernate,我更認(rèn)為是對java持久化的一個完整介紹和總結(jié)。我想,像without ejb一樣,是應(yīng)該人手一本的。

    關(guān)于翻譯,這本書被人詬病的厲害,原因是認(rèn)為翻譯的很差。我的感覺是,確實存在問題,但是也不至于差到網(wǎng)上說得那種地步。至少我讀過一遍,基 本上沒有碰到讀不過去的地方,相反,還是比較流暢的。但是不是說沒有問題,一些句子是根據(jù)上下文很快得出意思的。我想,對于剛使用Hibernate不久 的人來說,這種理解很可能就顯得比較困難,會顯得無所適從。另外,單純從英文版來說,這本書也不適合作為Hibernate的入門書。另外,這本書的定價 太高,我想,這也是讀者反應(yīng)很激烈的一個原因。很高的期望,很貴的價格,結(jié)果不是很滿意,自然謾罵的厲害。我想,出版社的讀者定位、市場策略包括定價都是 存在問題的。

    最后說說翻譯,翻譯確實不是一件輕松的事情,這點我有很深的體會。去年翻譯《Enterprise AJAX》時,每天的進度只有兩三頁,有時一個句子要反復(fù)揣摩好長時間,真是痛苦的一個過程(但是卻是收獲良多)。

    最后,一本好書。
    posted @ 2008-06-22 15:22 ronghao 閱讀(2030) | 評論 (3)編輯 收藏
    測試在sqlserver2000上進行,對工作流操作的相關(guān)方法在單元測試?yán)镞M行多線程并發(fā)。測試發(fā)現(xiàn)sqlserver出現(xiàn)死鎖的情況相當(dāng)多,一些典型的情況:

    1、對同一張表先insert再update是很快會引起死鎖的,不管操作的是否是同一記錄
    解決方法:對于同一記錄,需要調(diào)整hibernate的映射策略,使得一次insert完成操作。對于不同的記錄需要在代碼中手動flush,使得update先于insert。

    2、對兩張表進行多次update操作時,兩張表交替update也會很快引起死鎖
    解決方法:在代碼中手動flush,保證對兩張表的update不會出現(xiàn)交替的情況。

    3、部分大范圍掃描的select和update混合也會導(dǎo)致死鎖
    解決方法:優(yōu)化sql,盡量減少sql語句,通過給po增加持久化字段的方式減少關(guān)聯(lián)查詢

    經(jīng)過優(yōu)化,大部分情況下數(shù)據(jù)庫死鎖的情況得以避免,另外奇怪的是通過事件探查器在死鎖時并未發(fā)現(xiàn)鎖升級的事件。但是在一些特殊情況下(例如多個并發(fā)匯聚的直接聯(lián)合),死鎖依舊發(fā)生。最后不得不對方法進行synchronized關(guān)鍵字同步,這個通過synchronized flush完成。業(yè)務(wù)方法不必同步,最后批量操作數(shù)據(jù)庫時進行同步。

    換oracle進行測試,在未synchronized的情況下,未發(fā)生死鎖情況。由此可見sqlserver與oracle鎖實現(xiàn)機制存在很大的差別。對sqlserver鄙視之。另,同事說,sqlserver2005后性能和機制發(fā)生了很大的變化,未測試。

    補充一下我的一個最簡單情況下的測試用例:
    PO:
    public class TestPO {
        String id;
        String name;
        
    int num;
        
        .
    }

    映射文件 hibernate3:
    <hibernate-mapping default-access="field">
      
    <class table="WFMS_TESTPO" name="com.eway.workflow.test.po.TestPO">

        
    <id name="id" column="ID"><generator class="uuid" /></id>

        
    <property name="name" column="NAME" type="string"/>

        
    <property name="num" column="NUM" type="integer"/>

      
    </class>
    </hibernate-mapping>

    被測試方法(都配置有事務(wù)):
        public void testSave(int num) {
            TestPO po 
    = new TestPO();
            po.setName(
    "ronghao");
            po.setNum(num);
            theadTestDao.save(po);
            po.setName(
    "haorong");
        }

        
    public void testSaveByJdbc(int num) {
            String sql 
    = "insert into WFMS_TESTPO (ID,NAME,NUM) values (?,'RONGHAO',?)";
            Object[] params 
    = new Object[]{num,num};
            jdbcTemplate.update(sql, params);
            sql
    ="update WFMS_TESTPO set name='haorong' where id=?"  ;
            params 
    = new Object[]{num};
            jdbcTemplate.update(sql, params);
        }

    測試用例:
         public void testSave() throws Exception {
            TheadtestTemplate template 
    = new TheadtestTemplate();
            template.execute(
    new TheadtestCallback() {
                
    public void doInThead(int suquence) {
    //               theadTestManager.testSave(suquence);
                    theadTestManager.testSaveByJdbc(suquence);
                }
            }, 
    10);
        }

    測試結(jié)果:不論是hibernate還是jdbc,并發(fā)情況下都很快就會引起sqlserver2000的死鎖,換用兩種數(shù)據(jù)庫驅(qū)動jtds和jturbo死鎖的情況沒有變化。

    結(jié)論:sqlserver2000數(shù)據(jù)庫的lock配置策略,不支持,或者數(shù)據(jù)庫本身,就不支持對不同的行做同時操作(或者支持不完善),所謂的行鎖支持很不完善,死鎖情況非常容易發(fā)生。

    補充:我對數(shù)據(jù)庫的一些實現(xiàn)機制也并不是很了解,所以這里也只能列出現(xiàn)象而不能解釋死鎖的根本原因。另外感謝Alex的討論。
    posted @ 2008-06-19 13:34 ronghao 閱讀(6274) | 評論 (22)編輯 收藏
    今天用hsqldb做單元測試時碰到這么個異常
    failed batch; nested exception is java.sql.BatchUpdateException: failed batch
    經(jīng)過檢查發(fā)現(xiàn)是HSQLDB的問題
    The bug is in HSQLDB - a well known one (any Google search for HSQLDB and that "failed batch"
    message would have told you it).
    https://sourceforge.net/tracker/?func=detail&atid=378131&aid=1407528&group_id=23316
    解決方法 : turn off batching with HSQLDB it doesnt work.
    設(shè)置<prop key="hibernate.jdbc.batch_size">0</prop>即可
    posted @ 2008-05-30 18:40 ronghao 閱讀(681) | 評論 (1)編輯 收藏
    僅列出標(biāo)題
    共39頁: First 上一頁 14 15 16 17 18 19 20 21 22 下一頁 Last 
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    關(guān)注工作流和企業(yè)業(yè)務(wù)流程改進。現(xiàn)就職于ThoughtWorks。新浪微博:http://weibo.com/ronghao100

    常用鏈接

    留言簿(38)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    常去的網(wǎng)站

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 日韩国产免费一区二区三区| 亚洲成A∨人片天堂网无码| 亚洲伊人色欲综合网| 污视频网站在线观看免费| 国产精品免费_区二区三区观看| 亚洲卡一卡二卡乱码新区| 色se01短视频永久免费| 亚洲天堂2017无码中文| 成人毛片18女人毛片免费视频未| 国产亚洲sss在线播放| 99久久这里只精品国产免费| 色偷偷女男人的天堂亚洲网| 毛片免费全部免费观看| 美女露隐私全部免费直播| 亚洲国产综合精品一区在线播放| 人妻免费久久久久久久了| 在线看无码的免费网站| 亚洲日韩国产精品无码av| 蜜臀AV免费一区二区三区| 亚洲五月综合网色九月色| 岛国大片免费在线观看| 亚洲AV无码精品国产成人| 免费a在线观看播放| v片免费在线观看| 亚洲avav天堂av在线不卡| 亚洲成a人无码亚洲成www牛牛| 日日夜夜精品免费视频| 国产大陆亚洲精品国产| 国产亚洲色视频在线| 精品熟女少妇av免费久久| 亚洲一区二区三区深夜天堂| 永久免费观看的毛片的网站| 人妻巨大乳hd免费看| 亚洲视频在线观看| 国产精品免费久久久久久久久 | 在线毛片片免费观看| 国产片免费在线观看| 一区二区三区在线观看免费| 亚洲一区二区三区夜色| 国内自产拍自a免费毛片| 久久免费观看视频|