解決驗證碼最基礎的幾種方法其實不叫解決,只能算是避過驗證碼,真正的驗證碼識別,需要用的很多技術,圖像識別源碼分析,網絡數據流等等。驗證碼只是為了防止防止用戶誤操作,或者防止某些刷子,減少服務器的壓力而設置的,如果真的是為了
自動化測試,我們完全可以屏蔽改功能。
對于
web應用來說,大部分的系統在用戶登錄時都要求用戶輸入驗證碼,驗證碼的類型的很多,有字母數字的,有漢字的,甚至還要用戶輸入一條算術題的答案的,對于系統來說使用驗證碼可以有效果的防止采用機器猜測方法對口令的刺探,在一定程度上增加了安全性。但對于測試人員來說,不管是進行
性能測試還是自動化測試都是一個棘手的問題。
下面來談一下處理驗證碼的幾種方法。
去掉驗證碼
這是最簡單的方法,對于開發人員來說,只是把驗證碼的相關代碼注釋掉即可,如果是在測試環境,這樣做可省去了測試人員不少麻煩,如果自動化腳本是要在正式環境跑,這樣就給系統帶來了一定的風險。
設置萬能碼
去掉驗證碼的主要是安全問題,為了應對在線系統的安全性威脅,可以在修改程序時不取消驗證碼,而是程序中留一個“后門”---設置一個“萬能驗證碼”,只要用戶輸入這個“萬能驗證碼”,程序就認為驗證通過,否則按照原先的驗證方式進行驗證。
#coding=utf-8
importrandom
#生成0到10之間的隨機數#d = random.uniform(0,10)#print d#生成一個1000到9999之間的隨機整數
d = random.randint(1000,9999)
printu"生成的隨機數:%d"%d i = input(u"請輸入隨機數:")
printiifi == d:printu"登錄成功!!"
elifi == 1111:printu"登錄成功!!"
else:printu"請重新輸入驗證碼!"
運行結果:
>>> ================================ RESTART ================================ >>> 生成的隨機數:3764 請輸入隨機數:1111 1111 登錄成功!! >>> ================================ RESTART ================================ >>> 生成的隨機數:3763 請輸入隨機數:3763 3763 登錄成功!! >>> ================================ RESTART ================================ >>> 生成的隨機數:1928 請輸入隨機數:1354646 1354646 請重新輸入驗證碼! |
random用于生成隨機數
randint()方法用于生成隨機整數,傳遞的兩個參數分別是隨機數的范圍,randint(1000,9999)第二個參數要大于第一個參數。
我們要求用戶輸入隨機數,并且對用戶輸入做判斷,如果等于生成的隨機數那么,登錄成功,如果等于1111也算登錄成功,否則失敗。那么等于1111的判斷就是一個萬能碼。
驗證碼識別技術
例如可以通過Python-tesseract 來識別圖片驗證碼,Python-tesseract是光學字符識別Tesseract OCR引擎的
Python封裝類。能夠讀取任何常規的圖片文件(JPG, GIF ,PNG , TIFF等)。不過,目前市面上的驗證碼形式繁多,目前任何一種驗證碼識別技術,識別率都不是100% 。
記錄cookie
適用于UI自動化測試,且目前在大部應用的用戶名密碼不記錄在cookie 或 進行加密處理。
通過向瀏覽器中添加cookie 可以繞過登錄的驗證碼,這是比較有意思的一種解決方案。我們可以在用戶登錄之前,通過add_cookie()方法將用戶名密碼寫入瀏覽器cookie ,再次訪問系統登錄鏈接將自動登錄。例如下面的方式:
....#訪問xxxx網站driver.get("http://www.xxxx.cn/") #將用戶名密碼寫入瀏覽器cookie driver.add_cookie({'name':'Login_UserNumber','value':'username'}) driver.add_cookie({'name':'Login_Passwd','value':'password'}) #再次訪問xxxx網站,將會自動登錄 driver.get("http://www.xxxx.cn/")time.sleep(3)....driver.quit() |
使用cookie進行登錄最大的難點是如何獲得用戶名密碼的name ,如果找到不到name 的名字,就沒辦法向value 中輸用戶名、密碼信息。
可以通過get_cookies()方法來獲取登錄的所有的cookie信息,從而進行找到用戶名、密碼的name 對象的名字。
很多時候,我們
測試時,如果進行大數據量的并發測試時,單個電腦的CPU和內存可能無法承受,這個時候,我們需要進行一個分布式的測試,比如10000個并發,使用三臺電腦來進行并發,
Jmeter提供了這種功能,你可以很輕松的實現Jmeter的這種分布式測試
1 首先確何所有的電腦上都安裝Jmeter
2 在所有電腦上開起Jmeter,開啟命令是jmeter-server.bat,而不是以前的jmeter.bat
注意:你所要運行的不要開啟,那些用來負載的,才開啟服務器模式,這個模式沒有界面,只有控制臺的
3 在你所想要操作的電腦上,以jmeter.bat命令來啟動圖形化的jmeter界面
編輯jmeter.properties
remote_hosts=127.0.0.1,192.168.0.102 #remote_hosts=localhost:1099,localhost:2010
通過遠程啟動,便可驅動遠程開啟的jmeter同時進行并發測試,這樣便同時有幾個Jmeter進行并發測試了,這樣可以實現了分布式的開發.
1、childNodes引入空白節點問題:使用childElementCount或children
2、innerText: FF中不支持該屬性,使用textContent代替
3、變量名與某HTML對象id相同時,引用該變量只會取得id名與其相同的html對象(ie8-);聲明變量時前面一律加上var,盡量避免id名與變量名相同
4、為ele.style.property賦值時一律帶上單位:e.style.height= 34 + ‘px’
5、禁止選擇網頁內容:
6、訪問form中的元素:ff只支持document.formName.elements['elementName']的方式,ie下可以使用document.formName.item('name');統一使用elements的方式;凡是遇到集合類對象(NodeCollection、NodeList)一律使用collection['name']的方式
7、自定義html元素特性問題:IE下可以使用e.selfAttr = variable/e.selfAttr方式來設值和取值,FF中只能使用e.setAttribute(attr, value)/e.getAttribute('attr')方式
8、input元素的type特性問題:IE下該屬性是只讀的,FF中可以動態設置;一律不能修改,若需要修改則刪除原來元素,重新創建新元素
9、window.location.href問題:就瀏覽器可以通過這種方式來獲取當前頁面url;應當統一使用window.location來方位頁面url,如:location.hostname,location.port,location.pathname
10、在瀏覽器中打開新窗口問題:
View Code
11、body載入問題:FF中的body對象在body標簽為載入完全時即可訪問,IE下必須完全讀入后才執行
12、function、new function(){}、new Function('.....')三者的區別
13、FF中不支持e.parentElement方式方位父元素,只能使用e.parentNode方式
14、Table操作問題,IE中無法使用innerHTML方式對table和tr進行操作;一般方法是借助js類庫,將innerHLML轉化為dom節點,并插入到tbody下
15、IE下不支持使用e.setAttribute方式來整體設值style屬性問題:同時使用e.setAttribute('style', '.......')和e.style.cssText = ‘。。。。。。。’方式來設置
16、document.createElement('<div class="name"></div>')方式創建html元素在FF中不支持
17、iframe問題:
<iframe src="xxx.html" id="frameId" name="frameName" />
IE 中可以通過window.top.frmaeId或window.top.frameName方式來訪問farme;FF中只支持第二種方式;IE在iframe資源未加載完成時無法訪問iframe.contentWindow對象
18、url encoding 問題:encodeURIComponent適用于對url后的參數編碼、encodeURI:主要用于location對象跳轉時對整個url編碼
19、節點插入問題:IE:insertAdjacentElement(position,src);FF:insertBefore(src, ref)
20、IE9以下不能訪問html元素的構造器,如判斷元素是否為HTMLElement方法只能使用:e.nodeType === 1不能使用 e instanceof HTMLElement方式
客戶也經常提這樣的概念或者嘗試實踐。有些客戶可能只做易用性測試,有些客戶則關注探索性測試。還很少看到兩者都做得。這里簡單詮釋下兩者的相同和不同,如果有不同意的地方,敬請指正。
相同點
1. 易用性測試和探索性測試都是面向業務的測試。所謂面向業務的測試是區別于面向技術的測試,它更多關注用戶感受,邏輯是否合理,流程是否正確,功能是否有遺漏等。
2. 兩者屬于手工測試范疇。雖然有時候用戶也可以用工具輔助做探索性,但是兩者都屬于手工測試。依賴于測試人員的專業能力和分析能力
3. 任何人都可以參與的測試方式
不同點
易用性測試用一句話概況就是:軟件使用是否方便。具體來講它分:可理解性,可操作性,可
學習性,吸引性和依從性5個方面進行分析。易用性測試本身不是為了尋找系統還存在哪些
bug,而且在假設沒有bug的基礎上,對系統從人體工程學上進行測試。如果期間發現bug,那純當系統Bug處理。易用性一般只以建議的方式對發現的問題進行登記。易用性測試建議在項目初期界面設計階段引入,因為對于后期來說,系統的更改成本和風險過高。
對于易用性測試,本身不能絕對性,任何界面設計專家也不能完全代表用戶。因此需要更多的分析用戶群體的行為習慣,例如:老年人會習慣界面文字比較大,圖案比較傳統的方式。而年輕人會選擇頁面精細,功能豐富的系統。所以在進行易用性測試的過程中,從用戶的角度進行測試是根本。在有些產品用戶群體比較廣泛的情況下,往往需要做Alpha,Beta測試來進行統計。以獲取最合適的人群。
探索性測試比較流行的定義是:邊設計用例邊測試。
如果把腳本測試作為一張網的話,那么探索性測試就是在尋找能透過網洞的bug。
探索性測試從測試手法上多會采用顛倒次序,改變狀態,跟隨產生的數據等來發現系統可能存在的潛在問題。因此探索性測試更傾向于是功能性的測試。探索性測試一般發生在系統集成測試完成,可以在UAT之前或者同時。
探索性測試也可以是沒有界面的測試,這點跟易用性測試完全不同。對于沒有界面的測試,需要開發人員或者測試人員有很強的數據追蹤能力,了解數據在不同階段的變化過程和變化預期。
當然此外還有更多不同之處,這里拋磚引玉,希望讀者繼續發掘!
哈希表這個數據結構想必大多數人都不陌生,而且在很多地方都會利用到hash表來提高查找效率。在
Java的Object類中有一個方法:
public native int hashCode();
根據這個方法的聲明可知,該方法返回一個int類型的數值,并且是本地方法,因此在Object類中并沒有給出具體的實現。
為何Object類需要這樣一個方法?它有什么作用呢?今天我們就來具體探討一下hashCode方法。
一.hashCode方法的作用
對于包含容器類型的程序設計語言來說,基本上都會涉及到hashCode。在Java中也一樣,hashCode方法的主要作用是為了配合基于散列的集合一起正常運行,這樣的散列集合包括HashSet、HashMap以及HashTable。
為什么這么說呢?考慮一種情況,當向集合中插入對象時,如何判別在集合中是否已經存在該對象了?(注意:集合中不允許重復的元素存在)
也許大多數人都會想到調用equals方法來逐個進行比較,這個方法確實可行。但是如果集合中已經存在一萬條數據或者更多的數據,如果采用equals方法去逐一比較,效率必然是一個問題。此時hashCode方法的作用就體現出來了,當集合要添加新的對象時,先調用這個對象的hashCode方法,得到對應的hashcode值,實際上在HashMap的具體實現中會用一個table保存已經存進去的對象的hashcode值,如果table中沒有該hashcode值,它就可以直接存進去,不用再進行任何比較了;如果存在該hashcode值, 就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址,所以這里存在一個沖突解決的問題,這樣一來實際調用equals方法的次數就大大降低了,說通俗一點:Java中的hashCode方法就是根據一定的規則將與對象相關的信息(比如對象的存儲地址,對象的字段等)映射成一個數值,這個數值稱作為散列值。下面這段代碼是java.util.HashMap的中put方法的具體實現:
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; } |
put方法是用來向HashMap中添加新的元素,從put方法的具體實現可知,會先調用hashCode方法得到該元素的hashCode值,然后查看table中是否存在該hashCode值,如果存在則調用equals方法重新確定是否存在該元素,如果存在,則更新value值,否則將新的元素添加到HashMap中。從這里可以看出,hashCode方法的存在是為了減少equals方法的調用次數,從而提高程序效率。
有些朋友誤以為默認情況下,hashCode返回的就是對象的存儲地址,事實上這種看法是不全面的,確實有些JVM在實現時是直接返回對象的存儲地址,但是大多時候并不是這樣。
因此有人會說,可以直接根據hashcode值判斷兩個對象是否相等嗎?肯定是不可以的,因為不同的對象可能會生成相同的hashcode值。雖然不能根據hashcode值判斷兩個對象是否相等,但是可以直接根據hashcode值判斷兩個對象不等,如果兩個對象的hashcode值不等,則必定是兩個不同的對象。如果要判斷兩個對象是否真正相等,必須通過equals方法。
也就是說對于兩個對象,如果調用equals方法得到的結果為true,則兩個對象的hashcode值必定相等;
如果equals方法得到的結果為false,則兩個對象的hashcode值不一定不同;
如果兩個對象的hashcode值不等,則equals方法得到的結果必定為false;
如果兩個對象的hashcode值相等,則equals方法得到的結果未知。
前言
最近一直在研究
Android內核驅動開發的相關事宜,使用VMware虛擬機雖然可以更方便的開發,但是對于開發環境硬件的要求還是比較高的,若用于開發,效率太低了,所以考慮使用單獨PC去裝載
Linux環境進行開發,這里這里挑選的Ubuntu,因為
Google測試Android源代碼時使用的就是Ubuntu。因此如果從事Android底層驅動開發,強烈建議使用Ubuntu。本篇博客就Ubuntu系統使用U盤安裝到硬盤,進行詳細的講解,這里使用的Ubuntu鏡像版本是Ubuntu12.04。
本篇博客的主要內容:
制作啟動盤
安裝Ubuntu到硬盤
準備工作
安裝Ubuntu系統到硬盤中,有一些準備工作需要做:
2GB以上空間的U盤
Universal-USB-Installer(自啟動Linux U盤創建工具)
Ubuntu系統鏡像
2GB以上空間的U盤
如果需要把Ubuntu安裝在硬盤中,一般使用的方式是使用系統光盤或者U盤安裝,因為現在U盤的獲取比較便捷,基本上誰手邊都會有一個可用的U盤。這里需要一個不小于2GB的空白U盤,建議在使用前對U盤中的重要資料進行備份。
Universal-USB-Installer
Universal USB Installer(通用USB安裝程序)是一個自啟動Linux U盤創建工具,可從大量精選的Linux發行版中挑選一個安裝到您的U盤上。通用USB安裝程序使用方便,只需選擇自啟動Linux發行版的ISO文件,和連接的U盤,單擊“安裝”即可。其他功能包括,持續保存(如果可用的話),以FAT32格式格式化U盤(推薦)確保一個干凈的安裝。安裝完成后,就擁有了一個安裝了Linux版本的自啟動U盤。
Universal USB Installer網上下載的版本很多,本篇博客中介紹的版本號是1.9.5.2,在我的CSDN資料中提供了下載,方便直接下載。
Universal USB Installer:點擊下載。
Ubuntu系統鏡像
Ubuntu有三個版本,分別是桌面版(Desktop Edition),服務器版(Server Edition),上網本版(Netbook Remix),這里使用桌面版即可。Ubuntu每6個月發布一個新版本,而每個版本都有代號和版本號,其中有LTS是長期支持版,所以這里下載的也是LTS的。
到現在為止,Ubuntu最新的LTS版本是Ubuntu 14.04 LTS,可以直接從官方網站上下載:點擊下載。
但是最新版的肯定有諸多的問題,那么可以考慮下載歷史版本,我這里安裝的版本是Ubuntu 12.04 LTS。可以考慮從各種開源鏡像站下載。
在開源鏡像站中選擇相應的版本,找到desktop版本的下載ISO鏡像,可以發現有非常多的下載鏈接。其中i386表示32位
操作系統,amd64表示64位操作系統。
所以如果想安裝32位Ubuntu操作系統,可以下載ubuntu-12.04.4-desktop-i386.iso,如果想安裝64位Ubuntu操作系統,可以下載ubuntu-12.04.4-desktop-amd64.iso,點擊下載即可,大約七百多MB。
制作Ubuntu啟動盤
運行Universal USB Installer,點擊<I Agree>按鈕。然后按照提示選中需要安裝的Linux版本、鏡像文件、制作鏡像的U盤。如下圖:
最后點擊<Create>按鈕,接下來軟件會自動幫我們把鏡像文件寫入到U盤中,最后提示完成即可。
安裝Ubuntu到硬盤
制作好啟動盤之后,就可以正式開始安裝Ubuntu系統了。要想讓你的電腦從U盤啟動,有兩種方法,一種是在BIOS里修改啟動順序;另一種是開機時按某功能鍵(具體決定于機型,通常是F2),然后選擇從哪啟動,這里就不詳細講解了。
當進入U盤啟動之后,可以選擇預覽或者直接安裝。這里選擇直接安裝,如如下視圖,對于英語不太好的可以選擇中文,點擊<Install Ubuntu>安裝。之后的步驟可按照向導一步一步完成即可,安裝結束后會提示重新啟動電腦。
最近有個項目,需要用php操作mongoDb數據,所以了解下mongoDb為此整理了下,常見的操作......
1,連接MongoDB數據庫
$conn = new Mongo(); 其他鏈接方式 //$conn=new Mongo(); #連接本地主機,默認端口. //$conn=new Mongo(“172.21.15.69″); #連接遠程主機 //$conn=new Mongo(“xiaocai.loc:10086″); #連接指定端口遠程主機 //$conn=new Mongo(“xiaocai.loc”,array(“replicaSet”=>true)); #負載均衡 //$conn=new Mongo(“xiaocai.loc”,array(“persist”=>”t”)); #持久連接 //$conn=new Mongo(“mongodb://sa:123@localhost”); #帶用戶名密碼 //$conn=new Mongo(“mongodb://localhost:27017,localhost:27018″); #連接多個服務器 //$conn=new Mongo(“mongodb:///tmp/mongo-27017.sock”); #域套接字 //$conn=new Mongo(“mongodb://admin_miss:miss@localhost:27017/test”,array(‘persist’=>’p',”replicaSet”=>true)); #完整 |
$db=$conn->mydb; #選擇mydb數據庫
$collection=$db->myTable; #選擇集合(選擇’表’)
//$collection=$db->selectCollection(myTable); #第二種寫法
//$collection=$conn->mydb->myTable; #更簡潔的寫法
3,插入數據記錄
$array=array(‘column_name’=>’col’.rand(100,999),’column_exp’=>’xiaocai’); $result=$collection->insert($array); #簡單插入 echo “新記錄ID:”.$array['_id']; #MongoDB會返回一個記錄標識 var_dump($result); #返回:bool(true) //**向集合中安全插入數據,返回插入狀態(數組). **/ $array=array(‘column_name’=>’col’.rand(100,999),’column_exp’=>’xiaocai2′); $result=$collection->insert($array,true); #用于等待MongoDB完成操作,以便確定是否成功.(當有大量記錄插入時使用該參數會比較有用) echo “新記錄ID:”.$array['_id']; #MongoDB會返回一個記錄標識 var_dump($result); #返回:array(3) { ["err"]=> NULL ["n"]=> int(0) ["ok"]=> float(1) } |
4,更新數據記錄
//** 修改更新 傳統更新 **/ $where=array(‘column_name’=>’col123′); $newdata=array(‘column_exp’=>’GGGGGGG’,'column_fid’=>444); $result=$collection->update($where,array(‘$set’=>$newdata)); #$set:讓某節點等于給定值,類似的還有$pull $pullAll $pop $inc,在后面慢慢說明用法 /* * 結果: * 原數據 * {“_id”:ObjectId(“4d635ba2d549a02801000003″),”column_name”:”col123″,”column_exp”:”xiaocai”} * 被替換成了 * {“_id”:ObjectId(“4d635ba2d549a02801000003″),”column_name”:”col123″,”column_exp”:”GGGGGGG”,”column_fid”:444} */ //** 替換更新 ,覆蓋原記錄**/ $where=array(‘column_name’=>’col709′); $newdata=array(‘column_exp’=>’HHHHHHHHH’,'column_fid’=>123); $result=$collection->update($where,$newdata); /* * 結果: * 原數據 * {“_id”:ObjectId(“4d635ba2d549a02801000003″),”column_name”:”col709″,”column_exp”:”xiaocai”} * 被替換成了 * {“_id”:ObjectId(“4d635ba2d549a02801000003″),”column_exp”:”HHHHHHHHH”,”column_fid”:123} */ //** 批量更新 **/ $where=array(‘column_name’=>’col’); $newdata=array(‘column_exp’=>’multiple’,’91u’=>684435); $result=$collection->update($where,array(‘$set’=>$newdata),array(‘multiple’=>true)); /** * 所有’column_name’='col’都被修改 */ //** 自動累加 **/ $where=array(’91u’=>684435); $newdata=array(‘column_exp’=>’edit’); $result=$collection->update($where,array(‘$set’=>$newdata,’$inc’=>array(’91u’=>-5))); /** * 更新91u=684435的數據,并且91u自減5 */ |
PHP操作MongoDB 數據庫總結記錄
5,刪除記錄操作
//** 刪除節點 **/ $where=array(‘column_name’=>’col685′); $result=$collection->update($where,array(‘$unset’=>’column_exp’)); /** * 刪除節點column_exp */ /* * * * 完整格式:update(array $criteria, array $newobj [, array $options = array() ] ) * 注意:1.注意區分替換更新與修改更新 * 2.注意區分數據類型如 array(’91u’=>’684435′)與array(’91u’=>684435) * * */ //** 清空表 **/ $collection->remove(); #清空集合 //** 刪除指定MongoId **/ $id = new MongoId(“4d638ea1d549a02801000011″); $collection->remove(array(‘_id’=>(object)$id)); |
6,查詢數據記錄
//** 統計表記錄數 **/ echo ‘count:’.$collection->count().”<br>”; #全部 echo ‘count:’.$collection->count(array(‘type’=>’user’)).”<br>”; #統計‘type’ 為 user 的記錄 echo ‘count:’.$collection->count(array(‘age’=>array(‘$gt’=>50,’$lte’=>74))).”<br>”; #統計大于50小于等于74 echo ‘count:’.$collection->find()->limit(5)->skip(0)->count(true).”<br>”; #獲得實際返回的結果數 /** * 注:$gt為大于、$gte為大于等于、$lt為小于、$lte為小于等于、$ne為不等于、$exists不存在 */ //** 獲取表中所有記錄 **/ $cursor = $collection->find()->snapshot(); foreach ($cursor as $id => $value) { echo “$id: “; var_dump($value); echo “<br>”; } /** * 注意: * 在我們做了find()操作,獲得$cursor游標之后,這個游標還是動態的. * 換句話說,在我find()之后,到我的游標循環完成這段時間,如果再有符合條件的記錄被插入到collection,那么這些記錄也會被$cursor 獲得. * 如果你想在獲得$cursor之后的結果集不變化,需要這樣做: * $cursor = $collection->find(); */ //** 查詢一條數據 **/ $cursor = $collection->findOne(); /** * 注意:findOne()獲得結果集后不能使用snapshot(),fields()等函數; */ //** 設置顯示字段 age,type 列不顯示 **/ $cursor = $collection->find()->fields(array(“age”=>false,”type”=>false)); $cursor = $collection->find()->fields(array(“user”=>true)); //只顯示user 列 /** * 我這樣寫會出錯:$cursor->fields(array(“age”=>true,”type”=>false)); */ //** 設置條件 (存在type,age節點) and age!=0 and age<50 **/ $where=array(‘type’=>array(‘$exists’=>true),’age’=>array(‘$ne’=>0,’$lt’=>50,’$exists’=>true)); $cursor = $collection->find($where); //** 分頁獲取結果集 **/ $cursor = $collection->find()->limit(5)->skip(0); //** 排序 **/ $cursor = $collection->find()->sort(array(‘age’=>-1,’type’=>1)); ##1表示降序 -1表示升序,參數的先后影響排序順序 //** 索引 **/ $collection->ensureIndex(array(‘age’ => 1,’type’=>-1)); #1表示降序 -1表示升序 $collection->ensureIndex(array(‘age’ => 1,’type’=>-1),array(‘background’=>true)); #索引的創建放在后臺運行(默認是同步運行) $collection->ensureIndex(array(‘age’ => 1,’type’=>-1),array(‘unique’=>true)); #該索引是唯一的 /** * ensureIndex (array(),array(‘name’=>’索引名稱’,'background’=true,’unique’=true)) * 詳見:http://www.php.net/manual/en/mongocollection.ensureindex.php */ //** 取得查詢結果 **/ $cursor = $collection->find(); $array=array(); foreach ($cursor as $id => $value) { $array[]=$value; } |
7.關閉鏈接
$conn->close(); #關閉連接
8,常見函數使用
a.$inc 如果記錄的該節點存在,讓該節點的數值加N;如果該節點不存在,讓該節點值等于N
設結構記錄結構為 array(’a’=>1,’b’=>’t’),想讓a加5,那么:
$coll->update(array(’b’=>’t’),array(’$inc’=>array(’a’=>5)))
b.$set 讓某節點等于給定值
設結構記錄結構為 array(’a’=>1,’b’=>’t’),b為加f,那么:
$coll->update(array(’a’=>1),array(’$set’=>array(’b’=>’f’)))
c.$unset 刪除某節點
設記錄結構為 array(’a’=>1,’b’=>’t’),想刪除b節點,那么:
$coll->update(array(’a’=>1),array(’$unset’=>’b’))
d.$push 如果對應節點是個數組,就附加一個新的值上去;不存在,就創建這個數組,并附加一個值在這個數組上;如果該節點不是數組,返回錯誤。
設記錄結構為array(’a’=>array(0=>’haha’),’b’=& gt;1),想附加新數據到節點a,那么:
$coll->update(array(’b’=>1),array(’$push’=>array(’a’=>’wow’)))
這樣,該記錄就會成為:array(’a’=>array(0=>’haha’,1=>’wow’),’b’=>1)
e.$pushAll 與$push類似,只是會一次附加多個數值到某節點
f.$addToSet 如果該階段的數組中沒有某值,就添加之
設記錄結構為array(’a’=>array(0=& gt;’haha’),’b’=>1),如果想附加新的數據到該節點a,那么:
$coll->update(array(’b’=>1),array(’$addToSet’=>array(’a’=>’wow’)))
如果在a節點中已經有了wow,那么就不會再添加新的,如果沒有,就會為該節點添加新的item——wow。
g.$pop 設該記錄為array(’a’=>array(0=>’haha’,1=>’wow’),’b’=>1)
刪除某數組節點的最后一個元素:
$coll->update(array(’b’=>1),array(’$pop=>array(’a’=>1)))
刪除某數組階段的第一個元素
$coll->update(array(’b’=>1),array(’$pop=>array(’a’=>-1)))
h.$pull 如果該節點是個數組,那么刪除其值為value的子項,如果不是數組,會返回一個錯誤。
設該記錄為 array(’a’=>array(0=>’haha’,1=>’wow’),’b’=>1),想要刪除a中value為 haha的子項:
$coll->update(array(’b’=>1),array(’$pull=>array(’a’=>’haha’)))
結果為: array(’a’=>array(0=>’wow’),’b’=>1)
i.$pullAll 與$pull類似,只是可以刪除一組符合條件的記錄。
1、什么是存儲過程。存儲過程是
數據庫服務器端的一段程序,它有兩種類型。一種類似于SELECT查詢,用于檢索數據,檢索到的數據能夠以數據集的形式返回給客戶。另一種類似于INSERT或DELETE查詢,它不返回數據,只是執行一個動作。有的服務器允許同一個存儲過程既可以返回數據又可以執行動作。
2、什么時候需要用存儲過程
如果服務器定義了存儲過程,應當根據需要決定是否要用存儲過程。存儲過程通常是一些經常要執行的任務,這些任務往往是針對大量的記錄而進行的。在服務器上執行存儲過程,可以改善應用程序的性能。這是因為:
.服務器往往具有強大的計算能力和速度。
.避免把大量的數據下載到客戶端,減少網絡上的傳輸量。
例如,假設一個應用程序需要計算一個數據,這個數據需要涉及到許多記錄。如果不使用存儲過程的話,把這些數據下載到客戶端,導致網絡上的流量劇增。
不僅如此,客戶端可能是一臺老掉牙的計算機,它的運算速度很慢。而改用存儲過程后,服務器會很快地把數據計算出來,并且只需傳遞一個數據給客戶端,其效率之高是非常明顯的。
3、存儲過程的參數
要執行服務器上的存儲過程,往往要傳遞一些參數。這些參數分為四種類型:
第一種稱為輸入參數,由客戶程序向存儲過程傳遞值。
第二種稱為輸出參數,由存儲過程向客戶程序返回結果。
第三種稱為輸入/輸出參數,既可以由客戶程序向存儲過程傳遞值,也可以由存儲過程向客戶程序返回結果。
第四種稱為狀態參數,由存儲過程向客戶程序返回錯誤信息。
要說明的是,并不是所有的服務器都支持上述四種類型的參數,例如,InterBase就不支持狀態參數。
4、oracle 存儲過程的基本語法
1.基本結構
CREATE OR REPLACEPROCEDURE 存儲過程名字
(
參數1 IN NUMBER,
參數2 IN NUMBER
) IS
變量1 INTEGER :=0;
變量2 DATE;
BEGIN
END 存儲過程名字
2.SELECT INTO STATEMENT
將select查詢的結果存入到變量中,可以同時將多個列存儲多個變量中,必須有一條
記錄,否則拋出異常(如果沒有記錄拋出NO_DATA_FOUND)
例子:
BEGIN
SELECT col1,col2 into 變量1,變量2 FROM typestruct where xxx;
EXCEPTION
WHEN NO_DATA_FOUND THEN
xxxx;
END;
一:無返回值的存儲過程
存儲過程為:
CREATE OR REPLACE PROCEDURE TESTA(PARA1 IN VARCHAR2,PARA2 IN VARCHAR2) AS
BEGIN
INSERT INTO HYQ.B_ID (I_ID,I_NAME) S (PARA1, PARA2);
END TESTA;
然后呢,在java里調用時就用下面的代碼:
package com.hyq.src; import java.sql.*; import java.sql.ResultSet; public class TestProcedureOne { public TestProcedureOne() { } public static void main(String[] args ){ String driver = "oracle.jdbc.driver.OracleDriver"; String strUrl = "jdbc:oracle:thin:@127.0.0.1:1521: hyq "; Statement stmt = null; ResultSet rs = null; Connection conn = null; CallableStatement cstmt = null; try { Class.forName(driver); conn = DriverManager.getConnection(strUrl, " hyq ", " hyq "); CallableStatement proc = null; proc = conn.prepareCall("{ call HYQ.TESTA(?,?) }"); proc.setString(1, "100"); proc.setString(2, "TestOne"); proc.execute(); } catch (SQLException ex2) { ex2.printStackTrace(); } catch (Exception ex2) { ex2.printStackTrace(); } finally{ try { if(rs != null){ rs.close(); if(stmt!=null){ stmt.close(); } if(conn!=null){ conn.close(); } } } catch (SQLException ex1) { } } } } |
當然了,這就先要求要建張表TESTTB,里面兩個字段(I_ID,I_NAME)。
二:有返回值的存儲過程(非列表)
存儲過程為:
CREATE OR REPLACE PROCEDURE TESTB(PARA1 IN VARCHAR2,PARA2 OUT VARCHAR2) AS
BEGIN
SELECT INTO PARA2 FROM TESTTB WHERE I_ID= PARA1;
END TESTB;
在java里調用時就用下面的代碼:
package com.hyq.src; public class TestProcedureTWO { public TestProcedureTWO() { } public static void main(String[] args ){ String driver = "oracle.jdbc.driver.OracleDriver"; String strUrl = "jdbc:oracle:thin:@127.0.0.1:1521:hyq"; Statement stmt = null; ResultSet rs = null; Connection conn = null; try { Class.forName(driver); conn = DriverManager.getConnection(strUrl, " hyq ", " hyq "); CallableStatement proc = null; proc = conn.prepareCall("{ call HYQ.TESTB(?,?) }"); proc.setString(1, "100"); proc.registerOutParameter(2, Types.VARCHAR); proc.execute(); String testPrint = proc.getString(2); System.out.println("=testPrint=is="+testPrint); } catch (SQLException ex2) { ex2.printStackTrace(); } catch (Exception ex2) { ex2.printStackTrace(); } finally{ try { if(rs != null){ rs.close(); if(stmt!=null){ stmt.close(); } if(conn!=null){ conn.close(); } } } catch (SQLException ex1) { } } } } } |
注意,這里的proc.getString(2)中的數值2并非任意的,而是和存儲過程中的out列對應的,如果out是在第一個位置,那就是proc.getString(1),如果是第三個位置,就是proc.getString(3),當然也可以同時有多個返回值,那就是再多加幾個out參數了。
三:返回列表
由于oracle存儲過程沒有返回值,它的所有返回值都是通過out參數來替代的,列表同樣也不例外,但由于是集合,所以不能用一般的參數,必須要用pagkage了.所以要分兩部分,
1, 建一個程序包。如下:
CREATE OR REPLACE PACKAGE TESTPACKAGE AS
TYPE Test_CURSOR IS REF CURSOR;
end TESTPACKAGE;
2,建立存儲過程,存儲過程為:
CREATE OR REPLACE PROCEDURE TESTC(p_CURSOR out TESTPACKAGE.Test_CURSOR) IS
BEGIN
OPEN p_CURSOR FOR SELECT * FROM HYQ.TESTTB;
END TESTC;
可以看到,它是把游標(可以理解為一個指針),作為一個out 參數來返回值的。
在java里調用時就用下面的代碼:
package com.hyq.src; import java.sql.*; import java.io.OutputStream; import java.io.Writer; import java.sql.PreparedStatement; import java.sql.ResultSet; import oracle.jdbc.driver.*; public class TestProcedureTHREE { public TestProcedureTHREE() { } public static void main(String[] args ){ String driver = "oracle.jdbc.driver.OracleDriver"; String strUrl = "jdbc:oracle:thin:@127.0.0.1:1521:hyq"; Statement stmt = null; ResultSet rs = null; Connection conn = null; try { Class.forName(driver); conn = DriverManager.getConnection(strUrl, "hyq", "hyq"); CallableStatement proc = null; proc = conn.prepareCall("{ call hyq.testc(?) }"); proc.registerOutParameter(1,oracle.jdbc.OracleTypes.CURSOR); proc.execute(); rs = (ResultSet)proc.getObject(1); while(rs.next()) { System.out.println("<tr><td>" + rs.getString(1) + "</td><td>"+rs.getString(2)+"</td></tr>"); } } catch (SQLException ex2) { ex2.printStackTrace(); } catch (Exception ex2) { ex2.printStackTrace(); } finally{ try { if(rs != null){ rs.close(); if(stmt!=null){ stmt.close(); } if(conn!=null){ conn.close(); } } } catch (SQLException ex1) { } } } } |
在這里要注意,在執行前一定要先把oracle的驅動包放到class路徑里,否則會報錯。
Hibernate中對動態查詢參數綁定提供了豐富的支持,那么什么是查詢參數動態綁定呢?其實如果我們熟悉傳統JDBC編程的話,我們就不難理解查詢參數動態綁定,如下代碼傳統JDBC的參數綁定:
PrepareStatement pre=connection.prepare(“select * from User where user.name=?”);
pre.setString(1,”zhaoxin”);
ResultSet rs=pre.executeQuery();
在Hibernate中也提供了類似這種的查詢參數綁定功能,而且在Hibernate中對這個功能還提供了比傳統JDBC操作豐富的多的特性,在Hibernate中共存在4種參數綁定的方式,下面我們將分別介紹:
A、 按參數名稱綁定:
在HQL語句中定義命名參數要用”:”開頭,形式如下:
Query query=session.createQuery(“from User user where user.name=:customername and user:customerage=:age ”);
query.setString(“customername”,name);
query.setInteger(“customerage”,age);
上面代碼中用:customername和:customerage分別定義了命名參數customername和customerage,然后用Query接口的setXXX()方法設定名參數值,setXXX()方法包含兩個參數,分別是命名參數名稱和命名參數實際值。
B、 按參數位置邦定:
在HQL查詢語句中用”?”來定義參數位置,形式如下:
Query query=session.createQuery(“from User user where user.name=? and user.age =? ”);
query.setString(0,name);
query.setInteger(1,age);
同樣使用setXXX()方法設定綁定參數,只不過這時setXXX()方法的第一個參數代表邦定參數在HQL語句中出現的位置編號(由0開始編號),第二個參數仍然代表參數實際值。
注:在實際開發中,提倡使用按名稱邦定命名參數,因為這不但可以提供非常好的程序可讀性,而且也提高了程序的易維護性,因為當查詢參數的位置發生改變時,按名稱邦定名參數的方式中是不需要調整程序代碼的。
C、 setParameter()方法:
在Hibernate的HQL查詢中可以通過setParameter()方法邦定任意類型的參數,如下代碼:
String hql=”from User user where user.name=:customername ”;
Query query=session.createQuery(hql);
query.setParameter(“customername”,name,Hibernate.STRING);
如上面代碼所示,setParameter()方法包含三個參數,分別是命名參數名稱,命名參數實際值,以及命名參數映射類型。對于某些參數類型setParameter()方法可以更具參數值的
Java類型,猜測出對應的映射類型,因此這時不需要顯示寫出映射類型,像上面的例子,可以直接這樣寫:
query.setParameter(“customername”,name);但是對于一些類型就必須寫明映射類型,比如java.util.Date類型,因為它會對應Hibernate的多種映射類型,比如Hibernate.DATA或者Hibernate.TIMESTAMP。
D、 setProperties()方法:
在Hibernate中可以使用setProperties()方法,將命名參數與一個對象的屬性值綁定在一起,如下程序代碼:
Customer customer=new Customer();
customer.setName(“pansl”);
customer.setAge(80);
Query query=session.createQuery(“from Customer c where c.name=:name and c.age=:age ”);
query.setProperties(customer);
setProperties()方法會自動將customer對象實例的屬性值匹配到命名參數上,但是要求命名參數名稱必須要與實體對象相應的屬性同名。
這里還有一個特殊的setEntity()方法,它會把命名參數與一個持久化對象相關聯,如下面代碼所示:
Customer customer=(Customer)session.load(Customer.class,”1”);
Query query=session.createQuery(“from Order order where order.customer=:customer ”);
query. setEntity(“customer”,customer);
List list=query.list();
上面的代碼會生成類似如下的SQL語句:
Select * from order where customer_ID=’1’;
以下實現復制步驟(以快照復制為例)
1.建立一個 WINDOWS 用戶,設置為管理員權限,并設置密碼,作為發布快照文件的有效訪問用戶。
2.在SQL SERVER下實現發布服務器和訂閱服務器的通信正常(即可以互訪)。打開1433端口,在防火墻中設特例
3.在發布服務器上建立一個共享目錄,作為發布快照文件的存放目錄。例如:在D盤根目錄下建文件夾名為SqlCopy
4.設置SQL 代理(發布服務器和訂閱服務器均設置)本篇
文章發表于www.xker.com(小新技術網)
打開服務(控制面板---管理工具---服務)
---右擊SQLSERVER AGENT---屬性---登錄---選擇“此帳戶“
---輸入或選擇第一步中創建的WINDOWS 用戶
---“密碼“中輸入該用戶密碼
5.設置SQL SERVER 身份驗證,解決連接時的權限問題(發布、訂閱服務器均設置)
步驟為:對象資源管理器----右擊SQL實例-----屬性----安全性----服務器身份驗證------選“SQL Server和WINDOWS“,然后點確定
6.開啟SQL Server 2005的網絡協議TCP/IP和管道命名協議并重啟網絡服務。
7.在SQL Server中創建步驟1中對應的系統用戶登陸名,作為發布
數據庫的擁有者(設置為dbo_owner和public)。
8.以系統超級用戶sa登陸SQL Server建立數據庫和表。
9.發布服務器和訂閱服務器互相注冊
步驟如下:視圖----單擊以注冊服務器----右鍵數據庫引擎----新建服務器注冊-----填寫要注冊的遠程服務器名稱------身份驗證選“SQL Server驗證“-----用戶名(sa) 密碼------創建組(也可不建)-----完成。
10.對于只能用IP,不能用計算機名的,為其注冊服務器別名
二、開始:
發布服務器配置(在發布服務器上配置發布和訂閱)
1. 選擇 復制 節點
2. 右鍵本地發布 ----下一步---------系統彈出對話框看提示----直到“指定快照文件夾“
----在“快照文件夾“中輸入準備工作中創建的目錄(指向步驟3所建的共享文件夾)------選擇發布數據庫-------選擇發布類型-------選擇訂閱服務器類型-------選擇要發布的對象------設置快照代理-------填寫發布名稱。本篇文章發表于www.xker.com(小新技術網)
3. 右鍵本地訂閱--------選擇發布服務器-------選擇訂閱方式(如果是在服務器方訂閱的話選擇推送訂閱反之
選擇請求訂閱)-------填加訂閱服務器--------選擇代理計劃(一般選擇連續運行)---------其余選擇默認項。
至此, SQL SERVER 2005 同步復制就完成了。使用復制技術,用戶可以將一份客戶端的數據發布到多臺服務器上,從而使不同的服務器用戶都可以在權限的許可的范圍內共享這份數據。復制技術可以確保分布在不同地點的數據自動同步更新,從而保證數據的一致性,就無需編程實現客戶端和服務器端數據同步了!大大提高了工作效率!SQL Server 2000訂閱與發布的具體操作
同步過程
一、準備工作,如果完成則可跳過。
1、內網DB服務器作為發布服務器,外網DB服務器作為訂閱服務器。
發布服務器和訂閱服務器上分別創建Windows用戶jl,密碼jl,隸屬于administrators,注意要保持一致。
2、發布服務器上創建一個共享目錄,作為發布快照文件的存放目錄。例如:在D盤根目錄下建文件夾名為SqlCopy,設置用戶jl,權限為完全控制。
3、確定發布服務器和訂閱服務器的數據庫autoweb保持一致。
4、在發布服務器和訂閱服務器的SQL Server中創建用戶登陸名jl,作為發布數據庫autoweb的擁有者(設置為dbo_owner和public)。用戶名和密碼都一致。
5、打開服務(控制面板---管理工具---服務)
---右擊SQLSERVER AGENT---屬性---登錄---選擇“此帳戶”
---輸入或選擇第一步中創建的WINDOWS 用戶jl,
---“密碼“中輸入該用戶密碼jl
6、開啟SQL Server 2000的網絡協議TCP/IP和管道命名協議并重啟網絡服務。
7、設置SQL SERVER 身份驗證,解決連接時的權限問題(發布、訂閱服務器均設置)
步驟為:對象資源管理器----右擊SQL實例-----屬性----安全性----服務器身份驗證------選“SQL Server和WINDOWS“,然后點確定。
8、發布服務器和訂閱服務器互相注冊
步驟如下:視圖----單擊以注冊服務器----右鍵數據庫引擎----新建服務器注冊-----填寫要注冊的遠程服務器名稱------身份驗證選“SQL Server驗證“-----用戶名(sa) 密碼------創建組(也可不建)-----完成。對于只能用IP,不能用計算機名的,為其注冊服務器別名
二、發布和訂閱
如下工作都在發布服務器上配置,包括發布和訂閱。
快照發布和訂閱
1、 選擇 復制 節點,右鍵本地發布 ----下一步---------系統彈出對話框看提示----直到“指定快照文件夾”----在“快照文件夾“中輸入準備工作中創建的目錄(指向步驟3所建的共享文件夾)------選擇發布數據庫-------選擇發布類型
下一步―――選擇要發布的數據庫autoweb中的表,將b(B)開頭的表去掉,V開頭的表去掉,c_開頭的表去掉,t_開頭的表去掉,剩下的表作為快照發布到訂閱服務器上(單向傳輸)
根據情況決定執行發布的間隔時間,如圖每天每20分鐘執行一次。
下一步快照代理安全性,設置如圖,連接到發布服務器用戶jl,密碼jl.
-------填寫發布名稱。
2、 選擇 復制 節點,右鍵本地訂閱,選擇發布服務器-------選擇訂閱方式(選擇推送訂閱))-------填加訂閱服務器--------選擇代理計劃(一般選擇連續運行)---------其余選擇默認項。
至此完成快照發布和訂閱。
合并發布和訂閱
1、選擇如下三個表作為合并發布的對象,用于雙向通訊
根據情況決定執行發布的間隔時間,如圖每天每20分鐘執行一次。
2、 選擇 復制 節點,右鍵本地訂閱,選擇發布服務器-------選擇訂閱方式(選擇推送訂閱))-------填加訂閱服務器--------選擇代理計劃(一般選擇連續運行)---------其余選擇默認項。
至此完成合并發布和訂閱
-------------------------------------------------------------------
主要是要注意權限的問題,一般做發布/訂閱,建議你做如下準備工作:
1.發布服務器,訂閱服務器都創建一個同名的windows用戶,并設置相同的密碼,做為發布快照文件夾的有效訪問用戶
我的電腦
--控制面板
--管理工具
--計算機管理
--用戶和組
--右鍵用戶
--新建用戶
--建立一個隸屬于administrator組的登陸windows的用戶
2.在發布服務器上,新建一個共享目錄,做為發布的快照文件的存放目錄,操作:
我的電腦--D: 新建一個目錄,名為: PUB
--右鍵這個新建的目錄
--屬性--共享
--選擇"共享該文件夾"
--通過"權限"按紐來設置具體的用戶權限,保證第一步中創建的用戶具有對該文件夾的所有權限
--確定
3.設置SQL代理(SQLSERVERAGENT)服務的啟動用戶(發布/訂閱服務器均做此設置)
開始--程序--管理工具--服務
--右鍵SQLSERVERAGENT
--屬性--登陸--選擇"此賬戶"
--輸入或者選擇第一步中創建的windows登錄用戶名
--"密碼"中輸入該用戶的密碼
4.設置SQL Server身份驗證模式,解決連接時的權限問題(發布/訂閱服務器均做此設置)
企業管理器
--右鍵SQL實例--屬性
--安全性--身份驗證
--選擇"SQL Server 和 Windows"
--確定
5.在發布服務器和訂閱服務器上互相注冊
企業管理器
--右鍵SQL Server組
--新建SQL Server注冊...
--下一步--可用的服務器中,輸入你要注冊的遠程服務器名--添加
--下一步--連接使用,選擇第二個"SQL Server身份驗證"
--下一步--輸入用戶名和密碼
--下一步--選擇SQL Server組,也可以創建一個新組
--下一步--完成
6.對于只能用IP,不能用計算機名的,為其注冊服務器別名
(在連接端配置,比如,在訂閱服務器上配置的話,服務器名稱中輸入的是發布服務器的IP)
開始--程序--Microsoft SQL Server--客戶端網絡實用工具
--別名--添加
--網絡庫選擇"tcp/ip"--服務器別名輸入SQL服務器名
--連接參數--服務器名稱中輸入SQL服務器ip地址
--如果你修改了SQL的端口,取消選擇"動態決定端口",并輸入對應的端口號