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

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

          主要看看WorkitemManager,因?yàn)橹饕壿嫾性谶@里

          public class WorkitemManager {

                  
          private WorkItemDAO workItemDAO;

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


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

          public class Workitem{

                  
          private int state = WorkitemInfo.PREPARE;

                  
          /**
               * 委派工作項(xiàng)
               
          */
              
          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()));
              }

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


          接下來(lái)的做法:
          三個(gè)類不變,將WorkitemManager打平,將邏輯移動(dòng)到Workitem

          public class WorkitemManager {

                  
          private WorkItemDAO workItemDAO;

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

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

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

          public class Workitem{

                  
          /**
               * 提交工作項(xiàng)
               
          */
              
          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");
                  
          //查找是否存在下一工作項(xiàng)
                  WorkItem sequenceWorkitem = workItemDAO.findSequenceWorkItem(instActivity.getId(), sID + 1);
                  
          //如果不存在則觸發(fā)節(jié)點(diǎn)流轉(zhuǎn)
                  if (sequenceWorkitem == null) {
                      instActivity.signal();
                  }
                  
          //否則把下一工作項(xiàng)激活
                  else {
                      sequenceWorkitem.setExecutive();
                  }
              }

          }


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

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

          public class Workitem{

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


          將業(yè)務(wù)邏輯拆分成兩部分,一部分在Workitem,另一部分委托給WorkitemExecutor。實(shí)際上是Domain Model將復(fù)雜邏輯的情況重新外包出去。調(diào)用的時(shí)候,面向的接口還是Domain Model的方法。注意到WorkitemExecutor和WorkitemManager的API是非常相似的。實(shí)際可以這樣認(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端的查詢的時(shí)候還是傾向于直接調(diào)用DAO,而不是通過(guò)Domain Model。

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

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


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

          回退(Rollback WorkItem)

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

          為什么要回退?

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

          回退模式

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

          串行

             

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

          分支

             

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

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

          并發(fā)

             

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


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

          多實(shí)例匯聚

             

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

          子流程

             

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

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

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

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

          回退的條件判斷

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

          任意人回退即回退,剩余工作項(xiàng)手工終止

          最后提交人回退才回退

             流程定義期定義該策略。

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

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

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

           

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

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

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

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

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

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

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

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

          最后說(shuō)說(shuō)翻譯,翻譯確實(shí)不是一件輕松的事情,這點(diǎn)我有很深的體會(huì)。去年翻譯《Enterprise AJAX》時(shí),每天的進(jìn)度只有兩三頁(yè),有時(shí)一個(gè)句子要反復(fù)揣摩好長(zhǎng)時(shí)間,真是痛苦的一個(gè)過(guò)程(但是卻是收獲良多)。

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

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

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

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

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

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

          補(bǔ)充一下我的一個(gè)最簡(jiǎn)單情況下的測(cè)試用例:
          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>

          被測(cè)試方法(都配置有事務(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);
              }

          測(cè)試用例:
               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);
              }

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

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

          補(bǔ)充:我對(duì)數(shù)據(jù)庫(kù)的一些實(shí)現(xiàn)機(jī)制也并不是很了解,所以這里也只能列出現(xiàn)象而不能解釋死鎖的根本原因。另外感謝Alex的討論。
          posted @ 2008-06-19 13:34 ronghao 閱讀(6279) | 評(píng)論 (22)編輯 收藏
          今天用hsqldb做單元測(cè)試時(shí)碰到這么個(gè)異常
          failed batch; nested exception is java.sql.BatchUpdateException: failed batch
          經(jīng)過(guò)檢查發(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 閱讀(684) | 評(píng)論 (1)編輯 收藏
          僅列出標(biāo)題
          共39頁(yè): First 上一頁(yè) 14 15 16 17 18 19 20 21 22 下一頁(yè) Last 
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

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

          常用鏈接

          留言簿(38)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          常去的網(wǎng)站

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 云林县| 通海县| 酒泉市| 福海县| 夹江县| 廊坊市| 武宣县| 萍乡市| 张掖市| 新营市| 应用必备| 灵寿县| 西乌珠穆沁旗| 望谟县| 黔西| 犍为县| 五莲县| 南乐县| 利辛县| 满洲里市| 平阳县| 志丹县| 潜山县| 龙胜| 宁都县| 弥渡县| 苍南县| 大渡口区| 晋州市| 定南县| 习水县| 大兴区| 汝州市| 独山县| 抚宁县| 房产| 仙居县| 来安县| 富锦市| 浮梁县| 调兵山市|