Berkeley DB Java Edition 使用手冊

Berkeley DB Java Edition使用說明

    頭一次看英文文檔,翻譯的不是十分準確,而且還有錯誤,還請大家多多諒解。
一、             簡介
       Berkeley DB Java Edition (JE)是一個完全用JAVA寫的,它適合於管理海量的,簡單的數據。
l         能夠高效率的處理1到1百萬條記錄,制約JE數據庫的往往是硬件系統,而不是JE本身。
l         多線程支持,JE使用超時的方式來處理線程間的死瑣問題。
l         Database都採用簡單的key/value對應的形式。
l         事務支持。
l         允許創建二級庫。這樣我們就可以方便的使用一級key,二級key來訪問我們的數據。
l         支持RAM緩衝,這樣就能減少頻繁的IO操作。
l         支持日誌。
l         數據備份和恢復。
l         遊標支持。
二、             獲取JE
JE下載地址:
解開包後 把JE_HOME/lib/je-<version>.jar 中的jar文件添加到你的環境變量中就可以使用je了。
相關幫助文檔可以參考 JE_HOME/docs/index.html
源代碼見JE_HOME/src/*.*
三、             JE常見的異常
DatabaseNotFoundException 當沒有找到指定的數據庫的時候會返回這個異常
DeadlockException 線程間死鎖異常
RunRecoveryException 回收異常,當發生此異常的時候,你必須得重新打開環境變量。
四、             關於日誌文件必須瞭解的六項
JE的日誌文件跟其他的數據庫的日誌文件不太一樣,跟C版的DBD也是有區別的
l         JE的日誌文件只能APPEND,第一個日誌文件名是 00000000.jdb,當他增長到一定大小的時候(默認是10M),開始寫第二個日誌文件00000001.jdb,已此類推。
l         跟C版本有所不同,JE的數據日誌和事務日誌是放在一起的,而不是分開放的。
l         JE cleaner負責清掃沒用到的磁盤空間,刪除後,或者更新後新的記錄會追加進來,而原有的記錄空間就不在使用了,cleaner負責清理不用的空間。
l         清理並不是立即進行的,當你關閉你的數據庫環境後,通過調用一個cleaner方法來清理。
l         清理也不是隻動執行的,需要你自己手動調用cleaner 方法來定時清理的。
l         日誌文件的刪除僅發生在檢查點之後。cleaner準備出哪些log 文件需要被刪除,當檢查點過後,刪掉一些不在被使用的文件。每寫20M的日誌文件就執行一次檢查點,默認下。
五、             創建數據庫環境
JE要求在任何DATABASE操作前,要先打開數據庫環境,就像我們要使用數據庫的話必須得先建立連接一樣。你可以通過數據庫環境來創建和打開database,或者更改database名稱和刪除database.
可以通過Environments對象來打開環境,打開環境的時候設置的目錄必須是已經存在的目錄,否則會出錯誤。默認情況下,如果指定的database不存在則不會自動創建一個新的detabase,但可以通過設置setAllowCreate來改變這一情況。
1.         打開database環境
示例:
package je.gettingStarted;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import java.io.File;
 
...
 
Environment myDbEnvironment = null;
 
try {
    EnvironmentConfig envConfig = new EnvironmentConfig();
    envConfig.setAllowCreate(true);//如果不存在則創建一個
    myDbEnvironment = new Environment(new File("/export/dbEnv"), envConfig);
} catch (DatabaseException dbe) {
    // 錯誤處理
}
2.         關閉database環境
可以通過Environment.close()這個方法來關閉database環境,當你完成數據庫操作後一定要關閉數據庫環境。
示例:
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
 
...
 
try {
    if (myDbEnvironment != null) {
        myDbEnvironment.close();
    }
} catch (DatabaseException dbe) {
    // Exception handling goes here
}
3.         清理日誌
通常在關閉數據庫連接的時候,有必要清理下日誌,用以釋放更多的磁盤空間。我們可以在Environment.close前執行下Environment.cleanLog()來達到此目的。
示例:
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
 
...
 
try {
    if (myDbEnvironment != null) {
        myDbEnvironment.cleanLog(); // 在關閉環境前清理下日誌
        myDbEnvironment.close();
    }
} catch (DatabaseException dbe) {
    // Exception handling goes here
}
4.         Database環境的配置
可以通過EnvironmentConfig這個對象來配置database環境。如果想得到當前環境的配置信息則可以通過Environment.getConfig()方法得到當前環境的配置信息。
也可以使用EnvironmentMutableConfig來配置環境,其實 EnvironmentConfig是EnvironmentMutableConfig的子類,所以EnvironmentMutableConfig能夠使用的設置,EnvironmentConfig也同樣能夠使用。
如果你要獲取當前環境的使用情況,那麼你可以通過使用EnvironmentStats.getNCacheMiss().來監視RAM cache命中率。EnvironmentStats可以由Environment.getStats()方法獲取。
EnvironmentConfig常見方法介紹
l         EnvironmentConfig.setAllowCreate() ;
如果設置了true則表示當數據庫環境不存在時候重新創建一個數據庫環境,默認爲false.
l         EnvironmentConfig.setReadOnly()
以只讀方式打開,默認爲false.
l         EnvironmentConfig.setTransactional()
事務支持,如果爲true,則表示當前環境支持事務處理,默認爲false,不支持事務處理。
                            EnvironmentMutableConfig的介紹
l         setCachePercent()
設置當前環境能夠使用的RAM佔整個JVM內存的百分比。
l         setCacheSize()
設置當前環境能夠使用的最大RAM。單位BYTE
l         setTxnNoSync()
當提交事務的時候是否把緩存中的內容同步到磁盤中去。
true 表示不同步,也就是說不寫磁盤
l         setTxnWriteNoSync()
當提交事務的時候,是否把緩衝的log寫到磁盤上
true 表示不同步,也就是說不寫磁盤
                            示例一:
package je.gettingStarted;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import java.io.File;
 
...
 
Environment myDatabaseEnvironment = null;
try {
    EnvironmentConfig envConfig = new EnvironmentConfig();
    //當環境不存在的時候自動創建環境
       envConfig.setAllowCreate(true);
       //設置支持事務
       envConfig.setTransactional(true);
    myDatabaseEnvironment =
        new Environment(new File("/export/dbEnv"), envConfig);
} catch (DatabaseException dbe) {
   System.err.println(dbe.toString());
   System.exit(1);
}
                  示例二:
package je.gettingStarted;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentMutableConfig;
import java.io.File;
...
try {
    Environment myEnv = new Environment(new File("/export/dbEnv"), null);
    EnvironmentMutableConfig envMutableConfig =
        new EnvironmentMutableConfig();
    envMutableConfig.setTxnNoSync(true);
    myEnv.setMutableConfig(envMutableConfig);
} catch (DatabaseException dbe) {
    // Exception handling goes here
}
                            示例三:
import com.sleepycat.je.Environment;
...
//沒有命中的CACHE
long cacheMisses = myEnv.getStats(null).getNCacheMiss();
...
5.         Database操作
在BDB中,數據是以key/value方式成隊出現的。
打開database
       可以通過environment.openDatabase()方法打開一個database,在調用這個方法的時候必須指定database的名稱。和databaseConfig() (注:數據庫設置)
       示例:
package je.gettingStarted;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import java.io.File;
...
Environment myDbEnvironment = null;
Database myDatabase = null;
...
try {
    // 打開一個環境,如果不存在則創建一個
    EnvironmentConfig envConfig = new EnvironmentConfig();
    envConfig.setAllowCreate(true);
    myDbEnvironment = new Environment(new File("/export/dbEnv"), envConfig);
 
    // 打開一個數據庫,如果數據庫不存在則創建一個
    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setAllowCreate(true);
    myDatabase = myDbEnvironment.openDatabase(null,
"sampleDatabase", dbConfig); //打開一個數據庫,數據庫名爲
                                   //sampleDatabase,數據庫的配置爲dbConfig
} catch (DatabaseException dbe) {
    // 錯誤處理
}
關閉database
       通過調用Database.close()方法來關閉數據庫,但要注意,在關閉數據庫前必須得先把遊標先關閉。
       使用示例:
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Database;
import com.sleepycat.je.Environment;
...
try {
        if (myDatabase != null) {
            myDatabase.close();
        }
        if (myDbEnvironment != null) {
            myDbEnvironment.close();
        }
} catch (DatabaseException dbe) {
    // 錯誤處理
}
設置數據庫屬性
       其實設置數據庫屬性跟設置環境屬性差不多,JE中通過DatabaseConfig對象來設置數據庫屬性。你能夠設置的數據庫屬性如下。
l         DatabaseConfig.setAllowCreate()
如果是true的話,則當不存在此數據庫的時候創建一個。
l         DatabaseConfig.setBtreeComparator()
設置用於Btree比較的比較器,通常是用來排序
l         DatabaseConfig.setDuplicateComparator()
設置用來比較一個key有兩個不同值的時候的大小比較器。
l         DatabaseConfig.setSortedDuplicates()
設置一個key是否允許存儲多個值,true代表允許,默認false.
l         DatabaseConfig.setExclusiveCreate()
以獨佔的方式打開,也就是說同一個時間只能有一實例打開這個database。
l         DatabaseConfig.setReadOnly()
以只讀方式打開database,默認是false.
l         DatabaseConfig.setTransactional()
如果設置爲true,則支持事務處理,默認是false,不支持事務。
                            使用示例:
package je.gettingStarted;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
...
// Environment open omitted for brevity
...
Database myDatabase = null;
try {
    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setAllowCreate(true);
    dbConfig.setSortedDuplicates(true);
    myDatabase =
        myDbEnv.openDatabase(null,
                             "sampleDatabase",
                             dbConfig);
} catch (DatabaseException dbe) {
    // Exception handling goes here.
}
一些用來管理的方法
l         Database.getDatabaseName()
取得數據庫的名稱
如:String dbName = myDatabase.getDatabaseName();
l         Database.getEnvironment()
取得包含這個database的環境信息
如:Environment theEnv = myDatabase.getEnvironment();
l         Database.preload()
預先加載指定bytes的數據到RAM中。
如:myDatabase.preload(1048576l); // 1024*1024
l         Environment.getDatabaseNames()
返回當前環境下的數據庫列表
如:
import java.util.List;
List myDbNames = myDbEnv.getDatabaseNames();
for(int i=0; i < myDbNames.size(); i++) {
    System.out.println("Database Name: " + (String)myDbNames.get(i));
}
l         Environment.removeDatabase()
刪除當前環境中指定的數據庫。
如:
String dbName = myDatabase.getDatabaseName();
myDatabase.close();
myDbEnv.removeDatabase(null, dbName);
l         Environment.renameDatabase()
給當前環境下的數據庫改名
如:
String oldName = myDatabase.getDatabaseName();  
String newName = new String(oldName + ".new", "UTF-8");
myDatabase.close();
myDbEnv.renameDatabase(null, oldName, newName);
l         Environment.truncateDatabase()
清空database內的所有數據,返回清空了多少條記錄。
如:
Int numDiscarded= myEnv.truncate(null,
myDatabase.getDatabaseName(),true);
System.out.println("一共刪除了 " + numDiscarded +" 條記錄 從數據庫 " + myDatabase.getDatabaseName());
6.         Database 記錄
JE的記錄包含兩部分,key鍵值和value數據值,這兩個值都是通過DatabaseEntry對象封裝起來,所以說如果要使用記錄,則你必須創建兩個DatabaseEntry對象,一個是用來做爲key,另外一個是做爲value.
 
DatabaseEntry能夠支持任何的能夠轉換爲bytes數組形式的基本數據。包括所有的JAVA基本類型和可序列化的對象.
          
使用記錄
 示例一:把字符串轉換DatabaseEntry
package je.gettingStarted;
import com.sleepycat.je.DatabaseEntry;
...
String aKey = "key";
String aData = "data";
try {
 //設置key/value,注意DatabaseEntry內使用的是bytes數組
 DatabaseEntry theKey=new DatabaseEntry(aKey.getBytes("UTF-8"));
 DatabaseEntry theData=new DatabaseEntry(aData.getBytes("UTF-8"));
} catch (Exception e) {
    // 錯誤處理
}
 示例二:把DatabaseEntry裏的數據轉換成字符串
byte[] myKey = theKey.getData();
byte[] myData = theData.getData();
String key = new String(myKey, "UTF-8");
String data = new String(myData, "UTF-8");
讀和寫database 記錄
    讀和寫database記錄的時候大體是基本一樣的,唯一有區別的是每個key寫是否允許寫多條記錄,默認情況下是不支持多條記錄的。
a)     你可以使用如下方法向database 裏添加記錄
l        Database.put()
    向database中添加一條記錄。如果你的database不支持一個key對應多個data或當前database中已經存在該key了,則使用此方法將使用新的值覆蓋舊的值。
l        Database.putNoOverwrite()
    向database中添加新值但如果原先已經有了該key,則不覆蓋。不管database是否允許支持多重記錄(一個key對應多個value),只要存在該key就不允許添加,並且返回perationStatus.KEYEXIST信息。
l        Database.putNoDupData()
    想database中添加一條記錄,如果database中已經存在了相同的 key和value則返回 OperationStatus.KEYEXIST.
使用示例:
    package je.gettingStarted;
    import com.sleepycat.je.Database;
    import com.sleepycat.je.DatabaseEntry;
    ...
    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
    }
b)     你可以使用如下方法從database 裏讀取記錄
1.         Database.get()
       基本的讀記錄的方法,通過key的方式來匹配,如果沒有改記錄則返回OperationStatus.NOTFOUND。
l         Database.getSearchBoth()
       通過key和value來同時匹配,同樣如果沒有記錄匹配key和value則會返回OperationStatus.NOTFOUND
使用示例:
package je.gettingStarted;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
...
String aKey = "myFirstKey";
 
try {
    DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();
   
    if (myDatabase.get(null, theKey, theData, LockMode.DEFAULT) ==
        OperationStatus.SUCCESS) {
        byte[] retData = theData.getData();
        String foundData = new String(retData, "UTF-8");
        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
}
c)     刪除記錄
   可以使用Database.delete()這個方法來刪除記錄。如果你的database支持多重記錄,則當前key下的所有記錄都會被刪除,如果只想刪除多重記錄中的一條則可以使用遊標來刪除。
    當然你也可以使用Environment.truncateDatabase()這個方法來清空database 中的所有記錄。
 
使用示例:
package je.gettingStarted;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
...
try {
    String aKey = "myFirstKey";
    DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
    myDatabase.delete(null, theKey);
} catch (Exception e) {
}
d)     提交事務
   當你對database進行了寫操作的時候,你的修改不一定馬上就能生效,有的時候他僅僅是緩存在RAM中,如果想讓你的修改立即生效,則可以使用Environment.sync()方法來把數據同步到磁盤中去。
e)     不同類型的數據的處理
1.     你可以使用DatabaseEntry來綁定基本的JAVA數據類型,主要有String、Character、Boolean、Byte、Short、Integer、Long、Float、Double.
       使用示例一:
              package je.gettingStarted;
              import com.sleepycat.bind.EntryBinding;
              import com.sleepycat.bind.tuple.TupleBinding;
              import com.sleepycat.je.DatabaseEntry;
              ...
              try {
                  String aKey = "myLong";
                  DatabaseEntry theKey = new
                  DatabaseEntry(aKey.getBytes("UTF-8"));   
 
                  Long myLong = new Long(123456789l);
                  DatabaseEntry theData = new DatabaseEntry();
                  EntryBinding myBinding =       
                  TupleBinding.getPrimitiveBinding(Long.class);
                  myBinding.objectToEntry(myLong, theData);
                  myDatabase.put(null, theKey, theData);
              } catch (Exception e) {
                  // Exception handling goes here
              }
       使用示例二:
       package je.gettingStarted;
       import com.sleepycat.bind.EntryBinding;
       import com.sleepycat.bind.tuple.TupleBinding;
       import com.sleepycat.je.Database;
       import com.sleepycat.je.DatabaseEntry;
       import com.sleepycat.je.LockMode;
       import com.sleepycat.je.OperationStatus;
       ...
       Database myDatabase = null;
       try {
           String aKey = "myLong";
           DatabaseEntry theKey = new
              DatabaseEntry(aKey.getBytes("UTF-8"));
           DatabaseEntry theData = new DatabaseEntry();
           EntryBinding myBinding =       
              TupleBinding.getPrimitiveBinding(Long.class);
           OperationStatus retVal = myDatabase.get(null, theKey, theData,
              LockMode.DEFAULT);
           String retKey = null;
           if (retVal == OperationStatus.SUCCESS) {
        Long theLong = (Long) myBinding.entryToObject(theData);
               retKey = new String(theKey.getData(), "UTF-8");
               System.out.println("For key: '" + retKey + "' found Long: '" +
                            theLong + "'.");
           } else {
               System.out.println("No record found for key '" + retKey + "'.");
           }
       } catch (Exception e) {
           // Exception handling goes here
       }
2.         可序列化的對象的綁定
1.         首先你需要創建一個可序列化對象
2.         打開或創建你的database,你需要兩個,一個用來存儲你的數據,另外一個用來存儲類信息。
3.         實例化catalog類,這個時候你可以使用com.sleepycat.bind.serial.StoredClassCatalog,來存儲你的類信息。
4.         通過com.sleepycat.bind.serial.SerialBinding來綁定數據和類。
5.         綁定並存儲數據。
示例:
l         創建一個可序列化的對象
package je.gettingStarted;
import java.io.Serializable;
public class MyData implements Serializable {
    private long longData;
    private double doubleData;
    private String description;
    MyData() {
        longData = 0;
        doubleData = 0.0;
        description = null;
    }
    public void setLong(long data) {
        longData = data;
    }
    public void setDouble(double data) {
        doubleData = data;
    }
    public void setDescription(String data) {
        description = data;
    }
    public long getLong() {
        return longData;
    }
    public double getDouble() {
        return doubleData;
    }
    public String getDescription() {
        return description;
    }
}
 
l         存儲數據
package je.gettingStarted;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
...
String aKey = "myData";
MyData data2Store = new MyData();
data2Store.setLong(123456789l);
data2Store.setDouble(1234.9876543);
data2Store.setDescription("A test instance of this class");
try {
    DatabaseConfig myDbConfig = new DatabaseConfig();
    myDbConfig.setAllowCreate(true);
    myDbConfig.setSortedDuplicates(true);
    Database myDatabase = myDbEnv.openDatabase(null, "myDb", myDbConfig);
    myDbConfig.setSortedDuplicates(false);
       //打開用來存儲類信息的庫
    Database myClassDb = myDbEnv.openDatabase(null, "classDb", myDbConfig);
    // 3)創建catalog
    StoredClassCatalog classCatalog = new StoredClassCatalog(myClassDb);
   // 4)綁定數據和類
    EntryBinding dataBinding = new SerialBinding(classCatalog,
                                                 MyData.class);
    DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
   // DatabaseEntry裏寫數據
    DatabaseEntry theData = new DatabaseEntry();
    dataBinding.objectToEntry(data2Store, theData);
    myDatabase.put(null, theKey, theData);
} catch (Exception e) {
    // 錯誤處理
}
l         讀數據
package je.gettingStarted;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
...
// The key data.
String aKey = "myData";
try {
    DatabaseConfig myDbConfig = new DatabaseConfig();
    myDbConfig.setAllowCreate(false);
    Database myDatabase = myDbEnv.openDatabase(null, "myDb", myDbConfig);
       //用來存儲類信息的庫
    Database myClassDb = myDbEnv.openDatabase(null, "classDb", myDbConfig);
 
    // 實例化catalog
    StoredClassCatalog classCatalog = new StoredClassCatalog(myClassDb);
    // 創建綁定對象
    EntryBinding dataBinding = new SerialBinding(classCatalog,
                                                 MyData.class);
    DatabaseEntry theKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();
    myDatabase.get(null, theKey, theData, LockMode.DEFAULT);
    // Recreate the MyData object from the retrieved DatabaseEntry using
    // 根據存儲的類信息還原數據
 MyData retrievedData=(MyData)dataBinding.entryToObject(theData);
 
} catch (Exception e) {
    // Exception handling goes here
}
3.         自定義對象的綁定
       使用tuple binding 來綁定自定義數據的步驟
①.     實例化你要存儲的對象
②.     通過com.sleepycat.bind.tuple.TupleBinding class來創建一個tuple binding。
③.     創建一個database,跟序列化的對象不同,你只需要創建一個。
④.     通過繼承第二步的類來創建一個entry binding 對象。
⑤.     存儲和使用數據
使用示例:
l         創建要存儲的對象
package je.gettingStarted;
public class MyData2 {
    private long longData;
    private Double doubleData;
    private String description;
    public MyData2() {
        longData = 0;
        doubleData = new Double(0.0);
        description = "";
    }
    public void setLong(long data) {
        longData = data;
    }
    public void setDouble(Double data) {
        doubleData = data;
    }
    public void setString(String data) {
        description = data;
    }
    public long getLong() {
        return longData;
    }
    public Double getDouble() {
        return doubleData;
    }
    public String getString() {
        return description;
    }
}
l         創建一個TupleBinding對象
package je.gettingStarted;
 
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
 
public class MyTupleBinding extends TupleBinding {
 
    // 把對象轉換成TupleOutput
    public void objectToEntry(Object object, TupleOutput to) {
 
        MyData2 myData = (MyData2)object;
        to.writeDouble(myData.getDouble().doubleValue());
        to.writeLong(myData.getLong());
        to.writeString(myData.getString());
    }
   //TupleInput轉換爲對象
    public Object entryToObject(TupleInput ti) {
        Double theDouble = new Double(ti.readDouble());
        long theLong = ti.readLong();
        String theString = ti.readString();
 
        MyData2 myData = new MyData2();
        myData.setDouble(theDouble);
        myData.setLong(theLong);
        myData.setString(theString);
        return myData;
    }
}
l          讀和寫數據
package je.gettingStarted;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.je.DatabaseEntry;
...
TupleBinding keyBinding = new MyTupleBinding();
MyData2 theKeyData = new MyData2();
theKeyData.setLong(123456789l);
theKeyData.setDouble(new Double(12345.6789));
theKeyData.setString("My key data");
 
DatabaseEntry myDate = new DatabaseEntry();
try {
    // theKeyData 存儲到DatabaseEntry
    keyBinding.objectToEntry(theKeyData, myDate);
    ...
    // Database 進行了一些讀和寫操作
    ...
    // Retrieve the key data
    theKeyData = (MyData2) keyBinding.entryToObject(myDate);
} catch (Exception e) {
    // 錯誤處理
}
f)      使用比較器
   JE是使用BTrees來組織結構的,這意味着當對database的讀和寫需要涉及BTrees間的節點比較。這些比較在key間是經常的發生的。如果你的database支持多重記錄,那麼也會存在data間的比較。
    默認的情況JE的比較器是按照字節的方式來進行比較的,這通常情況下能處理大多數的情況。但有的時候確實需要自定義比較器用於特殊的通途,比如說按照key來排序。
l        創建自己的比較器
    其實很簡單,只要你重寫Comparator class中的比較方法(compare)就可以了,通過Comparator.compare()會傳遞給你兩個byte 數組形式的值,如果你知道結構,則可以根據你自己定義的方法來進行比較
 
示例:
package je.gettingStarted;
import java.util.Comparator;
public class MyDataComparator implements Comparator {
    public MyDataComparator() {}
    public int compare(Object d1, Object d2) {
        byte[] b1 = (byte[])d1;
        byte[] b2 = (byte[])d2;
        String s1 = new String(b1, "UTF-8");
        String s2 = new String(b2, "UTF-8");
        return s1.compareTo(s2);
    }
}
l        讓database使用你自定義的比較器
   如果你想改變database中基本的排序方式,你只能重新創建database並重新導入數據。
①.    DatabaseConfig.setBtreeComparator()
    用於在database裏兩個key的比較
②.    DatabaseConfig.setOverrideBtreeComparator()
    如果爲true則代表讓database使用 DatabaseConfig.setBtreeComparator()設置的比較器來代替默認的比較器。
③.    DatabaseConfig.setDuplicateComparator()
    用於database可以使用多重記錄的時候的data的    比較。
④.    DatabaseConfig.setOverrideDuplicateComparator()
    如果爲true則代表讓database使用 DatabaseConfig. setDuplicateComparator()設置    的比    較器來代替默認的比較器。
使用示例:
package je.gettingStarted;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import java.util.Comparator;   
...
try {
    DatabaseConfig myDbConfig = new DatabaseConfig();
    myDbConfig.setAllowCreate(true);
 
    // 設置要使用的比較器
    myDbConfig.setDuplicateComparator(MyDataComparator.class);
    // 使用自己定義的比較器
    myDbConfig.setSortedDuplicates(true);
    Database myDatabase = myDbEnv.openDatabase(null, "myDb", myDbConfig);
} catch (DatabaseException dbe) {
    // Exception handling goes here
}
六、             遊標的使用
       遊標提供了遍歷你database中記錄的一種機制,使用遊標你可以獲取,添加,和刪除你的記錄。如果你的database支持多重記錄,則可以通過遊標訪問同一個key下的每一個記錄。
l         打開和關閉遊標
       要想使用遊標則你必須通過Database.openCursor()方法來打開一個遊標,你可以通過CursorConfig來配置你的遊標。
       可以通過Cursor.close()方法來關閉遊標。請注意在關閉database和環境前一定要關閉遊標,否則會帶來錯誤。
       打開遊標示例:
package je.gettingStarted;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
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 ...
}
關閉遊標示例:
package je.gettingStarted;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.Environment;
...
try {
    ...
} catch ... {
} finally {
    try {
        if (myCursor != null) {
            myCursor.close();
        }
 
        if (myDatabase != null) {
            myDatabase.close();
        }
        if (myDbEnvironment != null) {
            myDbEnvironment.close();
        }
    } catch(DatabaseException dbe) {
        System.err.println("Error in close: " + dbe.toString());
    }
}
l         通過遊標來獲取記錄
       可以通過遊標的Cursor.getNext()方法來遍歷記錄,Cursor.getNext()表示遊標指針向下移動一條記錄。同樣的Cursor.getPrev()表示遊標指針向上移動一條記錄。
       使用示例一:
package je.gettingStarted;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.LockMode; 
import com.sleepycat.je.OperationStatus;
...
Cursor cursor = null;
try {
    cursor = myDatabase.openCursor(null, null);
    DatabaseEntry foundKey = new DatabaseEntry();
    DatabaseEntry foundData = new DatabaseEntry();
 
    // 通過cursor.getNex方法來遍歷記錄
 while (cursor.getNext(foundKey, foundData, LockMode.DEFAULT) ==
        OperationStatus.SUCCESS) {
        String keyString = new String(foundKey.getData(), "UTF-8");
        String dataString = new String(foundData.getData(), "UTF-8");
        System.out.println("Key | Data : " + keyString + " | " +
                       dataString + "");
    }
} catch (DatabaseException de) {
    System.err.println("Error accessing database." + de);
} finally {
    // 使用後必須關閉遊標
    cursor.close();
}
使用示例二:
package je.gettingStarted;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.LockMode; 
import com.sleepycat.je.OperationStatus;
...
Cursor cursor = null;
try {
    ...
    // Open the cursor.
    cursor = myDatabase.openCursor(null, null);
    DatabaseEntry foundKey = new DatabaseEntry();
    DatabaseEntry foundData = new DatabaseEntry();
 
    // 使用cursor.getPrev方法來遍歷遊標獲取數據
    while (cursor.getPrev(foundKey, foundData, LockMode.DEFAULT)
       == OperationStatus.SUCCESS) {
        String theKey = new String(foundKey.getData(), "UTF-8");
        String theData = new String(foundData.getData(), "UTF-8");
        System.out.println("Key | Data : " + theKey + " | " + theData + "");
    }
} catch (DatabaseException de) {
    System.err.println("Error accessing database." + de);
} finally {
    // 使用後必須關閉遊標
    cursor.close();
}
l         搜索數據
       你可以通過遊標方式搜索你的database記錄,你也可以通過一個key來搜索你的記錄,同樣的你也可以通過key和value組合在一起來搜索記錄。如果查詢失敗,則遊標會返回OperationStatus.NOTFOUND。
       遊標支持都檢索方法如下:
1)        Cursor.getSearchKey()
       通過key的方式檢索,使用後遊標指針將移動到跟當前key匹配的第一項。
2)      Cursor.getSearchKeyRange()
    把遊標移動到大於或等於查詢的key的第一個匹配key,大小比較是通過你設置的比較器來完成的,如果沒有設置則使用默認的比較器。
3)      Cursor.getSearchBoth()
    通過key和value方式檢索,然後把遊標指針移動到與查詢匹配的第一項。
4)      Cursor.getSearchBothRange()
    把遊標移動到所有的匹配key和大於或等於指定的data的第一項。
 比如說database存在如下的key/value記錄,,大小比較是通過你設置的比較器來完成的,如果沒有設置則使用默認的比較器。
假設你的database存在如下的記錄。
               Alabama/Athens
               Alabama/Florence
               Alaska/Anchorage
               Alaska/Fairbanks
                Arizona/Avondale
               Arizona/Florence
然後查詢

查詢的key
查詢的data
遊標指向
Alaska
Fa
Alaska/Fairbanks
Arizona
Fl
Arizona/Florence
Alaska
An
Alaska/Anchorage
使用示例:
package je.gettingStarted;
 
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
...
 
String searchKey = "Alaska";
String searchData = "Fa";
 
Cursor cursor = null;
try {
    ...
    cursor = myDatabase.openCursor(null, null);
    DatabaseEntry theKey =
         new DatabaseEntry(searchKey.getBytes("UTF-8"));
    DatabaseEntry theData =
         new DatabaseEntry(searchData.getBytes("UTF-8"));
    cursor = myDatabase.openCursor(null, null);
    OperationStatus retVal = cursor.getSearchBothRange(theKey,
 theData, LockMode.DEFAULT);
    if (retVal == OperationStatus.NOTFOUND) {
        System.out.println(searchKey + "/" + searchData +
                           " not matched in database " +
                           myDatabase.getDatabaseName());
    } else {
        String foundKey = new String(theKey.getData(), "UTF-8");
        String foundData = new String(theData.getData(), "UTF-8");
        System.out.println("Found record " + foundKey + "/" + foundData +
                           "for search key/data: " + searchKey +
                           "/" + searchData);
    }
 
} catch (Exception e) {
    // Exception handling goes here
} finally {
   cursor.close();
}
l         使用遊標來定位多重記錄
       如果你的庫支持多重記錄,你可以使用遊標來遍歷一個key下的多個data.
1)      Cursor.getNext(), Cursor.getPrev()
       獲取上一條記錄或下一條記錄
2)      Cursor.getSearchBothRange()
    用語定位到滿足指定data的第一條記錄。
3)      Cursor.getNextNoDup(), Cursor.getPrevNoDup()
    跳到上一個key的最後一個data或下一個key的第一個data,忽略     當前key多重記錄的存在。
4)      Cursor.getNextDup(), Cursor.getPrevDup()
    在當前key中把指針移動到前一個data或後一個data.
5)      Cursor.count()
    獲取當前key下的data總數。
使用示例:
package je.gettingStarted;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
...
Cursor cursor = null;
try {
    ...
    // Create DatabaseEntry objects
    // searchKey is some String.
    DatabaseEntry theKey = new DatabaseEntry(searchKey.getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();
    cursor = myDatabase.openCursor(null, null);
 
    OperationStatus retVal = cursor.getSearchKey(theKey,
theData, LockMode.DEFAULT);
   
    // 如果count超過一個,則遍歷
    if (cursor.count() > 1) {
        while (retVal == OperationStatus.SUCCESS) {
            String keyString = new String(theKey.getData(), "UTF-8");
            String dataString = new String(theData.getData(), "UTF-8");
            System.out.println("Key | Data : " + keyString + " | " +
                               dataString + "");
            retVal = cursor.getNextDup(theKey, theData, LockMode.DEFAULT);
        }
    }
} catch (Exception e) {
    // Exception handling goes here
} finally {
   // Make sure to close the cursor
   cursor.close();
}
l         通過遊標來添加數據
       你可以通過遊標來向database裏添加數據
       你可以使用如下方法來向database裏添加數據
1)        Cursor.put()
    如果database不存在key,則添加,如果database存在key但允許多重記錄,則可以通過比較器在適當的位置插入數據,如果key已存在且不支持多重記錄,則替換原有的數據。
2)      Cursor.putNoDupData()
    如果存在相同的key和data則返回OperationStatus.KEYEXIST.
       如果不存在key則添加數據。
3)      Cursor.putNoOverwrite()
    如果存在相同的key在database裏則返OperationStatus.KEYEXIS,
    如果不存在key則添加數據。
package je.gettingStarted;
   
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.OperationStatus;
...
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 {
    ...
    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"));
    cursor = myDatabase.openCursor(null, null);
 
    OperationStatus retVal = cursor.put(key1, data1); // 添加成功
    retVal = cursor.put(key2, data2); // 添加成功
    retVal = cursor.put(key2, data3); // 如果允許多重記錄則添加成功                                                                                      //否則添加失敗 
} catch (Exception e) {
    // Exception handling goes here
} finally {
   // Make sure to close the cursor
   cursor.close();
}
l         使用遊標來刪除記錄
       你可以通過調用Cursor.delete().方法來刪除當前遊標所指向的記錄。刪除後如果沒有移動過指針這個時候調用Cursor.getCurrent()還是可以得到當前值的,但移動以後就不可以了。如果沒有重設指針,對同一個位置多次調用刪除方法,會返回OperationStatus.KEYEMPTY狀態。
    使用示例:
package je.gettingStarted;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
...
Cursor cursor = null;
try {
    ...
    DatabaseEntry theKey = new DatabaseEntry(searchKey.getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();
    cursor = myDatabase.openCursor(null, null);
    OperationStatus retVal = cursor.getSearchKey(theKey, theData,                                        LockMode.DEFAULT);
    //如果date不是多重記錄.
    if (cursor.count() == 1) {
            System.out.println("Deleting " +
                               new String(theKey.getData(), "UTF-8") +
                               "|" +
                               new String(theData.getData(), "UTF-8"));
            cursor.delete();//刪除當前記錄
    }
} catch (Exception e) {
    // Exception handling goes here
} finally {
   // Make sure to close the cursor
   cursor.close();
}
l         修改當前遊標所在位置的值
       可以通過Cursor.putCurrent()方法來修改,這個方法只有一個參數就是將要修改的值。這個方法不能用在多重記錄。
    使用示例:
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
...
Cursor cursor = null;
try {
    ...
    DatabaseEntry theKey = new DatabaseEntry(searchKey.getBytes("UTF-8"));
    DatabaseEntry theData = new DatabaseEntry();
    cursor = myDatabase.openCursor(null, null);
    OperationStatus retVal = cursor.getSearchKey(theKey, theData,
LockMode.DEFAULT);
   
    //將要被替換的值
    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();
}
七、             二級database
       在JE中包含你需要的(主要)數據的database被叫做primary database.而通過某種關係關聯起來的叫secondary database.通常secondary database和primary database的key是一樣的,只不過是爲了對應多條不同類型的數據。
       你可以通過SecondaryDatabase來創建二級庫,而通過使用繼承了SecondaryKeyCreator的 SecondaryConfig 來配置二級庫。
       通常primary database和secondary database間是存在某種關聯的,所以如果對其中一個做了一些變動,另外一個可能也需要跟着做相應的變動。
l         打開和關閉二級庫
       你可以通過Environment.openSecondaryDatabase()這個方法來打開一個二級庫,在打開前你必須指定二級庫的名稱和配置信息。
    需要與primary database和secondary database間綁定的就是索引。二級庫通常能夠提供額外的信息。
    如果想要使用二級庫,primary database不能支持多重記錄,他的key必須得保證唯一。不然你沒辦法進行關聯。
 
所以說如果你要想打開和創建一個二級庫需要如下步驟
①.     打開你的primary database.
②.     創建key creater實例。
③.     設置你的二級庫的配置文件,也就是創建SecondaryConfig
④.     這個時候你就可以打開你的二級庫了。
使用示例如下:
package je.gettingStarted;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.SecondaryDatabase;
import com.sleepycat.je.SecondaryConfig;
import java.io.File;
...
DatabaseConfig myDbConfig = new DatabaseConfig();
//二級庫的配置信息
SecondaryConfig mySecConfig = new SecondaryConfig();
myDbConfig.setAllowCreate(true);
mySecConfig.setAllowCreate(true);
// 通常二級庫經常是允許多重記錄的
mySecConfig.setSortedDuplicates(true);
 
// primary database
Environment myEnv = null;
Database myDb = null;
SecondaryDatabase mySecDb = null;
try {
    //打開primary
       String dbName = "myPrimaryDatabase";
    myEnv = new Environment(new File("/tmp/JEENV"), null);
    myDb = myEnv.openDatabase(null, dbName, myDbConfig);
 
    //創建tuple binding
    TupleBinding myTupleBinding = new MyTupleBinding();
 
    // 創建二級庫的key創建器
    FullNameKeyCreator keyCreator = new        FullNameKeyCreator(myTupleBinding);
 
    // 設置二級庫的創建器
    mySecConfig.setKeyCreator(keyCreator);
 
    //打開二級庫
    String secDbName = "mySecondaryDatabase";
    mySecDb = myEnv.openSecondaryDatabase(null, secDbName,
       myDb, mySecConfig);
} catch (DatabaseException de) {
    // 錯誤處理
}
可以調用二級庫的close()方法來關閉一個二級庫,在關閉 primary database前你必須先關閉一個二級庫。
try {
    if (mySecDb != null) {
        mySecDb.close();
    }
 
    if (myDb != null) {
        myDb.close();
    }
 
    if (myEnv != null) {
        myEnv.close();
    }
} catch (DatabaseException dbe) {
    // Exception handling goes here
}
l         二級庫的鍵創建器
              在使用二級庫的時候,你必須提供二級庫的鍵創建器來爲二級庫創建二級。在程序中你可以使用SecondaryConfig.setKeyCreator()來指定一個鍵創建器。你可以使用任何的數據創建二級庫的鍵,只要對你來說是需要的。
    自定義鍵創建器需要繼承SecondaryKeyCreator類,並且要重寫其中的createSecondaryKey方法。createSecondaryKey返回一個boolean形式的值,如果返回false,則表示二級庫不存在這個key.
   示例:
    假設你的primary database使用如下的結構來存儲data.
package je.gettingStarted;
public class PersonData {
    private String userID;
    private String surname;
    private String familiarName;
    public PersonData(String userID, String surname, String familiarName) {
        this.userID = userID;
        this.surname = surname;
        this.familiarName = familiarName;
    }
    public String getUserID() {
        return userID;
    }
    public String getSurname() {
        return surname;
    }
    public String getFamiliarName() {
        return familiarName;
    }
}
 
 你可以使用如下的方法來創建鍵創建器
package je.gettingStarted;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.je.SecondaryKeyCreator;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.SecondaryDatabase;
import java.io.IOException;
public class FullNameKeyCreator implements SecondaryKeyCreator {
    private TupleBinding theBinding;
    public FullNameKeyCreator(TupleBinding theBinding1) {
            theBinding = theBinding1;
    }
    public boolean createSecondaryKey(SecondaryDatabase secDb,
                                      DatabaseEntry keyEntry,
                                      DatabaseEntry dataEntry,
                                      DatabaseEntry resultEntry) {
 
        try {
            PersonData pd =
                (PersonData) theBinding.entryToObject(dataEntry);
                String fullName = pd.getFamiliarName() + " " +
                    pd.getSurname();
                resultEntry.setData(fullName.getBytes("UTF-8"));
        } catch (IOException willNeverOccur) {}
        return true;
    }
}
然後你可以向下面的方法使用你的鍵創建器
package je.gettingStarted;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.SecondaryDatabase;
import com.sleepycat.je.SecondaryConfig;
...
Environment myEnv = null;
Database myDb = null;
SecondaryDatabase mySecDb = null;
try {
...
    TupleBinding myDataBinding = new MyTupleBinding();
       //創建鍵創建器
    FullNameKeyCreator fnkc = new FullNameKeyCreator(myDataBinding);
 
    SecondaryConfig mySecConfig = new SecondaryConfig();
       //設置鍵創建器
    mySecConfig.setKeyCreator(fnkc);
    String secDbName = "mySecondaryDatabase";
    mySecDb = myEnv.openSecondaryDatabase(null, secDbName, myDb,
                                          mySecConfig);
} catch (DatabaseException de) {
    // Exception handling goes here
} finally {
    try {
        if (mySecDb != null) {
            mySecDb.close();
        }
        if (myDb != null) {
            myDb.close();
        }
 
        if (myEnv != null) {
            myEnv.close();
        }
    } catch (DatabaseException dbe) {
        // Exception handling goes here
    }
}
l         二級庫的配置設置
       可以通過SecondaryConfig來配置二級庫,SecondaryConfig是 DatabaseConfig的子類。所以你可以使用DatabaseConfig的一些信息。
    SecondaryConfig支持如下方法。
n         SecondaryConfig.setAllowPopulate()
       如果設置爲true,則表示二級庫允許自動填充。當primary database中的內容加進來後自動也會把secondary裏的數據也填充進來。
n         SecondaryConfig.setKeyCreator()
       設置要使用的鍵創建器
l         讀二級庫的數據
       你可以通過SecondaryDatabase.get()或使用SecondaryCursor.來讀取二級庫的信息。與primary database不同的是data不是直接返回給你,而是返回給你對應的primary database中的key和data和二級庫中對應的key.
如果二級庫支持多重記錄,則只返回第一條對應的數據。如果想看其他的只能通過SecondaryCursor(二級庫遊標)了。
    使用示例:
package je.gettingStarted;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.SecondaryDatabase;
...
try {
   ...
    String searchName = "John Doe";
    DatabaseEntry searchKey =
        new DatabaseEntry(searchName.getBytes("UTF-8"));
    DatabaseEntry primaryKey = new DatabaseEntry();
    DatabaseEntry primaryData = new DatabaseEntry();
 
    // 通過searchKey查找primary databasekeyvalue
    OperationStatus retVal = mySecondaryDatabase.get(null, searchKey,
primaryKey, primaryData, LockMode.DEFAULT);
} catch (Exception e) {
    // Exception handling goes here
}
l         刪除二級庫的記錄
       通常來講你不能夠直接修改二級庫的記錄,而只能通過修改primary database來達到修改二級庫的目的。但是你可以直接通過econdaryDatabase.delete()來刪除二級庫的記錄。但是如果你的庫支持多重記錄,那麼只能刪除匹配的第一條。
    使用示例:
package je.gettingStarted;
 
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.SecondaryDatabase;
...
try {
    ...
    String searchName = "John Doe";
    DatabaseEntry searchKey =
        new DatabaseEntry(searchName.getBytes("UTF-8"));
    // 刪除匹配JOHN DOE鍵的第一個二級庫的記錄
    OperationStatus retVal = mySecondaryDatabase.delete(
null, searchKey);
} catch (Exception e) {
    // Exception handling goes here
}
l         使用二級庫遊標
       跟primary database一樣你同樣可以使用遊標來操作數據。當你使用二級庫遊標的去記錄的時候,都會包含primary的記錄。SecondaryCursor.getSearchBoth()來搜索並不是搜索key/value對,而是搜索key和primary key.
    使用示例:
package je.gettingStarted;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.SecondaryDatabase;
import com.sleepycat.je.SecondaryCursor;
...
try {
    ...
    String secondaryName = "John Doe";
    DatabaseEntry secondaryKey =
        new DatabaseEntry(secondaryName.getBytes("UTF-8"));
    DatabaseEntry foundData = new DatabaseEntry();
    SecondaryCursor mySecCursor =
        mySecondaryDatabase.openSecondaryCursor(null, null);
 
    OperationStatus retVal = mySecCursor.getSearchKey(secondaryKey,
    foundData, LockMode.DEFAULT);
    while (retVal == OperationStatus.SUCCESS) {
        mySecCursor.delete();
        retVal = mySecCursor.getNextDup(secondaryKey,
         foundData, LockMode.DEFAULT);
    }
} catch (Exception e) {
    // Exception handling goes here
}
l         關聯database
       如果你創建了兩個或兩個以上的二級庫,你可以通過JoinCursor.來把他們關聯起來,可以採用JoinCursor來實現多維度的查詢,例如可以查詢
String theColor = "red";
String theType = "minivan";
String theMake = "Toyota";
來實現直接查找這些指定數據的記錄。與普通的遊標比較起來是可以對data域進行查詢,與單個次級數據庫遊標查詢比較起來是可以實現多個條件的聯立查詢。
1)        使用Join Cursor
a)         打開兩個或多個跟同一個primary database關聯的二級庫
b)        對每個二級庫分別定義遊標。
c)        創建secondary cursors數組
d)        通過Database.join()方法來建立關係
e)         通過JoinCursor.getNext()方法來遍歷相關記錄直到OperationStatus is not SUCCESS.爲止。
f)         關閉你的遊標
g)        關閉你所有的二級遊標。
使用示例:
package je.gettingStarted;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.JoinCursor;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.SecondaryCursor;
import com.sleepycat.je.SecondaryDatabase;
...
 
// Query strings:
String theColor = "red";
String theType = "minivan";
String theMake = "Toyota";
 
SecondaryCursor colorSecCursor = null;
SecondaryCursor typeSecCursor = null;
SecondaryCursor makeSecCursor = null;
 
JoinCursor joinCursor = null;
 
// 這些是用來做查詢用的
DatabaseEntry foundKey = new DatabaseEntry();
DatabaseEntry foundData = new DatabaseEntry();
 
try {
    DatabaseEntry color = new DatabaseEntry(theColor.getBytes("UTF-8"));
    DatabaseEntry type = new DatabaseEntry(theType.getBytes("UTF-8"));
    DatabaseEntry make = new DatabaseEntry(theMake.getBytes("UTF-8"));
       // 創建二級庫遊標:
    colorSecCursor = automotiveColorDB.openSecondaryCursor(
null, null);
    typeSecCursor = automotiveTypeDB.openSecondaryCursor(
null, null);
    makeSecCursor = automotiveMakeDB.openSecondaryCursor(
null, null);
 
    // 查詢條件
    OperationStatus colorRet =
        colorSecCursor.getSearchKey(color, foundData, LockMode.DEFAULT);
    OperationStatus typeRet =
        typeSecCursor.getSearchKey(type, foundData, LockMode.DEFAULT);
    OperationStatus makeRet =
        makeSecCursor.getSearchKey(make, foundData, LockMode.DEFAULT);
 
    // If all our searches returned successfully, we can proceed
    if (colorRet == OperationStatus.SUCCESS &&
        typeRet == OperationStatus.SUCCESS &&
        makeRet == OperationStatus.SUCCESS) {
 
       // Get a secondary cursor array and populate it with our
        // 創建SecondaryCursor
        SecondaryCursor[] cursorArray = {colorSecCursor,
        typeSecCursor, makeSecCursor, null};
 
        // Create the join cursor
        joinCursor = automotiveDB.join(cursorArray, null);
 
        // Now iterate over the results, handling each in turn
        while (joinCursor.getNext(foundKey, foundData, LockMode.DEFAULT) ==
                        OperationStatus.SUCCESS) {
 
            // Do something with the key and data retrieved in
            // foundKey and foundData
        }
    }
} catch (DatabaseException dbe) {
    // Error reporting goes here
} catch (Exception e) {
    // Error reporting goes here
} finally {
    try {
        // Make sure to close out all our cursors
        if (colorSecCursor != null) {
            colorSecCursor.close();
        }
        if (typeSecCursor != null) {
            typeSecCursor.close();
        }
        if (makeSecCursor != null) {
            makeSecCursor.close();
        }
        if (joinCursor != null) {
            joinCursor.close();
        }
    } catch (DatabaseException dbe) {
        // Error reporting goes here
    }
}
 
l         配置JoinCursor
       你可以通過JoinConfig來配置joinCursor,JoinConfig只有一個方法。
    JoinConfig.setNoSort(),如果設置爲true,則表示不使用自動排序,false的時候則按照關聯少的到關聯多的方式排序。
    使用示例:
JoinConfig config = new JoinConfig();
config.setNoSort(true);
JoinCursor joinCursor = myDb.join(cursorArray, config);
八、             數據的備份和恢復
       在通常情況下你可以通過把你的database文件移動到安全的地方來保護你的數據。可能下面這些情況可能需要你額外需要考慮的
l         數據庫和日誌
              在瞭解數據備份和恢復前你有必要了解下JE database的內部數據結構、日誌文件          和內存CACHE。
              JE以追加的方式向日志文件中添加數據,這意味着你日誌文件永遠不會被覆蓋,          修改和刪除。日誌文件的命名是NNNNNNNN.jdb,NNNNNNNN是八進制的數字,第      一個日誌文件名是00000000.jdb,以後每增加一個則加一。每個日誌文件的最大大小默認    是10000000 bytes ,當日志文件超過這個大小後就會在生成一個日誌文件,但你可以通          過修改je.log.fileMax來修改默認的日誌文件的大小。
              因爲JE使用不覆蓋的方式來寫日誌文件,所以過一段時間必須得清理或壓縮下日         志文件用來釋放更多的磁盤空間。JE使用後臺線程來清理任務。如果這個日誌文件已             經不在使用,那麼清理器就會自動幫你刪除或給你加個刪除標記。
              JE databases採用BTree的方式組織,當記錄被添加、修改或刪除,實際上這些操          作都是控制BTree的子節點,比如說更改一個節點到另外一個父節點下。
              數據庫修改和同步,當對數據進行操作的時候,操作是直接操作內存中的BTree            子節點的,如果你的database不支持事務,則cache是唯一的,以保證相互間的修改不      會產生衝突。這種不時時把數據同步到磁盤其實是比較好的,因爲他減少了頻繁的IO             讀取,從而提高了速度。但是如果你的數據要求十時性很強,你可以手動調用       Environment.sync()來時時的把數據同步到你的磁盤中,但這樣可能會帶來IO的繁忙。
              其實一但當環境被打開,JE就會嘗試着從日誌文件中恢復那些被丟失的節點
              你可以選擇熱備份和冷備份兩種,但他都數據完全備份(也就是說所有的數據),        所以說不要把冷熱備份與完全備份和增量備份相混淆。
              所謂的熱備份就是在你的程序還在運行的時候把所有的*.jdb文件COPY出去,這          這樣的好處是你不必停止你的服務。但這樣當前CACHE在內存中的數據還沒有被備份     下來。
              離線備份比較完全,但他的缺點也是顯而易見的,你必須終止你的服務。首先你要       終止你的寫操作,然後調用Environment.sync()方法來把你內存中的數據同步      到硬盤中去,關閉你的遊標,你的數據庫,你的環境,這個時候把所有的.jdb 文件COPY出去就行了。
l         使用備份幫助類
        JE提供了DbBackup 這個類來協助你保護好你的數據。
       使用示例:
package je.gettingStarted;
...
import com.sleepycat.je.util.DbBackup;
...
    Environment env = new Environment(...);
    DbBackup backupHelper = new DbBackup(env);
 
    // 找到前一次備份的版本號
    // 檢查保存狀態
    long lastFileCopiedInPrevBackup = ...
 
    // 開始備份,檢查你要備份那些文件
    backupHelper.startBackup();
    try {
        String[] filesForBackup =
backupHelper.getLogFilesInBackupSet(lastFileCopiedInPrevBackup);
        // 複製文件
        myApplicationCopyMethod(filesForBackup)
        // 保存你本次備份的信息
        lastFileCopiedInPrevBackup = backupHelper.getLastFileInBackupSet();
        myApplicationSaveLastFile(lastFileCopiedInBackupSet);
    }
    finally {
        // Remember to exit backup mode, or all log files won't be cleaned
        // and disk usage will bloat.
       backupHelper.endBackup();
   }
l         災難恢復
1.         停止你的服務
2.         刪除所有的在你環境所在目錄的內容
3.         把你的備份COPY到你的目錄中
4.         如果你是backup的增量方式備份你的環境目錄,請COPY最新的哪個版本(翻譯的不是十分準確原文如下:If you are using a backup utility that runs incremental backups of your environment directory, copy any log files generated since the time of your last full backup. Be sure to restore all log files in the order that they were written. The order is important because it is possible the same log file appears in multiple archives, and you want to run recovery using the most recent version of each log file)
5.         重新打開你的環境,恢復你的服務。
l         熱備份
1.         從你的環境中複製所有的.jdb文件到你想要備份的地方,不管是熱備份還是離線備份都要這麼做。在備份前要確保你把內存中的數據也同步到了磁盤中去了。這個階段屬於完全備份。
2.         然後定期複製新生成的那些.jdb文件。
3.         刪除那些已經做了刪除標記的舊的jdb文件(通常是調用cleaner產生的)。
九、             BDB針對應用的一些管理
l         用je.properties來初始化一些配置信息
    je.properties必須位於你環境的主目錄下,且名稱必須是je.properties.
       je使用一些後臺的線程來對你的環境進行預配置,這些後臺線程包括如下:
l         Cleaner thread. 主要是進行一些清理性工作,當環境進行寫操作的時候才啓動。
1)        je.cleaner.minUtilization
   主要是用來確保騰出一定的空間來存放記錄。如果空間不夠用了則移出一些舊的不在使用的記錄,默認這個空間佔50%。
2)     je.cleaner.expunge
   主要是用來確認一個日誌文件是否已經沒用,可以被刪除了,如果一個日誌由原先的NNNNNNNN.jdb 名稱被修改爲NNNNNNNN.del名稱那麼你要對這進行一些處理。
l         Compressor thread 用來壓縮一些已經刪除的,被修改了的沒用的BTree節點,當環境發生寫操作的時候才觸發。
l         Checkpointer thread,檢查你的環境,本編程總是運行。
l         Sizing the Cache內存中CACHE大小的設置
       你可以使用 EnvironmentMutableConfig.setCachePercent()來修改
je.maxMemoryPercent屬性來改變je能夠使用的最大內存的佔整個JVM虛擬機內存的百分比。
       你也可以通過使用EnvironmentConfig.setCacheSize().來改變je.maxMemory中設置的JE能夠使用的最大內存。
       你也可以通過調用EnvironmentStats.getNCacheMiss()來取得當前有多少數據沒有通過內存的方式來命中。
l         命令行工具
1)        DbDump,把database轉換成一種可讀的形式,通常用來導出數據。
       參數如下:
①.     –f :指定把數據輸出到那個文件中,如果沒有指定則輸出到控制檯(也就是屏幕)。
②.     –h :用來指定環境所在目錄,這個參數必須指定。
③.     –l :列出當前環境的數據庫名,如果沒有指定-s着此參數必選。
④.     –p :以可讀的方式輸出數據庫記錄
⑤.     –r :這個操作通過不同的排序方式輸出一些經過整理的那些有用的數據數據,通常如果指定你的數據庫名爲dbname,則他會把結果輸出到dbname.dump(翻譯的不是太準確原文如下:Salvage data from a possibly corrupt file. When used on a uncorrupted database, this option should return data equivalent to a normal dump, but most likely in a different order. This option causes the ensuing output to go to a file named dbname.dump where dbname is the name of the database you are dumping. The file is placed in the current working directory.)
⑥.     –R :跟-r有所不同,他輸出的是所有的數據包括被刪除的和沒有被刪除的(原文如下:Aggressively salvage data from a possibly corrupt file. This option differs from the -r option in that it will return all possible data from the file at the risk of also returning already deleted or otherwise nonsensical items. Data dumped in this fashion will almost certainly have to be edited by hand or other means before the data is ready for reload into another database. This option causes the ensuing output to go to a file named dbname.dump where dbname is the name of the database you are dumping. The file is placed in the current working directory.)
⑦.     –s:指定數據庫名稱 如果沒有指定-l參數,則此參數必選。
⑧.     –v:確定顯示信息是按照-r模式還是按照-R模式
⑨.     –V:打印數據庫版本後推出
使用示例:
> java com.sleepycat.je.util.DbDump -h . -p -s VendorDB 
VERSION=3
format=print
type=btree
database=VendorDB
dupsort=false
HEADER=END
 Mom's Kitchen
 sr/01/01xpt/00/0d53 Yerman Ct.t/00/0c763 554 9200t/00/0bMiddle Townt/00
 /0eMaggie Kultgent/00/10763 554 9200 x12t/00/02MNt/00/0dMom's Kitchent/00
 /0555432
 Off the Vine
 sr/01/01xpt/00/10133 American Ct.t/00/0c563 121 3800t/00/0aCentennialt/00
 /08Bob Kingt/00/10563 121 3800 x54t/00/02IAt/00/0cOff the Vinet/00/0552002
 Simply Fresh
 sr/01/01xpt/00/1115612 Bogart Lanet/00/0c420 333 3912t/00/08Harrigant/00
 /0fCheryl Swedbergt/00/0c420 333 3952t/00/02WIt/00/0cSimply Fresht/00/0
 553704
 The Baking Pan
 sr/01/01xpt/00/0e1415 53rd Ave.t/00/0c320 442 2277t/00/07Dutchint/00/09
 Mike Roant/00/0c320 442 6879t/00/02MNt/00/0eThe Baking Pant/00/0556304
 The Pantry
 sr/01/01xpt/00/111206 N. Creek Wayt/00/0c763 555 3391t/00/0bMiddle Town
 t/00/0fSully Beckstromt/00/0c763 555 3391t/00/02MNt/00/0aThe Pantryt/00
 /0555432
 TriCounty Produce
 sr/01/01xpt/00/12309 S. Main Streett/00/0c763 555 5761t/00/0bMiddle Townt
 /00/0dMort Dufresnet/00/0c763 555 5765t/00/02MNt/00/11TriCounty Producet
 /00/0555432
DATA=END
>
2)        DbLoad 導入通過DbDump方式導出的數據
       常見的參數如下:
①.     –c: 可以指定兩個參數 database 表示將要被導入的庫名 dupsort 如果爲true表示數據庫支持多重記錄。
②.     –f:指定將要導入的文件
③.     –n: 不覆蓋已經存在的值(key/value),如果存在相同的則輸出警告信息。
④.     –h:指定環境所在的目錄。
⑤.     –s:指定數據庫的名稱
⑥.     –t:把指定的文本信息導入到庫中,文本信息必須以key/value形式成對出現。
⑦.     –v:輸出詳細信息
⑧.     –V:輸出數據庫版本;
使用示例:
> java com.sleepycat.je.util.DbDump -h . -s VendorDB -f vendordb.txt
> java com.sleepycat.je.util.DbLoad -h . -f vendordb.txt
>
3)        DbVerify,檢查數據庫文件是否有錯誤。參數如下:
①.     –h:確定環境路徑
②.     –q:禁止輸出詳細的調試信息,只輸出成功還是失敗。
③.     –s:確定數據庫的名稱。
④.     –v:輸出每個節點信息。
⑤.     –V:輸出數據庫版本信息。
使用示例:   
> java com.sleepycat.je.util.DbVerify -h . -s VendorDB
<BtreeStats>
<BottomInternalNodesByLevel total="1">
 <Item level="1" count="1"/>
</BottomInternalNodesByLevel>
<InternalNodesByLevel total="1">
 <Item level="2" count="1"/>
</InternalNodesByLevel>
<LeafNodes count="6"/>
<DeletedLeafNodes count="0"/>
<DuplicateCountLeafNodes count="0"/>
<MainTreeMaxDepth depth="2"/>
<DuplicateTreeMaxDepth depth="0"/>
</BtreeStats>
 
Write by 延年
Date 2007-8-3
 
發佈了36 篇原創文章 · 獲贊 3 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章