半山云嵐

             :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            10 隨筆 :: 0 文章 :: 1 評論 :: 0 Trackbacks

          #

          從09年1月份開始在項目中推行測試驅(qū)動開發(fā),在實踐中有一些所得:

          1. (TDD的基本原則)如果需要對代碼的邏輯行為做任何修改,必須思考并先創(chuàng)建單元測試用例。

          2. TDD的初衷是可以完全取代 Low Level Design。但是在實踐中發(fā)現(xiàn),對于大多數(shù)新的TDD程序員,要在完全沒有詳細(xì)設(shè)計之前寫出有效實用的單元測試用例是非常困難的事情。其實這是個思維習(xí)慣問題(我們絕大多數(shù)人都習(xí)慣了用文字來描述場景和問題,而不習(xí)慣用代碼來做同樣的事情,仿佛代碼沒有文字直觀),經(jīng)過有意識地訓(xùn)練,我發(fā)現(xiàn)用測試代碼來描述問題更具體,更節(jié)省時間。那么對于經(jīng)驗不足的程序員,一個折中的辦法是,可以允許他在寫測試代碼前有一個簡單的詳細(xì)設(shè)計文檔。但是注意:這個文檔不是一個很正式的可參考的設(shè)計文檔,不是我們的 work product ,它僅僅用于幫助經(jīng)驗不夠的程序員進(jìn)行思考分析。(在敏捷開發(fā)實踐中,我們提倡 Simple Design,設(shè)計不應(yīng)該描述太多的代碼細(xì)節(jié))

          3. 在設(shè)計文檔中(無論是 High Level Design 或 Detail Design),我們需要對預(yù)期需要的單元測試用例進(jìn)行描述,比如羅列出單元測試需要測試哪些場景。這有兩個好處:

          a. 強制設(shè)計人員在寫設(shè)計時就考試考慮如何進(jìn)行驗證;

          b. 確保程序員在 coding 時不會忘記某些必要的測試。

          4. 如果有 Code Review 的流程,單元測試用例和運行結(jié)果也必須是代碼審查活動的一部分。

          5. 單元測試用例必須要被頻繁的執(zhí)行或重構(gòu),團隊需要保證提交到版本控制庫里的所有代碼的測試用例在任何時候都可以成功通過(建議部署持續(xù)集成CI的流程來自動化單元測試執(zhí)行),不是僅僅在開發(fā)的時候?qū)懸淮尉妥圆痪S護(hù)了。而且一個程序員寫的測試用例要能夠很容易得被其它程序員執(zhí)行。

          6. 盡可能將測試數(shù)據(jù)和用到它的測試代碼放在同一個類中或同一個目錄下,盡可能將不同單元測試用例的測試數(shù)據(jù)隔離,避免互相影響(除了一些基礎(chǔ)數(shù)據(jù),業(yè)務(wù)測試數(shù)據(jù)可以在每個用例測試前創(chuàng)建,測試后清除);程序員需要保證,單元測試用例的執(zhí)行不能過于依賴本地數(shù)據(jù)和環(huán)境的配置。

          7. TDD的單元測試用例應(yīng)該從需求,從業(yè)務(wù)場景的角度來思考和創(chuàng)建,而不是像傳統(tǒng)單元測試那用針對每個類,每個方法來寫測試。記?。赫嬲腡DD是一個業(yè)務(wù)驅(qū)動的設(shè)計和開發(fā)方法!

          More to be added...

          posted @ 2010-01-06 17:45 Alfred. Yao 閱讀(212) | 評論 (0)編輯 收藏

          用了 Derby 數(shù)據(jù)庫好一段時間了,雁過留痕。

          作為內(nèi)存數(shù)據(jù)庫或嵌入式數(shù)據(jù)庫,Derby 是一個很好的選擇,完全由 Java 實現(xiàn),在 JDK1.6 中 Derby 已經(jīng)作為其一部分來發(fā)布了,稱之為 JavaDB。

          Derby 數(shù)據(jù)庫通過 derby.properties 文件來配置,這個文件需要放在程序的 classpath 或者 jdk 的 javadb classpath 路徑中。下面是一些配置的例子:

          ---------------------------- Derby derby.properties information ----------------------------
          --### Derby System Home Directory
          derby.system.home=D:/workspace/Boulevard/BoulevardDB


          --### Derby Authentication Configuration and Turn On
          derby.connection.requireAuthentication=true
          derby.authentication.provider=BUILTIN
          derby.database.propertiesOnly=true
          --### User/Password Definition (System Level)
          derby.user.alfred=alfred
          --### User Authorization Definition (System Level)
          derby.database.defaultAccessMode=fullAccess
          derby.fullAccessUsers=cube


          --### Some Others Configuration
          derby.infolog.append=true
          derby.storage.pageSize=4096
          derby.storage.pageReservedSpace=60
          derby.locks.deadlockTimeout=20
          derby.locks.waitTimeout=30
          ---------------------------- Derby derby.properties information ----------------------------


          Derby 數(shù)據(jù)庫可以啟動為兩種不同的模式:嵌入式模式服務(wù)器模式

          1. 嵌入式模式(embedded):當(dāng) Java 應(yīng)用程序第一次嘗試連接數(shù)據(jù)庫時啟動數(shù)據(jù)庫實例,當(dāng)程序進(jìn)程結(jié)束時數(shù)據(jù)庫實例也被關(guān)閉。當(dāng)數(shù)據(jù)庫實例啟動后,僅僅啟動它的進(jìn)程可以訪問,其它任何外部進(jìn)程(運行在當(dāng)前進(jìn)程的JVM之外的程序)都不能訪問。

              下面的命令用于啟動并連接一個嵌入式數(shù)據(jù)庫實例:
          ------------------------------- Derby connection information -------------------------------
          -- ### Use ij command to connect embeded database; if not exist, create it.
          CONNECT 'jdbc:derby:data;create=true;user=alfred;password=alfred' AS CUBE;
          -- ### Use ij command to connect embeded database; if not exist, don't create it, throw out error.
          CONNECT 'jdbc:derby:data;user=alfred;password=alfred' AS CUBE;
          ------------------------------- Derby connection information -------------------------------

          2. 服務(wù)器模式(network server):數(shù)據(jù)庫實例只能由命令行啟動:"...\javadb\bin\startNetworkServer.bat",該實例始終有效,直到通過命令行停止:"...\javadb\bin\stopNetworkServer.bat"。任何運行在本地或遠(yuǎn)程的JVM進(jìn)程都可以訪問,不會隨著訪問它的程序的結(jié)束而關(guān)閉。

              下面的命令用于連接(不能啟動)服務(wù)器數(shù)據(jù)庫實例:
          ------------------------------- Derby connection information -------------------------------
          -- ### Use ij command to connect network server database, reading default repository;
          -- ### If not exist, create it.
          CONNECT 'jdbc:derby://localhost:1527/data;create=true;user=alfred;password=alfred' AS BOULEVARD;
          -- ### Use ij command to connect network server database; reading default repository;
          -- ### If not exist, don't create it, throw out error.
          CONNECT 'jdbc:derby://localhost:1527/data;user=alfred;password=alfred' AS BOULEVARD;
          -- ### Use ij command to connect network server database, reading specific repository;
          -- ### If not exist, create it.
          CONNECT 'jdbc:derby://localhost:1527/D:/workspace/Boulevard/BoulevardDB/data;create=true;user=alfred;password=alfred' AS BOULEVARD;
          -- ### Use ij command to connect network server database, reading specific repository;
          -- ### If not exist, don't create it, throw out error.
          CONNECT 'jdbc:derby://localhost:1527/D:/workspace/Boulevard/BoulevardDB/data;user=alfred;password=alfred' AS BOULEVARD;
          ------------------------------- Derby connection information -------------------------------

          posted @ 2010-01-06 17:44 Alfred. Yao 閱讀(2025) | 評論 (0)編輯 收藏

          Hibernate提供了三個級別的緩存策略:Session緩存(基本的事務(wù)級緩存),Query Cache(查詢緩存),Seond-Level Cache(二級緩存)

          Session緩存(First-Level Cache):Session是Hibernate用于管理持久化對象的核心機制,它是針對持久性數(shù)據(jù)的事務(wù)級緩存。PersistenceContext中包括:

          entityKeyscollectionKeys

          insertion,updates,deletions

          collectionCreationscollectionRemovals,collectionUpdates

          由此可見,Session不會把所有的持久化對象實體本身緩存,而只是緩存實體或Collection的Identiy值,和狀態(tài)被更新過的實體或Collection(包括插入,更新,刪除)

          當(dāng)Session中緩存的內(nèi)容過多時會導(dǎo)致OutOfMemory的問題,可以通過兩種方式刪除緩存的內(nèi)容:

          • clear(): 清除所有Session緩存;
          • evict(PersistentObject): 將一個特定持久化對象從Session緩存中刪除。 

          Query Cache:Hibernate可以對頻繁進(jìn)行的查詢(相同查詢,相同參數(shù))進(jìn)行緩存以提高效率。但是查詢緩存不會緩存結(jié)果集中實際的數(shù)據(jù)實體,而是只緩存Identiy值和結(jié)果值類型,因此它應(yīng)該總是和二級緩存一起使用。但由于實際環(huán)境中完全相同的頻繁查詢很少,所以默認(rèn)該緩存是disabled的。可以通過兩種方式使之生效:(個人覺得這個緩存意義不大)

          配置屬性:<prop key="hibernate.cache.use_query_cache">true</prop>

          代碼:Query.setCacheable(true)

          Second-Level Cache:  二級緩存,用于對持久化對象實體或Collection的實際數(shù)據(jù)進(jìn)行緩存,用于提供一個集群級別(cluster level),JVM級別,或文件系統(tǒng)級的緩存機制。Hibernate的二級緩存通常都是由第三方的開源項目提供,可以通過配置選擇特定的緩存實現(xiàn),例如:EHCache,OSCache,JBoss Cable等。

          配置屬性:<prop key="hibernate.cache.provider_class">org.hibernate.cache.OSCacheProvider</prop>

          二級緩存同樣可以通過兩種方式來管理:

          配置屬性:<prop key="hibernate.cache.use_second_level_cache">false</prop>

          代碼:Query.setCacheMode(CacheMode.IGNORE) (或者GET,NORMAL,PUT,REFRESH)

          • IGNORE: 禁用二級緩存
          • NORMAL: 啟用二級緩存,正常讀寫
          • GET: 只從二級緩存讀,除非有數(shù)據(jù)update
          • PUT: 只向二級緩存寫
          • REFRESH: 強制對寫入二級緩存的內(nèi)容進(jìn)行刷新

          最后,在程序調(diào)試中,我們可能需要查看各種Cache中實際緩存的內(nèi)容,可以通過配置屬性讓Hibernate收集緩存統(tǒng)計信息。當(dāng)我們遭遇可能由于緩存導(dǎo)致的問題時,這一方法特別有用。

          配置屬性:<prop key="hibernate.generate_statistics">true</prop>

          在代碼中獲得統(tǒng)計信息:sessionFactory.getStatistics()

          posted @ 2010-01-06 17:21 Alfred. Yao 閱讀(1936) | 評論 (0)編輯 收藏

          在做自動定價流程的性能測試中,發(fā)現(xiàn)一個很棘手的性能問題:一個非常簡單的查詢跑起來非常非常慢,幾乎每一秒鐘只從數(shù)據(jù)庫返回一條記錄!數(shù)據(jù)庫是zLinux Server 上的 DB2 8.5,CPU Utilization 只有 35% 而已。

          花了大半天時間,最后我以為是數(shù)據(jù)庫的問題(因為是由別人管理的遠(yuǎn)程數(shù)據(jù)庫),SQL如此簡單應(yīng)該沒有問題;但是從DBA的反饋是他檢查了數(shù)據(jù)庫的所有狀態(tài),一切正常:( 我?guī)缀醣罎ⅰ?/p>

          但今天,最終還是承認(rèn)SQL確實有問題,而且犯大忌 (對查詢優(yōu)化的認(rèn)識有待提高啊…)

          -- less 1 record inserted per second, 30000 records insertion need 10 hours
          -- to complete (timeout...)
          INSERT INTO WWPRT.CABLEPRODUCT_PRICETYPE_JOIN_CN (CABLEPRODUCTID,
          PRICETYPE, SLAVE) 
          (
               SELECT cableproduct.ID, pricetypes.PRICETYPE, 'N'
               FROM WWPRT.CABLE_PRODUCT_JOIN_CN AS cableproduct,
               (
                    SELECT DISTINCT usprice.PRICETYPE FROM WWPRT.PRICE_CN usprice
                    JOIN WWPRT.CONF_PRICETYPE pt ON pt.ID = usprice.PRICETYPE
                    WHERE usprice.CABLEID = 'USCABF3FVT01'
                    AND pt.DOMAIN IN ( SELECT DISTINCT DOMAIN FROM WWPRT.CONF_GAP )
               ) AS pricetypes
               WHERE CABLEID = 'GEOANNF3FVT1'
               AND EXISTS (
                   ......
               )
          )


          其罪魁禍?zhǔn)拙褪恰癴rom”分句中的 subselect 語句,數(shù)據(jù)庫會對 WWPRT.CABLE_PRODUCT_JOIN_CN 表中的每一條記錄都執(zhí)行一次上面的 subselect 語句,太可怕了!修改了 SQL 如下,讓上面的 subselect 部分只執(zhí)行一次作為臨時表:


          -- 30000 records insertion only costs 9 seconds to complete!
          WITH US_PRICETYPES (PRICETYPE) AS (
              SELECT DISTINCT usprice.PRICETYPE FROM WWPRT.PRICE_CN usprice
              JOIN WWPRT.CONF_PRICETYPE pt ON pt.ID = usprice.PRICETYPE
              LEFT OUTER JOIN WWPRT.CONF_GAP gap ON pt.DOMAIN = gap.DOMAIN
              WHERE usprice.CABLEID = 'USCABF3FVT01'
              AND gap.DOMAIN IS NOT NULL
          )
          SELECT COUNT(*) FROM NEW TABLE
          (
              INSERT INTO WWPRT.CABLEPRODUCT_PRICETYPE_JOIN_CN
              (CABLEPRODUCTID, PRICETYPE, SLAVE)
              (
              SELECT cableproduct.ID, pricetypes.PRICETYPE, 'N'
              FROM WWPRT.CABLE_PRODUCT_JOIN_CN AS cableproduct,
              US_PRICETYPES AS pricetypes
              WHERE CABLEID = 'GEOANNF3FVT1'
              AND EXISTS (
                  ......
              )
          ))

          性能的改進(jìn)太可怕了,需要10個小時跑完的查詢10秒就結(jié)束了!盡管我用的是DB2,但我想對其它數(shù)據(jù)庫應(yīng)該也有這樣的問題,沒有驗證過…

          Followed:
          --------------------------------------------------------------------------------------
          謝謝Daniel的建議:

          如果程序不關(guān)心究竟有多少條記錄被插入了,可以用另一個 Fetch,而不是 Count(*),這樣性能會更好:

          SELECT CABLEPRODUCTID FROM NEW TABLE
          (INSERT INTO WWPRT.CABLEPRODUCT_PRICETYPE_JOIN_CN (CABLEPRODUCTID,
          PRICETYPE, SLAVE)
          (
               SELECT cableproduct.ID, pricetypes.PRICETYPE, 'N'
               FROM WWPRT.CABLE_PRODUCT_JOIN_CN AS cableproduct,
               US_PRICETYPES AS pricetypes
               WHERE CABLEID = 'GEOANNF3FVT1'
               AND EXISTS (
                   ......
               )
          )) FETCH FIRST 1 ROWS ONLY

          posted @ 2010-01-06 16:43 Alfred. Yao 閱讀(422) | 評論 (0)編輯 收藏

          僅列出標(biāo)題
          共2頁: 上一頁 1 2 
          主站蜘蛛池模板: 民权县| 石屏县| 闸北区| 莱州市| 老河口市| 石城县| 平山县| 和龙市| 泽普县| 宁晋县| 通榆县| 江阴市| 永春县| 浦城县| 姜堰市| 凉城县| 肇东市| 平顺县| 喀喇| 丹巴县| 福安市| 平湖市| 天祝| 河南省| 长治县| 浏阳市| 青冈县| 延长县| 邳州市| 平武县| 孙吴县| 阿拉尔市| 西峡县| 万宁市| 阿瓦提县| 卓资县| 松阳县| 宁波市| 安图县| 呼伦贝尔市| 安仁县|