一、Berkeley DB Java Edition簡介
Berkeley DB JE is a general-purpose, transactionally protected, embedded database written in 100% Java (JE makes no JNI calls). As such, it offers the Java developer safe and efficient in-process storage and management of arbitrary data.
Berkeley DB的特點: 數(shù)據(jù)存儲能力很強(Large database support)
支持多線程(Multiple thread and process support)
數(shù)據(jù)庫的記錄很簡單,只是簡單的鍵值對(Database records)
提供事務(wù)支持(Transactions)
提供索引支持(Indexes)
提供內(nèi)存緩存的支持(In-memory cache)
按照連續(xù)的記錄文件存儲數(shù)據(jù)(Log files)
后臺線程的支持(Background threads)
數(shù)據(jù)庫環(huán)境封裝了一個或多個數(shù)據(jù)庫(Database environments)
支持?jǐn)?shù)據(jù)庫的備份和恢復(fù)(Backup and restore)
下載Berkeley DB Java Edition:可以從
http://www.sleepycat.com/download/index.shtml網(wǎng)址下載
安裝Berkeley DB Java Edition:將下載的文件解壓后放到JE_HOME下面。
使用Berkeley DB,應(yīng)用程序使用時,需要配置je.jar包,要將JE_HOME/lib/je.jar 加到你的應(yīng)用程序中
Berkeley DB Exception主要有三個異常:DatabaseNotFoundException,DeadlockException,RunRecoveryException
二、Berkeley DB Java Edition的使用及舉例說明
根據(jù)Berkeley DB的文檔介紹,在這里簡單的介紹一下用Berkeley DB開發(fā)的步驟:
構(gòu)建數(shù)據(jù)庫的開發(fā)環(huán)境
用Berkeley DB Java Edition開發(fā)數(shù)據(jù)庫時,需要構(gòu)建數(shù)據(jù)庫的開發(fā)環(huán)境。Berkeley DB主要是在這個環(huán)境下工作的,并且處理數(shù)據(jù)庫也是在這個環(huán)境下工作的。 import com.sleepycat.je.Environment;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.EnvironmentConfig;
import java.io.File;
......
Environment myDbEnvironment = null;
try {
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setAllowCreate(true);
/**這里的"/export/dbEnv"就是指定的數(shù)據(jù)庫開發(fā)環(huán)境的目錄*/
myDbEnvironment = new Environment(new File("/export/dbEnv"), envConfig);
} catch (DatabaseException dbe) {
// Exception handling goes here
}
在這里對環(huán)境的屬性可以進(jìn)行設(shè)置,當(dāng)然也可以通過配置文件je.properties來得到。
像上例中的"envConfig.setAllowCreate(true);"這個設(shè)置就是說,當(dāng)設(shè)置為true時,說明若沒有數(shù)據(jù)庫的環(huán)境時,可以打開。否則就不能打開。其它的屬性,可以查看有關(guān)Berkeley DB Java Edition文檔。
程序運行完了,一般要關(guān)閉數(shù)據(jù)庫的環(huán)境的,這就需要調(diào)用myDbEnvironment.close()方法來執(zhí)行,釋放系統(tǒng)資源。
構(gòu)建數(shù)據(jù)庫
數(shù)據(jù)庫的構(gòu)造是在數(shù)據(jù)庫環(huán)境的基礎(chǔ)上構(gòu)建的,這里還需要數(shù)據(jù)庫屬性的配置,不同的數(shù)據(jù)庫可以配置不同的屬性設(shè)置。
例: import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import java.io.File;
......
Environment myDbEnvironment = null;
Database myDatabase = null;
......
try {
// Open the environment. Create it if it does not already exist.
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setAllowCreate(true);
myDbEnvironment = new Environment(new File("/export/dbEnv"), envConfig);
// Open the database. Create it if it does not already exist.
DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setAllowCreate(true);
myDatabase = myDbEnvironment.openDatabase(null, "sampleDatabase", dbConfig);
}
catch (DatabaseException dbe) { // Exception handling goes here}
從這里的例子可以看出數(shù)據(jù)庫myDatabase需要DatabaseConfig的配置,一般數(shù)據(jù)庫的屬性的配置需要它在這里的配置,然后數(shù)據(jù)庫myDatabase才可以得到。如:myDatabase = myDbEnvironment.openDatabase(null,"sampleDatabase",dbConfig);
這里的“dbConfig.setAllowCreate(true);”就是設(shè)置數(shù)據(jù)的是否可以創(chuàng)建的屬性,這里是可以創(chuàng)建的。有關(guān)數(shù)據(jù)庫配置的其它屬性,可以查看有關(guān)的文檔。
釋放數(shù)據(jù)庫資源一般用Database.close()來操作。
使用數(shù)據(jù)庫的字段
Berkeley DB中的記錄包括兩個字段,就是鍵和值,并且這些鍵和值都必須是com.sleepycat.je.DatabaseEntry類的實例。 import com.sleepycat.je.DatabaseEntry;
......
String aKey = "key";
String aData = "data";
try {
DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
DatabaseEntry theData = new DatabaseEntry(aData.getBytes("UTF-8"));
} catch (Exception e) { // Exception handling goes here}
在這里可以看到,我們用的是UTF-8的編碼,其實,也可以使用其它的編碼。對于存儲和得到數(shù)據(jù)都不會產(chǎn)生任何影響。
當(dāng)?shù)玫綇臄?shù)據(jù)庫中得到數(shù)據(jù)時,因為它都是按照字節(jié)存儲的,所以,我們?nèi)〕鰯?shù)據(jù)時,需要轉(zhuǎn)換一下。 import com.sleepycat.je.DatabaseEntry;
......
byte[] myKey = theKey.getData();
byte[] myData = theData.getData();
String key = new String(myKey);
String data = new String(myData);
......
這樣就可以按照J(rèn)ava中的String類,來取出數(shù)據(jù)了。
對數(shù)據(jù)庫的各種操作具有了數(shù)據(jù)庫對象之后,就可以根據(jù)這個數(shù)據(jù)庫對象來執(zhí)行具體的數(shù)據(jù)庫操作,包括插入,檢索,刪除的操作。
在Berkeley DB中默認(rèn)是不支持重復(fù)記錄的,若有重復(fù)的記錄集的話,也可以使用游標(biāo)來完成操作。 從數(shù)據(jù)庫中插入數(shù)據(jù):這里提供了三種方法,put(),putNoOverwrite(),putNoDupData(),它們的主要區(qū)別還是在于插入的記錄是否有重復(fù),根據(jù)重復(fù)記錄來判斷這些操作,多數(shù)情況下,使用put()方法來操作插入。 import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Database;
.....
String aKey = "myFirstKey";
String aData = "myFirstData";
try {
DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
DatabaseEntry theData = new DatabaseEntry(aData.getBytes("UTF-8"));
myDatabase.put(null, theKey, theData);
} catch (Exception e) {
// Exception handling goes here
}
從數(shù)據(jù)庫中取得數(shù)據(jù),一般使用get()方法得到指定關(guān)鍵字的記錄,getSearchBoth()這個方法是根據(jù)所匹配的關(guān)鍵字來得到所需要的記錄。 import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Database;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
.....
// Environment and database opens omitted for clarity.
// Environment and database may be opened read-only.
String aKey = "myFirstKey";
try {
// Create a pair of DatabaseEntry objects. theKey
// is used to perform the search. theData is used
// to store the data returned by the get() operation.
DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
DatabaseEntry theData = new DatabaseEntry();
// Perform the get.
if (myDatabase.get(null, theKey, theData, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
// Recreate the data String.
byte[] retData = theData.getData();
String foundData = new String(retData);
System.out.println("For key: '" + aKey + "' found data: '" + foundData + "'.");
} else {
System.out.println("No record found for key '" + aKey + "'.");
}
} catch (Exception e) { // Exception handling goes here}
刪除數(shù)據(jù)庫中的記錄,一般是用Database.delete()方法,若數(shù)據(jù)庫中存在重復(fù)的記錄,要刪除掉具有重復(fù)的關(guān)鍵字的記錄的話,就會將所有具有該關(guān)鍵字的記錄都刪除掉了。也就是刪除掉了一個記錄的集合。 import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Database;
.....
// Environment and database opens omitted for clarity.
// Environment and database can NOT be opened read-only.
try {
String aKey = "myFirstKey";
DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
// Perform the deletion. All records that use this key are
// deleted.
myDatabase.delete(null, theKey);
} catch (Exception e) { // Exception handling goes here}
這里是根據(jù)指定的關(guān)鍵字"myFirstKey",來刪除掉指定的記錄,若該記錄有重復(fù)的話,那么就會刪掉該關(guān)鍵字的所有的記錄集合。
游標(biāo)的操作
游標(biāo)的各種操作和數(shù)據(jù)庫的各種操作都差不多。方法大體上是一樣的。 游標(biāo)的建立和關(guān)閉
游標(biāo)也是建立在數(shù)據(jù)庫基礎(chǔ)之上的,因此,游標(biāo)需要得到數(shù)據(jù)庫的對象。 import com.sleepycat.je.Environment;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Cursor;import java.io.File;
......
Environment myDbEnvironment = null;
Database myDatabase = null;
Cursor myCursor = null;
try {
myDbEnvironment = new Environment(new File("/export/dbEnv"), null);
myDatabase = myDbEnvironment.openDatabase(null, "myDB", null);
myCursor = myDatabase.openCursor(null, null);
} catch (DatabaseException dbe) { // Exception handling goes here ...}
從這里可以看到游標(biāo)是建立在數(shù)據(jù)庫基礎(chǔ)之上的。
游標(biāo)的關(guān)閉直接調(diào)用cursor.close()方法就可以了。
用游標(biāo)插入數(shù)據(jù)到數(shù)據(jù)庫
這里也是采用的put(),putNoDupData(),putNoOverwrite()三個方法來實現(xiàn)的,它們的使用方法和數(shù)據(jù)庫的插入方法一樣,這幾個方法的區(qū)別也是在于對重復(fù)記錄的判斷上,具體的區(qū)別請看有關(guān)的資料。 import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Database;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.OperationStatus;
.....
// Create the data to put into the database
String key1str = "My first string";
String data1str = "My first data";
String key2str = "My second string";
String data2str = "My second data";
String data3str = "My third data";
Cursor cursor = null;
try {
...
// Database and environment open omitted for brevity
...
DatabaseEntry key1 = new DatabaseEntry(key1str.getBytes("UTF-8"));
DatabaseEntry data1 = new DatabaseEntry(data1str.getBytes("UTF-8"));
DatabaseEntry key2 = new DatabaseEntry(key2str.getBytes("UTF-8"));
DatabaseEntry data2 = new DatabaseEntry(data2str.getBytes("UTF-8"));
DatabaseEntry data3 = new DatabaseEntry(data3str.getBytes("UTF-8"));
// Open a cursor using a database handle
cursor = myDatabase.openCursor(null, null);
// Assuming an empty database.
OperationStatus retVal = cursor.put(key1, data1);
// SUCCESS
retVal = cursor.put(key2, data2);
// SUCCESS
retVal = cursor.put(key2, data3);
// SUCCESS if dups allowed,
// KEYEXIST if not.
} catch (Exception e) { // Exception handling goes here}
finally { // Make sure to close the cursor cursor.close();}
在這個例子中,可以看到一開始的時候,cursor.put(key2, data2)執(zhí)行后,在數(shù)據(jù)庫中就已經(jīng)有了key2的記錄,后來,要調(diào)用了cursor.put(key2, data3)方法后,由于在數(shù)據(jù)庫中已經(jīng)存在了一個key2的記錄,現(xiàn)在又在這個key2基礎(chǔ)上添加一個,若允許數(shù)據(jù)庫是重復(fù)的,因此這里就替代掉了原來的data2的數(shù)據(jù)了,現(xiàn)在是data3了。
用游標(biāo)來得到數(shù)據(jù)和檢索數(shù)據(jù)
使用游標(biāo)最大的優(yōu)點還是在于能夠很方便的檢索記錄。用游標(biāo)得到記錄可以用getNext(),getPrev()等方法。 import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.LockMode;
.....
Cursor cursor = null;
try {
...
// Database and environment open omitted for brevity
...
// Open the cursor.
cursor = myDatabase.openCursor(null, null);
// Cursors need a pair of DatabaseEntry objects to operate. These hold
// the key and data found at any given position in the database.
DatabaseEntry foundKey = new DatabaseEntry();
DatabaseEntry foundData = new DatabaseEntry();
// To iterate, just call getNext() until the last database record has been
// read. All cursor operations return an OperationStatus, so just read
// until we no longer see OperationStatus.SUCCESS
while (cursor.getNext(foundKey, foundData, LockMode.DEFAULT) == OperationStatus.SUCCESS) {
// getData() on the DatabaseEntry objects returns the byte array
// held by that object. We use this to get a String value. If the
// DatabaseEntry held a byte array representation of some other data
// type (such as a complex object) then this operation would look
// considerably different.
String keyString = new String(foundKey.getData());
String dataString = new String(foundData.getData());
System.out.println("Key - Data : " + keyString + " - " + dataString + "");
}} catch (DatabaseException de) { System.err.println("Error accessing database." + de);}
finally { // Cursors must be closed. cursor.close();}
在這里例子實際上就是遍歷數(shù)據(jù)庫所有的記錄,將它打印出來,也可以使用getPrev()方法,也可以實現(xiàn)遍歷數(shù)據(jù)庫的操作。在重復(fù)的記錄中,用數(shù)據(jù)庫就無法操作,用游標(biāo)就可以實現(xiàn)對重復(fù)記錄的訪問,也是使用這幾個方法。
檢索關(guān)鍵字是使用游標(biāo)的getSearchKey(),getSearchKeyRange(),getSearchBoth(),getSearchBothRange()方法,這些方法的區(qū)別在于匹配的操作,具體的區(qū)別請參考有關(guān)的資料。 import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.LockMode;
.....
// For this example, hard code the search key and data
String searchKey = "Al";
String searchData = "Fa";
Cursor cursor = null;
try {
...
// Database and environment open omitted for brevity
...
// Open the cursor.
cursor = myDatabase.openCursor(null, null);
DatabaseEntry theKey = new DatabaseEntry(searchKey.getBytes("UTF-8"));
DatabaseEntry theData = new DatabaseEntry(searchData.getBytes("UTF-8"));
// Open a cursor using a database handle
cursor = myDatabase.openCursor(null, null);
// Perform the search
OperationStatus retVal = cursor.getSearchBothRange(theKey, theData, LockMode.DEFAULT);
// NOTFOUND is returned if a record cannot be found whose key begins
// with the search key AND whose data begins with the search data.
if (retVal == OperationStatus.NOTFOUND) {
System.out.println(searchKey + "/" + searchData + " not matched in database " + myDatabase.getDatabaseName());
} else {
// Upon completing a search, the key and data DatabaseEntry
// parameters for getSearchBothRange() are populated with the
// key/data values of the found record.
String foundKey = new String(theKey.getData());
String foundData = new String(theData.getData());
System.out.println("Found record " + foundKey + "/" + foundData +"for search key/data: " + searchKey + "/" + searchData)
; }} catch (Exception e) { // Exception handling goes here} finally { // Make sure to close the cursor cursor.close();}
這個例子,用的是getSearchBothRange()方法,它主要是用來查詢匹配這個關(guān)鍵字和該關(guān)鍵字的數(shù)據(jù)的所有記錄。 Cursor.count()這個方法,是用來統(tǒng)計當(dāng)前鍵的出現(xiàn)的總的記錄數(shù)。
用游標(biāo)刪除數(shù)據(jù)庫中的數(shù)據(jù),用cursor.delete()方法,和數(shù)據(jù)庫中的delete()方法用法一樣。不在舉例說明了。
使用游標(biāo)來代替當(dāng)前鍵的值,用cursor.putCurrent()方法來實現(xiàn),這在數(shù)據(jù)庫中的操作方法中是很難實現(xiàn)的,用游標(biāo)的這個方法,可以很方便的實現(xiàn)該操作。 import com.sleepycat.je.DatabaseEntry;import com.sleepycat.je.Database;import com.sleepycat.je.DatabaseEntry;import com.sleepycat.je.Cursor;import com.sleepycat.je.OperationStatus; import com.sleepycat.je.LockMode;......Cursor cursor = null;try { ... // Database and environment open omitted for brevity ... // Create DatabaseEntry objects // searchKey is some String. DatabaseEntry theKey = new DatabaseEntry(searchKey.getBytes("UTF-8")); DatabaseEntry theData = new DatabaseEntry(); // Open a cursor using a database handle cursor = myDatabase.openCursor(null, null); // Position the cursor. Ignoring the return value for clarity OperationStatus retVal = cursor.getSearchKey(theKey, theData, LockMode.DEFAULT); // Replacement data String replaceStr = "My replacement string"; DatabaseEntry replacementData = new DatabaseEntry(replaceStr.getBytes("UTF-8")); cursor.putCurrent(replacementData);} catch (Exception e) { // Exception handling goes here} finally { // Make sure to close the cursor cursor.close();}
這里的操作是用來代替掉指定的關(guān)鍵字的值,但是若有重復(fù)記錄的話,就無法實現(xiàn)了。
事務(wù)的處理
Berkeley DB Java Edition提供了全部的事務(wù)的ACID的所有屬性。(Atomicity,Consistency,Isolation,Durability)。
在Berkeley DB中使用事務(wù)時,可以很方便的配置和使用: 需要在數(shù)據(jù)庫環(huán)境中配置事務(wù)的操作為可用的,EnvironmentConfig.setTransactional(true),這樣就設(shè)置好了數(shù)據(jù)庫環(huán)境下可以方便的使用事務(wù)。
在數(shù)據(jù)庫中也要設(shè)置事務(wù)為可用的,DatabaseConfig.setTransactional(true),這樣數(shù)據(jù)庫就設(shè)置好了事務(wù)的處理。
接下來就可以在程序中使用事務(wù)了,在需要的地方插入transaction.commit(),提交事務(wù)。
數(shù)據(jù)備份和恢復(fù)
數(shù)據(jù)的備份分為兩種:一種是部分備份,另一種是全部備份。它們的共同點就是拷貝所有的log files(.jdb)文件從數(shù)據(jù)庫的環(huán)境目錄到所要歸檔的目錄或備份媒體。在這之前,必須要關(guān)閉事務(wù),若存在事務(wù),就無法它們的區(qū)別就是:部分備份是從最后一次備份開始到凡是修改過的或新增加的記錄都需要備份,而全部備份是將所有的數(shù)據(jù)庫信息,包括內(nèi)存緩存中的數(shù)據(jù)都要備份,不過這需要停止寫操作。
數(shù)據(jù)的恢復(fù):簡單的說就是將備份的log files文件拷貝到數(shù)據(jù)庫的環(huán)境目錄下,這里可以是增量備份,也可以是全部的備份,一般都是增量恢復(fù),這些操作必須要關(guān)閉JE的應(yīng)用程序。
其它
在這里只是說一下je.properties文件,這個文件是用來提供配置Berkeley DB的后臺以及整體系統(tǒng)運行的參數(shù),有的參數(shù)在je.properties文件中可以設(shè)置,在程序中也可以設(shè)置,但是在這個文件里的參數(shù)優(yōu)先于程序中設(shè)置的參數(shù)。這個文件必須放在數(shù)據(jù)庫環(huán)境的根目錄下,這樣系統(tǒng)才能執(zhí)行該文件中的參數(shù)。