qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          如何調試CSS的跨瀏覽器樣式bug

          首先要做的是挑選一個好的瀏覽器。我的選擇是Chrome,因為它擁有強大的調試工具。當我在Chrome上完成調試后,我會接著在Safari或者Firefox上調試。
            如果在這些“好的”瀏覽器上沒有達到期望的效果,很有可能是代碼本身違背了CSS規則。不要試圖使用hack方法來解決在這些“好的”瀏覽器上出現的問題,而是應該找出問題的原因。通常我會檢查以下可能的BUG出處:
            HTML代碼解釋 - 你是否忘記閉合一個標簽? 你是否用一個inline元素包住一個block元素? 違背標準的代碼可能在不同的瀏覽器上被解釋呈現成不同的效果。
            使用CSS lint工具檢查CSS代碼。留意那些檢查出來的Errors。多數情況下,Errors比Warnings更容易引發跨瀏覽器差異。
            忘記使用reset樣式表,而是依靠于(不同的)瀏覽器默認的CSS樣式。
            瀏覽器支持性的差異。你是否使用了高級CSS3屬性或者HTML5元素?查看瀏覽器支持性文檔從而確保所有你的受眾的瀏覽器都被涵蓋。你需要設計“功能降級”來支持老式的瀏覽器。比如,把陰影邊框降級成邊框,或者把圓角降級成方塊。
            在不該有空格的地方加上了空格,margin可能因此呈現得詭異。
            使用了絕對定位,可是沒有設置垂直和水平偏移。這種情況下,絕對定位的元素將被呈現在跟position:static一樣的位置上。但是,如果你嘗試更改它的top,right,bottom或者left值,這個元素將馬上“跳”到參照于它最近的相對定位的父元素的位置。
            按照“不尋常”的方式組合了不同display方式的元素。比如,W3C標準并沒有說當一個table-cell緊鄰一個浮動元素時應該是怎樣的layout。因此,這樣寫的代碼并不是錯誤的,但可能會導致跨瀏覽器呈現不同效果的BUG。
            空格是否影響了layout。你應該不想讓排版樣式依賴于空格。
            小數點像素值會導致跨瀏覽器的不同效果。
            接下來正文來了
            最重要的需要記住的就是,W3C標準并沒有定義錯誤的行為。因此,如果你沒有按照規范寫,那么可能會導致跨瀏覽器不同效果;如果你組合“奇怪的”屬性(例如margin和inline element),那么也可能會導致跨瀏覽器不同效果bug。
            Display
            我認為書寫CSS就像在選擇一段旅程。你需要作出一些決定.比如你要首先選擇使用不同display方式的元素:block,inline,inline-block和table。當你選擇好以后,你可以使用一些具體的方法來改變其實際的顯示。
            塊元素應該使用margin,padding,height和width。然而line-height不適用。
            行內元素應該使用line-height,vertical align和空格符。然而margin,padding,height和width不適用。
            首先,表格有垂直和水平排列方式。其次,如果你遺漏了表格中的某元素,整個表格可能會有詭異的顯示。最后,margin不適用與表格的行和列,padding不適用與表格和表格的行。
            Positioning
            如果你選擇使用塊級元素,接下來你需要選擇position方式:
            Float - 如果你使用了float,那么這個元素就變成了塊級元素,而之前作用于該元素的vertical-align和line-height屬性都將失效。
            Absolute - 相對于最近的相對定位的父節點來計算偏移量。當父節點和兄弟節點改變時,絕對定位的元素并不會導致reflow。這個特性有利于制作動畫效果。但是,如果使用了絕對定位和動態更改內容將可能會導致顯示問題,一個典型的例子是絕對定位的圓角框。
            Static – 默認的position方式。
            Fixed - 元素位置相對于瀏覽器窗口。不常使用的方式。
            Relative – 通常對于該元素樣式不影響。只是其下屬的絕對定位的子節點將相對于該節點計算偏移。
            在這里我就不列舉所有的display和position組合了。總之,有兩件事情需要注意:
            對于我選擇的display和position方式,其他的屬性(比如margin,line-height)是不是適合?
            兄弟節點的position方式是不是契合?
            比如,float,table-cell和行內元素組合在一起是否合適?瀏覽器將如何解釋渲染?在W3C標準里有沒有定義?如果沒有,那么可能就有出現跨瀏覽器bug的風險。當然,這樣的組合并不是不可以,但你要想清楚為什么要這樣做,以及做好足夠的跨瀏覽器測試
            Internet Explorer
            當你解決了在“好的”瀏覽器上出現的問題后,現在應開始著手IE平臺。我的建議是從你希望支持的最老版本的IE開始,因為很多老版本IE上的問題在新版本中延續出現。
            就算對于IE,你也應該嘗試找出問題而不是依賴于使用hack方法。盲目使用*和_的hack方法就像在一個返回錯誤值的函數中加入修正量(如下),而不是找出其中的算法性錯誤。
            return result + 4;
            當然,有時候在IE6和IE7里面使用hack是必要的。對于IE8,通常只在需要兼容CSS3的地方使用hack。通常情況下,在IE6/7里需要使用hack的地方有:
            hasLayout問題,使用zoom:1
            相對定位導致元素消失
            3px浮動BUG
            被撐大的容器的浮動錯誤,可是經常被overflow:hidden碰巧的掩飾了。
            還有一些不太常見的需要使用hack的情況,比如當兩個浮動元素中間有comment代碼時將會觸發重復內容bug。對于只在IE中出現的css問題,我的建議是仔細描述你所看見的,并在google中搜索相應的解決方法。在你找到bug原因前,不要盲目使用hack掩飾它。IE自帶的調試工具很糟糕,所以可能你需要給元素增加背景色來方便你查看頁面上真實的排版。
          實現解決方案
            當你找到bug的原因并且知道解決方法后,你同時也應該知道如何在修改代碼時不破壞已有的正常效果的代碼。下面是我的建議:
            1. 依賴樣式級聯
            2. 使用針對特定瀏覽器的前綴
            3. 使用針對IE6/7的*和_
            4. 不要使用針對IE8的\9
            5. 知道什么時候該放棄針對IE的hack
            6. 不要對最新版本的Firefox,Chrome,Safari使用任何hack
            1. 依賴樣式級聯
            首先,在任何可能的情況下都盡量依靠樣式級聯。瀏覽器總是采取他們能夠讀懂的最后聲明的樣式。所以,你應該從針對老版本瀏覽器的樣式開始書寫,這樣個瀏覽器就能讀懂和使用它能讀懂的最后的樣式。例如:
            .foo{
            background-color: #ccc; /* older browsers will use this */
            background-color: rgba(0,0,0,0.2); /* browsers that understand rgba will use this */
            }
            2. 使用針對特定瀏覽器的前綴
            使用針對特定瀏覽器的前綴,尤其對于還未被廣泛采用的屬性適用。例如:
            .foo{
            background: #1e5799; /* Old browsers */
            background: -moz-linear-gradient(top, #1e5799 0%, #2989d8 50%, #207cca 51%, #7db9e8 100%); /* FF3.6+ */
            background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#1e5799), color-stop(50%,#2989d8), color-stop(51%,#207cca), color-stop(100%,#7db9e8)); /* Chrome,Safari4+ */
            background: -webkit-linear-gradient(top, #1e5799 0%,#2989d8 50%,#207cca 51%,#7db9e8 100%); /* Chrome10+,Safari5.1+ */
            background: -o-linear-gradient(top, #1e5799 0%,#2989d8 50%,#207cca 51%,#7db9e8 100%); /* Opera 11.10+ */
            background: -ms-linear-gradient(top, #1e5799 0%,#2989d8 50%,#207cca 51%,#7db9e8 100%); /* IE10+ */
            background: linear-gradient(top, #1e5799 0%,#2989d8 50%,#207cca 51%,#7db9e8 100%); /* W3C */
            }
            注意,這套代碼里有兩個針對不同版本webkit的語法。前綴代碼的順序同樣應該從針對老版本瀏覽器開始書寫(參照第一條)。
            如果有一個W3C標準定義的語法,你應該把它放在最后(例如上述代碼最后一行)。這樣隨著瀏覽器開始支持這些新特性的標準語法,你的代碼也能夠穩健表現。
            3. 使用針對IE6/7的*和_對于舊版本IE特有的bug,使用*和_來彌補。比如:
            .clearfix {
            overflow: hidden; /* new formatting context in better browsers */
            *overflow: visible; /* protect IE7 and older from the overflow property */
            *zoom: 1; /* give IE hasLayout, a new formatting context equivalent */
            }
            所有的IE hack都針對于某版本和其之前的所有瀏覽器,比如:
            _ 針對IE6和更早版本
            * 針對IE7和更早版本
            \9 針對IE8和更早版本 (注意,IE9在某些CSS屬性上同樣對于這個hack敏感)
            所以,hack代碼的順序同樣也應該從針對老版本瀏覽器開始書寫(參照第一條)。
            4. 不要使用針對IE8的\9
            我從來不會使用\9來解決IE8里面出現的樣式bug。我只會在彌補瀏覽器支持性上使用\9來做“降級”處理。比如我使用了box-shadow(在更先進的瀏覽器上正常),可是在IE8下很難看,因此我使用了\9來增加了一個新border。這種情況不能依靠樣式級聯(參照第一條)來處理,因為這是增加一個新樣式,而不是修改一個已有的樣式。
            5. 知道什么時候該放棄針對IE的hack
            不要試追求在IE中得到一模一樣的效果。你是否愿意浪費額外的HTTP請求,繁雜的HTML/JS/CSS代碼段為了實現在IE6-8中同樣看到圓角框效果?對于我個人來說,我的答案是“不會”。
            你應該知道什么時候放棄針對某功能的hack。例如,不要使用filter去模擬CSS3里的漸變效果,那樣會導致性能問題和排版bug。最簡單的辦法是,壓根不要寄希望你的網頁在所有瀏覽器中都表現得一模一樣。對于IE 6-8的用戶,最好的辦法就是給他們一個簡單化的用戶體驗(注意,是簡單化而不是殘缺)。
            下述糟糕的代碼就是使用filter去模擬CSS3里的gradient:
            .foo {
            filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1e5799', endColorstr='#7db9e8',GradientType=0 ); /* IE6-9 */
            }
            6. 不要對最新版本的Firefox,Chrome,Safari使用任何hack
            對于Firefox,Chrome,Safari上的樣式bug,最好的辦法是仔細檢查,很有可能這是因為你的代碼違背了CSS的規則。

          posted @ 2014-09-17 09:46 順其自然EVO 閱讀(328) | 評論 (0)編輯 收藏

          XAMPP和Bugfree詳細教程

           一、XAMPP安裝配置
            xampp是一款跨平臺的集成 apache + mysql + php環境,是的配置AMP服務器變得簡單輕松,支持windows,solaris,
            下載地址:http://sourceforge.net/projects/xampp/files/
            啟動apache和mysql服務,如果apache不能成功啟動,最大原因是80端口被占用(),把占用端口的進程關掉即可.
          xampp默認安裝路徑為:C:\xampp
            解決方案:運行在cmd中運行 (安裝目錄)apache/bin/httpd.exe
            如我的路徑是“C:\xampp\apache\bin\httpd.exe”
            找到錯誤的具體原因(發現真的是端口被占用)
            “(OS 10048)通常每個套接字地址(協議/網絡地址/端口)只允許使用一次。  : AH00072: mak
            e_sock: could not bind to address 0.0.0.0:80
            AH00451: no listening sockets available, shutting down
            AH00015: Unable to open logs”
            轉自網絡
            XAMPP修改Apache端口 無法重啟服務
            由于先前裝了PHPNOW,之后又裝了XAMPP,XAMPP的apache服務就啟動不了,改了XAMPP的apache的端口號后,服務還是無法啟動;
            解決方法:由于apache中httpd.conf文件加載了SSL,而之前已有SSL,所以XAMPP要修改apache與SSL才可使用,或在httpd.conf文件中將SSL的加載用#號注釋掉,操作如下:
            打開apache配置文件,如D:\xampp\apache\conf\httpd.conf:
            修改“Listen 80”為“Listen 8080”;
            修改“ServerName localhost:80”為“ServerName localhost:8080”;
            打開SSL加載文件,如D:\xampp\apache\conf\extra\httpd-ssl.conf
            將所有443端口改為4433端口 或 直接在Apache配置文件httpd.conf中,去掉或注釋“Include "conf/extra/httpd-ssl.conf"”
            最后啟動mYSQL發現啟動不了,原因是我已經安裝了dedeCMS,其中也使用了XAMPP,所以果斷把安裝目錄刪除了,重裝,發現OK了,哈哈
            
            表示已經安裝成功,點擊界面上排右側可切換多種語言,這里選擇“中文”,將界面語言設置為中文。
            選擇左邊的“安全”選項
            
            紅字是不安全的,所以要去掉紅字。
            MySQL安全控制臺&XAMPP目錄保護
            瀏覽器中輸入http://localhost/security/xamppsecurity.php ,敲回車后出現如下圖:
            
            為mysql root設置密碼“111111”,輸入自己的密碼(這里需要自己設置密碼);PhpMyAdmin 認證選擇http,然后點擊【改變密碼】,密碼設置成功。
            設置Xampp目錄保護,輸入用戶名和密碼,點擊【保護XAMPP文件夾】后,提示XAMPP目錄保護設置成功。
            一定要記住密碼哦,每次配置的時候都需要輸入用戶名和密碼的。
            下面用PHPmyadimin配置mysql,在瀏覽器中輸入http://localhost/phpmyadmin,敲回車后如下圖
            
            下面用PHPmyadimin配置mysql,在瀏覽器中輸入http://localhost/phpmyadmin,敲回車后如下圖
            剛才設置的密碼在這里要用到了,用戶名輸入root,密碼輸入剛才自己設置的密碼。就可以進入數據庫了。
            如果重新登錄phpmyadmin,發現無法連接,需要在PHPmyadmin下配置config.inc.php文件,該文件位于
            C:\xampp\phpMyAdmin中,找到config.inc.php文件,打開編輯,配置如下:
            cfg[′Servers′][i]['auth_type']     = 'http';
            cfg[′Servers′][i]['user'] = 'root';
            cfg[′Servers′][i]['password'] = '123456';
            pwd那行,是根據自己情況設置的。保存一下就可以了。
            設置PHP運行于安裝模式
            這里設置和XAMPP 1.7的版本不同,1.8的版本中設置:
            在安裝好的XAMPP界面點擊Apache后側的Config會彈出一個下拉框,這里選擇打開
            PHP(php.ini),文檔打開后,查找safe_mode字段設置為ON保存退出。
            然后通過http://localhost/security/index.php檢查其狀態。
            bugfree3.0.4下載、安裝和配置:
            3.1 下載地址:
            3.2 安裝步驟:
            3.2.1 解壓后拷貝bugfree
            3.0.4至xampp安裝目錄的htdocs文件夾的根目錄下,例如D: \xampp\htdocs\,命名為bugfree3;
            3.2.2 瀏覽器中輸入http://localhost/bugfree3/install/,所有環境都檢查ok后 【繼續】按鈕變為可點擊狀態;
          上圖錯誤解決方法:xampp安裝目錄的htdocs文件夾的根目錄下新建BugFile文件夾。 修改后,點擊【繼續】至下圖
            解決:將上圖的數據庫用戶名改為root即可
            完成安裝,進入BugFree,初始用戶名: root  初始密碼:111111
            
            安裝成功了:
            

          posted @ 2014-09-17 09:42 順其自然EVO 閱讀(8398) | 評論 (1)編輯 收藏

          以用戶為中心的設計---可用性測試

            可用性測試是指,讓一群有代表性的用戶嘗試對產品進行典型造作,同時觀察員和開發人員在一旁觀察,聆聽,做記錄。
            每個產品設計者都希望自己的產品可用性非常的棒,非常的適合用戶使用習慣,減少用戶學習成本,并且具備更多人性化的功能。這也是每個互聯網公司所追求的,那么為了能在上線前,先評估產品的使用情況,我們會有一個用戶研究部門組織人員進行所謂的可用性測試。
            可用性測試有幾點我們需要明白:
            1.用戶是誰,找幾個用戶
            2.如何設計任務
            3.如何進行測試
            4.如何分析找到可用性問題
            1.用戶是誰,如何找用戶
            在我們進行可用性測試時,我們應該是找那些用戶,幾個用戶,一般來說多找“輕用戶”和“有潛在需求的人”;如果我們資金不足的情況下,可請5名參與測試人員和5名陪同測試人員,(也不是說人員越來約好,5個人可以把產品問題的80%找到),1個主持人。裝上必要的屏幕錄像軟件,即時的記錄用戶的操作軌跡。
            2.如何設計任務
            如果你實在不知道該測試那些,那么請您把產品的功能,按順序排列到表格,找出產品核心功能,再找出核心功能5個最重要的模塊,或者按照核心功能設計5個任務,任務不宜過多,如果多了測試人員煩躁,陪同人員也沒不耐煩。
            3.如何進行測試
            不要聽用戶說,要看他操作,說的不必做的來得實在。人往往是說一套做一套的。如果用戶操作出錯,先不用提醒,看用戶是否自行解決,陪同人員同時記錄問題,但不要急于討論問題的解決方案。馬上想到的方案或者用戶提出的方案都不一定是最好的。這個工作可以留待可以安靜思考或者大家討論時進行。
            4.如何分析找到可用性問題
            召集所有陪同人員,將所以記錄的問題匯總,然后找出可以馬上改進的問題,先把這些問題處理,接下來把大的東西再仔細的通過錄像,筆記進行仔細的考慮,后續與產品,交互等一起進行改進。

          posted @ 2014-09-17 09:40 順其自然EVO 閱讀(215) | 評論 (0)編輯 收藏

          關于UNIX功能測試宏

           UNIX的功能測試宏,在頭文件中定義了很多POSIX.1和XPG3的符號。但是除了POSIX.1和XPG3定義外,大多數實現在這些頭文件中也加上了他們自己的定義。如果在編譯一個程序時,希望它只是用POSIX定義而不使用任何實現定義的限制,那么就需要定義常數_POSIX_SOURCE,所有POSIX.1頭文件中都是用此常數。當該常數定義時,就能排除任何實現專有的定義。
            常數_POSIX_SOURCE及對應的常數_XOPEN_SOURCE被稱為功能性測試宏(feature test macro)。所有功能測試宏都以下劃線開始。要使用他們時,通常在cc命令行中以下列方式定義:
            cc -D_POSIX_SOURCE file.c
            這使得在C程序包括任何頭文件之前,定義了功能測試宏。如果我們僅想用POSIX.1定義,那么也可以將源文件的第一行設置為:
            #define _POSIX_SOURCE 1
            另一個功能測試宏是:__STDC__,它由符合ANSI C標準的編譯程序自動定義。這樣就允許我們編寫ANSI C編譯程序和非ANSI C編譯程序都能編譯的程序。例如,一個頭文件可能會是:
          void *myfunc(const char*, int);
          #else
          void *myfunc();
          #endif
          #ifdef __STDC__
          void *myfunc(const char*, int);
          #else
          void *myfunc();
          #endif
            這樣就能發揮ANSI C原型功能的長處,要注意在開始和結束的兩個連續的下劃線常常打印成一個長下劃線。

          posted @ 2014-09-17 09:38 順其自然EVO 閱讀(216) | 評論 (0)編輯 收藏

          測試用例在Scrum中有一席之地嗎?

           在Scrum中,需求通常以用戶故事表達。那么在Scrum中可以使用用例嗎?如果可以的話,什么情況下我們應該使用用例呢?
            Scott Kendrick問到:
            用例在Scrum中有一席之地嗎?我的直覺是,如果正確編寫了用戶故事,那就足以驅動討論和協作了,同時也足以用來制定測試用例了。
            首先,Scrum要求我們使用用戶故事,而不要使用用例嗎?Roy Morien認為不是:
            Scrum沒有強制任何引發誘導和記錄需求的方法,除了推薦面對面的對話、日常的站立會議(當然如果你想坐下也可以)、sprint計劃會議、甚至是用戶故事分析,Scrum推薦的就只有協作活動和透明性了。根據這些指導原則,我想這取決于你實際想做什么。
            鑒于此,在什么情況下你會想使用用戶故事呢?Charles Bradley建議:
            通常對于新的Scrum團隊,在他們轉向Scrum的頭幾個月,我建議他們就使用他們以前的需求搜集方法。學習Scrum時,不去學習一種全新的需求搜集方法會讓學習變得非常困難。
            同時Charles Bradley認為,“[……]Scrum的指導原則表明大部分Scrum團隊應該使用用戶故事,而對于那些要求‘任務/生命周期的行為要非常確定’的團隊,可以使用用例”。Adam Sroka不同意這種方法:
            傳統觀點認為,“關鍵”的應用程序需要更多文檔。我認為這是不對的。關鍵應用程序需要的是更多(以及更好)的驗證。要做到這一點,就需要詳盡的自動化測試,許多做“關鍵”應用程序的團隊都不那么做,這點我不能理解。
            但是,在純粹的功能范圍外,用例文檔可能會提供價值。Charles Bradley寫到:
            嗯,我曾經在航空領域工作過一段時間,盡管我沒有完備的知識來支持這份工作(比如,什么需求必須具備這個東西),在我們從事文檔工作的時候,讓我記憶猶新的是,編寫文檔的目的不是過程審計,而是找出飛機墜毀的起因和責任方(監管部門,訴訟保護)。因此,某些必要的文檔有助于(保護公司)那樣的工作,而且我認為,在某些時候用例可能會比用戶故事更加有助于證實你的案例(避免出錯)。
            像敏捷方法的所有方面一樣,對于用例給組織帶來的價值,應該要仔細檢查。你從付出的精力中究竟得到了什么?畢竟,就像Ron Jeffreis所說的,“我還沒有碰到過很多實際的人,真正善于編寫用例。”如果你承認你可能不擅長編寫用例,那么有什么事情是你一直在做的,能給你的組織帶來更多價值?

          posted @ 2014-09-17 09:32 順其自然EVO 閱讀(292) | 評論 (1)編輯 收藏

          Java中的JSON對象的使用

          申明:沒工作之前都沒聽過JSON,可能是自己太菜了。可能在前臺AJAX接觸到JSON,這幾天要求在純java的編程中,返回JSON字符串形式。
            網上有兩種解析JSON對象的jar包:JSON-lib.jar和json.jar,這里主要介紹JSON-lib.jar。
            jar包地址如下:
            json-lib-2.4-jdk15.jar所需全部JAR包.rar
            一、JSON-lib.jar還依賴以下jar包:
            commons-lang.jar
            commons-beanutils.jar
            commons-collections.jar
            commons-logging.jar
            ezmorph.jar
            json-lib-2.2.2-jdk15.jar
            二、應用
            JSON也是以key-value形式存在的。key是字符串,value可以是基本類型、JSONArray、JSONObject.
            JSONArray:[],望文生義也知道,他是數組形式,又可要放多個JSON
            JSONObject:{}就放一個JSON。
            由于他們的他們可以嵌套形式就比較多。
            三、輸出JSON實例考慮到對[]、{}進行對比,區別重復的變量,對變量名進行了首字母大寫,顯得不規范了。
          import net.sf.json.JSONArray;
          import net.sf.json.JSONObject;
          public class JSONTest {
          public static void main(String[] args) {
          JSONObject container1 = new JSONObject();
          container1.put("ClassName", "高三一班");
          System.out.println(container1.toString());
          JSONArray className = new JSONArray();
          className.add("高三一班");
          container1.put("className", className);
          System.out.println(container1.toString());
          JSONObject classInfo = new JSONObject();
          classInfo.put("stuCount", 50);
          classInfo.put("leader", "rah");
          container1.put("classInfo", classInfo);
          System.out.println(container1);
          JSONObject ClassInfo = new JSONObject();
          JSONArray stuCount = new JSONArray();
          stuCount.add(50);
          JSONArray leader = new JSONArray();
          leader.add("rah");
          ClassInfo.put("stuCount", stuCount);
          ClassInfo.put("leader", leader);
          container1.put("ClassInfo", ClassInfo);
          System.out.println(container1);
          JSONArray students = new JSONArray();
          JSONObject studentOne = new JSONObject();
          studentOne.put("name", "張麻子");
          studentOne.put("sex", "男");
          studentOne.put("age", 12);
          studentOne.put("hobby", "java develop");
          JSONObject studentTwo = new JSONObject();
          studentTwo.put("name", "王瘸子");
          studentTwo.put("sex", "男");
          studentTwo.put("age", 13);
          studentTwo.put("hobby", "C/C++ develop");
          students.add(studentOne);
          students.add(studentTwo);
          container1.put("students", students);
          System.out.println(container1);
          JSONArray Students = new JSONArray();
          JSONObject StudnetOne = new JSONObject();
          JSONArray name1 = new JSONArray();
          name1.add("張麻子");
          JSONArray sex1 = new JSONArray();
          sex1.add("男");
          JSONArray age1= new JSONArray();
          age1.add("12");
          JSONArray hobby1 = new JSONArray();
          hobby1.add("java develop");
          StudnetOne.put("name", name1);
          StudnetOne.put("sex", sex1);
          StudnetOne.put("age", age1);
          StudnetOne.put("hobby", hobby1);
          JSONObject StudnetTwo = new JSONObject();
          JSONArray name2 = new JSONArray();
          name2.add("王瘸子");
          JSONArray sex2 = new JSONArray();
          sex2.add("男");
          JSONArray age2= new JSONArray();
          age2.add("13");
          JSONArray hobby2 = new JSONArray();
          hobby2.add("C/C++ develop");
          StudnetTwo.put("name", name2);
          StudnetTwo.put("sex", sex2);
          StudnetTwo.put("age", age2);
          StudnetTwo.put("hobby", hobby2);
          Students.add(StudnetOne);
          Students.add(StudnetTwo);
          container1.put("Students", Students);
          System.out.println(container1);
          JSONArray teachers = new JSONArray();
          teachers.add(0,"王老師");
          teachers.add(1,"李老師 ");
          container1.put("teachers", teachers);
          System.out.println(container1);
          JSONArray Teachers = new JSONArray();
          JSONObject teacher1 = new JSONObject();
          teacher1.put("name", "小梅");
          teacher1.put("introduce","他是一個好老師");
          JSONObject teacher2 = new JSONObject();
          teacher2.put("name", "小李");
          teacher2.put("introduce","他是一個合格的老師");
          Teachers.add(0,teacher1);
          Teachers.add(1,teacher2);
          container1.put("Teachers", Teachers);
          System.out.println(container1);
          }
          }
          運行結果:
          {"ClassName":"高三一班"}
          {"ClassName":"高三一班","className":["高三一班"]}
          {"ClassName":"高三一班","className":["高三一班"],"classInfo":{"stuCount":50,"leader":"rah"}}
          {"ClassName":"高三一班","className":["高三一班"],"classInfo":{"stuCount":50,"leader":"rah"},"ClassInfo":{"stuCount":[50],"leader":["rah"]}}
          {"ClassName":"高三一班","className":["高三一班"],"classInfo":{"stuCount":50,"leader":"rah"},"ClassInfo":{"stuCount":[50],"leader":["rah"]},"students":[{"name":"張麻子","sex":"男","age":12,"hobby":"java develop"},{"name":"王瘸子","sex":"男","age":13,"hobby":"C/C++ develop"}]}
          {"ClassName":"高三一班","className":["高三一班"],"classInfo":{"stuCount":50,"leader":"rah"},"ClassInfo":{"stuCount":[50],"leader":["rah"]},"students":[{"name":"張麻子","sex":"男","age":12,"hobby":"java develop"},{"name":"王瘸子","sex":"男","age":13,"hobby":"C/C++ develop"}],"Students":[{"name":["張麻子"],"sex":["男"],"age":["12"],"hobby":["java develop"]},{"name":["王瘸子"],"sex":["男"],"age":["13"],"hobby":["C/C++ develop"]}]}
          {"ClassName":"高三一班","className":["高三一班"],"classInfo":{"stuCount":50,"leader":"rah"},"ClassInfo":{"stuCount":[50],"leader":["rah"]},"students":[{"name":"張麻子","sex":"男","age":12,"hobby":"java develop"},{"name":"王瘸子","sex":"男","age":13,"hobby":"C/C++ develop"}],"Students":[{"name":["張麻子"],"sex":["男"],"age":["12"],"hobby":["java develop"]},{"name":["王瘸子"],"sex":["男"],"age":["13"],"hobby":["C/C++ develop"]}],"teachers":["王老師","李老師 "]}
          {"ClassName":"高三一班","className":["高三一班"],"classInfo":{"stuCount":50,"leader":"rah"},"ClassInfo":{"stuCount":[50],"leader":["rah"]},"students":[{"name":"張麻子","sex":"男","age":12,"hobby":"java develop"},{"name":"王瘸子","sex":"男","age":13,"hobby":"C/C++ develop"}],"Students":[{"name":["張麻子"],"sex":["男"],"age":["12"],"hobby":["java develop"]},{"name":["王瘸子"],"sex":["男"],"age":["13"],"hobby":["C/C++ develop"]}],"teachers":["王老師","李老師 "],"Teachers":[{"name":"小梅","introduce":"他是一個好老師"},{"name":"小李","introduce":"他是一個合格的老師"}]}
            四、遍歷JSON實例
            以上面的輸出的JSON字符串進行按順序給它遍歷
            String ClassName1 = (String) container1.get("ClassName");
            System.out.println("ClassName data is: " + ClassName1);
            JSONArray className1 = container1.getJSONArray("className");
            System.out.println("className data is: " + className1);
            JSONObject classInfo1 = container1.getJSONObject("classInfo");
            System.out.println("classInfo data is: " + classInfo1);
            JSONObject ClassInfo1 = container1.getJSONObject("ClassInfo");
            System.out.println("ClassInfo data is: " + ClassInfo1);
            JSONArray students1 = container1.getJSONArray("students");
            System.out.println("students data is: " + students1);
            JSONArray Students1 = container1.getJSONArray("Students");
            System.out.println("Students data is: " + Students1);
            JSONArray teachers1 = container1.getJSONArray("teachers");
            for(int i=0; i < teachers1.size(); i++){
            System.out.println("teahcer " + i + " is: "+ teachers1.get(i));
            }
            JSONArray Teachers1 = container1.getJSONArray("Teachers");
            for(int i=0; i < Teachers1.size(); i++){
            System.out.println("Teachers " + i + " is: "+ Teachers1.get(i));
            }
            遍歷結果:
            ClassName data is: 高三一班
            className data is: ["高三一班"]
            classInfo data is: {"stuCount":50,"leader":"rah"}
            ClassInfo data is: {"stuCount":[50],"leader":["rah"]}
            students data is: [{"name":"張麻子","sex":"男","age":12,"hobby":"java develop"},{"name":"王瘸子","sex":"男","age":13,"hobby":"C/C++ develop"}]
            Students data is: [{"name":["張麻子"],"sex":["男"],"age":["12"],"hobby":["java develop"]},{"name":["王瘸子"],"sex":["男"],"age":["13"],"hobby":["C/C++ develop"]}]
            teahcer 0 is: 王老師
            teahcer 1 is: 李老師
            Teachers 0 is: {"name":"小梅","introduce":"他是一個好老師"}
            Teachers 1 is: {"name":"小李","introduce":"他是一個合格的老師"}
            上面包括了大部份的JSON的嵌套形式,可能有忽略的也可以參考上面的內容。

          posted @ 2014-09-16 09:52 順其自然EVO 閱讀(261) | 評論 (0)編輯 收藏

          IOS開發之顯示微博表情

           在上一篇博客中山寨了一下新浪微博,在之后的博客中會對上一篇代碼進行優化和重用,上一篇的微博請求的文字中有一些表情沒做處理,比如帶有表情的文字是這樣的“我要[大笑],[得意]”。顯示的就是請求的字符串,那么我們如何把文字在本地轉換成表情呢?下面將要說一下顯示表情的解決方案。
            要用到的知識:IOS開發中的資源文件.plist, 可變的屬性字符串,TextView和正則表達式的使用。
            解決的整體思路:把源字符串同過正則匹配獲取到每個表情的range, 再通過range獲取元字符串中的表情字符串,如[哈哈], 在把[哈哈] 和我們.plist中item下的chs字段匹配,然后獲取對應的圖片名,獲取圖片后把圖片轉換成可變字符串的附件,然后做一個替換即可。先這么大致一說,下面會詳細的講解一下。
            1.要想在我們手機上顯示網絡請求的表情,首先我們本地得有相應的資源文件,在.plist文件中又我們想要的東西,其中存儲的東西如下所示,整個root是一個數組,數組中的item是一個字典,字典中存放的時文字到圖片名的一個映射,當然啦,圖片名和我們本地資源的圖片名相同。截圖如下
            2.如何從.plist文件中獲取數據呢?先通過bundle獲取資源文件的路徑,在通過文件路徑創建數組,數組中存儲的數據就是文件中的內容代碼如下:
            //加載plist文件中的數據
            NSBundle *bundle = [NSBundle mainBundle];
            //尋找資源的路徑
            NSString *path = [bundle pathForResource:@"emoticons" ofType:@"plist"];
            //獲取plist中的數據
            NSArray *face = [[NSArray alloc] initWithContentsOfFile:path];
            3.生成我們的測試字符串,最后一個不是任何表情,不做替換。
            //我們要顯示的字符串(模擬網路請求的字符串格式)
            NSString *str = @"我[圍觀]你[威武]你[嘻嘻]我[愛你]你[兔子]我[酷]你[帥]我[思考]你[錢][123456]";
            4.把上面的str轉換為可變的屬性字符串,因為我們要用可變的屬性字符串在TextView上顯示我們的表情圖片,轉換代碼如下:
            //創建一個可變的屬性字符串
            NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:str];
            5.進行正則匹配,獲取每個表情在字符串中的范圍,下面的正則表達式會匹配[/*],所以[123567]也會被匹配上,下面我們會做相應的處理
            //正則匹配要替換的文字的范圍
            //正則表達式
            NSString * pattern = @"\\[[a-zA-Z0-9\\u4e00-\\u9fa5]+\\]";
            NSError *error = nil;
            NSRegularExpression * re = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
            if (!re) {
            NSLog(@"%@", [error localizedDescription]);
            }
            //通過正則表達式來匹配字符串
            NSArray *resultArray = [re matchesInString:str options:0 range:NSMakeRange(0, str.length)]; 6.數據準備工作完成,下面開始遍歷資源文件找到文字對應的圖片,找到后把圖片名存入字典中,圖片在源字符串中的位置也要存入到字典中,最后把字典存入可變數組中。代碼如下:
          1     //用來存放字典,字典中存儲的是圖片和圖片對應的位置
          2     NSMutableArray *imageArray = [NSMutableArray arrayWithCapacity:resultArray.count];
          3
          4     //根據匹配范圍來用圖片進行相應的替換
          5     for(NSTextCheckingResult *match in resultArray) {
          6         //獲取數組元素中得到range
          7         NSRange range = [match range];
          8
          9         //獲取原字符串中對應的值
          10         NSString *subStr = [str substringWithRange:range];
          11
          12         for (int i = 0; i < face.count; i ++)
          13         {
          14             if ([face[i][@"chs"] isEqualToString:subStr])
          15             {
          16
          17                 //face[i][@"gif"]就是我們要加載的圖片
          18                 //新建文字附件來存放我們的圖片
          19                 NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
          20
          21                 //給附件添加圖片
          22                 textAttachment.image = [UIImage imageNamed:face[i][@"png"]];
          23
          24                 //把附件轉換成可變字符串,用于替換掉源字符串中的表情文字
          25                 NSAttributedString *imageStr = [NSAttributedString attributedStringWithAttachment:textAttachment];
          26
          27                 //把圖片和圖片對應的位置存入字典中
          28                 NSMutableDictionary *imageDic = [NSMutableDictionary dictionaryWithCapacity:2];
          29                 [imageDic setObject:imageStr forKey:@"image"];
          30                 [imageDic setObject:[NSValue valueWithRange:range] forKey:@"range"];
          31
          32                 //把字典存入數組中
          33                 [imageArray addObject:imageDic];
          34
          35             }
          36         }
          37     }
            7.轉換完成,我們需要對attributeString進行替換,替換的時候要從后往前替換,弱從前往后替換,會造成range和圖片要放的位置不匹配的問題。替換代碼如下:
          1     //從后往前替換
          2     for (int i = imageArray.count -1; i >= 0; i--)
          3     {
          4         NSRange range;
          5         [imageArray[i][@"range"] getValue:&range];
          6         //進行替換
          7         [attributeString replaceCharactersInRange:range withAttributedString:imageArray[i][@"image"]];
          8
          9     }
            8.把替換好的可變屬性字符串賦給TextView
            1     //把替換后的值賦給我們的TextView
            2     self.myTextView.attributedText = attributeString;
            9.替換前后效果如下:

          posted @ 2014-09-16 09:52 順其自然EVO 閱讀(1737) | 評論 (1)編輯 收藏

          VS2013單元測試(使用VS2013自帶的單元測試)

           1、打開VS3013,隨便建一個解決方案,比如叫:LearnUnitTest,建一個類庫項目LearnUnitTest_Bank,該項目中添加一個BankAccount類,這個類及類中的方法就是我們要測試的對象。
            2、給LearnUnitTest添加一個測試項目:在解決方案名稱上右鍵=》添加=》新建項目=》VisualC#=》測試=》單元測試項目,項目名稱叫LearnUnitTest_BankTest,將LearnUnitTest_Bank添加為LearnUnitTest_BankTest的引用項目,將測試項目LearnUnitTest_BankTest里默認生成的類重命名為BankAccountTest。
            對于BankAccountTest類,類上有注解TestClass,方法上有注解TestMethod。可以在這類文件里添加其他類和方法,供測試方法使用。
            首個測試:
            3、現在我們測試BankAccount類的Debit方法,我們預先確定此次測試要檢查如下方面:
            a、如果信用余額(credit amount)比賬戶余額大,該方法就拋異常ArgumentOutOfRangeException
            b、如果信用余額小于0也拋異常
            c、如果a和b都滿足,該方法會從賬戶余額里減去amount(函數參數)
            注意:由a、b、c可以看郵BankAccount類中的Debit方法最后一行應該是-=,而不是+=——當然了,這個是故意留下的bug,而不是微軟的失誤,就等著在這次測試中把它測出來,然后修正掉。
            在測試類里添加如下方法測試Debit方法:
          // unit test code
          [TestMethod]
          public void Debit_WithValidAmount_UpdatesBalance()
          {
          // arrange
          double beginningBalance = 11.99;
          double debitAmount = 4.55;
          double expected = 7.44;
          BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
          // act
          account.Debit(debitAmount);
          // assert
          double actual = account.Balance;
          Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
          }
            測試方法的要求:
            必須要有TestMethod注解,返回類型為void,不能有參數。
            經過測試,我們發現了bug,把+=改為-=即可。
            使用單元測試改善代碼:
            依然是測試Debit,本次測試想完成以下意圖:
            a、如果credit amount(指的應該就是debit amount)比balance大,方法就拋ArgumentOutOfRangeException
            b、如果credit amount比0小,也拋ArgumentOutOfRangeException異常
            (1)創建測試方法
            首次嘗試創建一個測試方法來處理上述問題:
            代碼:
          //unit test method
          [TestMethod]
          [ExpectedException(typeof(ArgumentOutOfRangeException))]
          public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
          {
          // arrange
          double beginningBalance = 11.99;
          double debitAmount = -100.00;
          BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
          // act
          account.Debit(debitAmount);
          // assert is handled by ExpectedException
          }
            注意這個方法:Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange,意思是:當debit amount小于0時,本次測試應該會導致被測試的方法拋出ArgumentOutOfRange異常,否則本次測試就失敗了,沒有達到期望,需要修改Debit代碼以達成本次測試期望——正所謂TDD開發。
            我們使用了ExpectedExceptionAttribute特性來斷言期望的異常應當被拋出。除非方法拋出ArgumentOutOfRangeException異常,否則該特性就會導致測試失敗(要注意本次測試的意圖)。用正的和負的debitAmount運行這個測試,然后臨時把被測試的方法(Debit方法)修改一下:當demit amount小于0時拋出一個ApplicatinException。搗騰完這些,發現本次測試基本沒什么問題。
            為了測試debit amount 大于balance的情形,我們做下面幾個操作:
            a、創建一個新的測試方法名叫    Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
            b、從上一個測試方法
            Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
            復制方法體到本測試方法
            c、把debitAmount設置為一個比balance大的值
            (2)運行測試方法
            用不同的debitAmount值運行Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
            和 Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
            然后運行三個測試,這樣我們最開始設定的三個cases都被覆蓋了。
           (3)繼續分析
            后面兩個測試方法Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
            和Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
            有些問題:兩個測試運行的時候根據拋出的異常,你不知道是誰拋出的,靠ExpectedException特性做不到這件事。
            可以這樣修改:
            在類里定義兩個常量:
          // class under test
          public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
          public const string DebitAmountLessThanZeroMessage = "Debit amount less than zero";
          // method under test
          // ...
          if (amount > m_balance)
          {
          throw new ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
          }
          if (amount < 0)
          {
          throw new ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
          }
          // ...
            (4)重構測試方法
            首先,移除ExpectedException特性。取而代之的處理是:我們捕獲異常,來核實是在哪種條件下拋出的。
            修改一下Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
            方法:
          [TestMethod]
          public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
          {
          // arrange
          double beginningBalance = 11.99;
          double debitAmount = 20.0;
          BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\
          // act
          try
          {
          account.Debit(debitAmount);
          }
          catch (ArgumentOutOfRangeException e)
          {
          // assert
          StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
          }
          }
            (5)再次測試,再次重寫,再次分析
            當我們用不的參數再次運行測試方法Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
            的時候,會遇到下面一些問題:
            1、如果我們使用一個比balance大的debitAmount運行,產生的測試結果是所期望的。
            2、如果使用了一個debitAmount運行,使得assert 斷言失敗了(比如在Debit方法的某一行返回了一個非期望的異常),也沒什么問題,在本測試的情理之中。
            3、如果debitAmount是有效的(比0大比balance小)會發生什么呢?沒有異常拋出,斷言也不會失敗,測試方法通過了。——這不是我們想要的,注意我們此次的測試初衷:要么斷言成功,要么斷言失敗,如果壓根進入不了斷言代碼,只能說明測試方法寫的問題!
            為了解決這個問題,我們在測試方法的最后一行加入一個Fail斷言,來處理沒有異常發生的情況:沒有異常發生,就說明此次測試沒有達到期望!
            但是修改好再次運行,會發現如果所期望的異常被捕獲了,測試總會失敗。為了解決這個問題,我們在StringAssert之前加一個return。
            最終我們的Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
            方法如下:
          [TestMethod]
          public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange()
          {
          // arrange
          double beginningBalance = 11.99;
          double debitAmount = 20.0;
          BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\
          // act
          try
          {
          account.Debit(debitAmount);
          }
          catch (ArgumentOutOfRangeException e)
          {
          // assert
          StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage);
          return;
          }
          Assert.Fail("No exception was thrown.")
          }
            最終我們讓測試代碼變得更加強健,但更重要的是,在這個過程中,我也們也改善了被測試的代碼——這才是測試的最終目的。

          posted @ 2014-09-16 09:51 順其自然EVO 閱讀(1348) | 評論 (0)編輯 收藏

          關于UNIX功能測試宏

          UNIX的功能測試宏,在頭文件中定義了很多POSIX.1和XPG3的符號。但是除了POSIX.1和XPG3定義外,大多數實現在這些頭文件中也加上了他們自己的定義。如果在編譯一個程序時,希望它只是用POSIX定義而不使用任何實現定義的限制,那么就需要定義常數_POSIX_SOURCE,所有POSIX.1頭文件中都是用此常數。當該常數定義時,就能排除任何實現專有的定義。
            常數_POSIX_SOURCE及對應的常數_XOPEN_SOURCE被稱為功能性測試宏(feature test macro)。所有功能測試宏都以下劃線開始。要使用他們時,通常在cc命令行中以下列方式定義:
            cc -D_POSIX_SOURCE file.c
            這使得在C程序包括任何頭文件之前,定義了功能測試宏。如果我們僅想用POSIX.1定義,那么也可以將源文件的第一行設置為:
            #define _POSIX_SOURCE 1
            另一個功能測試宏是:__STDC__,它由符合ANSI C標準的編譯程序自動定義。這樣就允許我們編寫ANSI C編譯程序和非ANSI C編譯程序都能編譯的程序。例如,一個頭文件可能會是:
          void *myfunc(const char*, int);
          #else
          void *myfunc();
          #endif
          #ifdef __STDC__
          void *myfunc(const char*, int);
          #else
          void *myfunc();
          #endif
            這樣就能發揮ANSI C原型功能的長處,要注意在開始和結束的兩個連續的下劃線常常打印成一個長下劃線。

          posted @ 2014-09-16 09:51 順其自然EVO 閱讀(266) | 評論 (0)編輯 收藏

          數據庫中的事物處理

           數據庫
            數據庫的更新通常都是由客觀世界的所發生的事件引起的。為保證數據庫內容的一致,就要將數據庫的一組操作作為一個整體來進行,要么全部成功完成,要么全部失敗退出。如果由于故障或其它原因而使一組操作中有一些完成,有一些未完成,則必然會使得數據庫中的數據出現不一致,從而使得數據庫的完整性受到破壞。因此,更新操作序列必須作為一個整體在DBMS執行時出現,即“要么全做,要么全不做”。SQL提供了事務處理的機制,來幫助DBMS實現上述的功能。
            事務處理
            事務處理(TRANSACTION)的每個語句是由一個或多個SQL語句序列結合在一起所形成的一個邏輯處理單元。事務處理中句都是完成整個任務的一部分工作,所有的語句組織在一起能夠完成某一特定的任務。DBMS在對事務處理中的語句進行處理時,是按照下面的約定來進行的,這就是“事務處理中的所有語句被作為一個原子工作單位,所有的語句既可成功地被執行,也可以沒有任何一個語句被執行”。DBMS負責完成這種約定,即使在事務處理中應用程序異常退出,或者是硬件出現故障等各種意外情況下,也是如此。在任何意外情況下,DBMS都負責確保在系統恢復正常后,數據庫內容決不會出現“部分事務處理中的語句被執行完”的情況。
            sql語言
            sql語言為事務處理提供了兩個重要的語句,它們是COMMIT和ROLLBACK語句。它們的使用格式是:
            COMMIT WORK
            ROLLBACK WORK
            COMMIT語句用于告訴DMBS,事務處理中的語句被成功執行完成了。被成功執行完成后,數據庫內容將是完整的。而ROLLBACK語句則是用于告訴DBMS,事務處理中的語句不能被成功執行。這時候,DBMS將恢復本次事務處理期間對數據庫所進行的修改,使之恢復到本次事務處理之前的狀態。
            事務處理:
          QSqlDatabase::database().transaction();
          QSqlQuery query;
          query.exec("SELECT id FROM employee WHERE name = 'Torild Halvorsen'");
          if (query.next())
          {
          int employeeId = query.value(0).toInt();
          query.exec("INSERT INTO project (id, name, ownerid) "
          "VALUES (201, 'Manhattan Project', "
          + QString::number(employeeId) + ")");
          }
          QSqlDatabase::database().commit();
            如果數據庫引擎支持事務處理,則函數QSqlDriver::hasFeature(QSqlDriver::Transactions)將返回 真。
            可以通過調用QSqlDatabase::transaction()來初始化一個事務處理。之后執行你想在該事務處理的工作。
            完了再執行QSqlDatabase::commit()來提交事務處理或QSqlDatabase::rollback()取消事務處理。

          posted @ 2014-09-16 09:51 順其自然EVO 閱讀(199) | 評論 (0)編輯 收藏

          僅列出標題
          共394頁: First 上一頁 46 47 48 49 50 51 52 53 54 下一頁 Last 
          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 道真| 博客| 武穴市| 阿巴嘎旗| 安新县| 句容市| 会宁县| 宁南县| 石河子市| 鄂州市| 建昌县| 临颍县| 临沧市| 宁城县| 宜丰县| 马尔康县| 绿春县| 青浦区| 长春市| 许昌县| 横峰县| 昌乐县| 福建省| 宝清县| 鄂伦春自治旗| 康保县| 长武县| 淮滨县| 平湖市| 肃南| 安西县| 邹平县| 潞城市| 沈阳市| 大竹县| 邳州市| 莱西市| 山丹县| 安泽县| 沈丘县| 柳江县|