继续上篇《java之BerkeleyDB(一)》
上篇我们先直接看了个例子:
有几个注意的,在BDB数据库里,默认是不能有重复的两个相同的键,当然可以通过config配置sortedDupli...来设置可以,所以在读取数据库值的时候必须考虑两种情况,是否存在相同的键的记录
JE provides two basic mechanisms for the storage and retrieval of database key/data pairs:
-
The
Database.put()
andDatabase.get()
methods provide the easiest access for all non-duplicate records in the database. These methods are described in this section. -
Cursors provide several methods for putting and getting database records. Cursors and their database access methods are described in Using Cursors.
-
Database.put()
-
Database.putNoOverwrite()
-
Database.putNoDupData()
-
Database.get() 这个利用key来找
-
Database.getSearchBoth() 这个利用Key和value共同找记录
上篇我们简单的打开了数据库和进行简单的存储
这个对于我们存储数据的基本类型还行,但是涉及到用户自定义类的时候,单靠之前的转换函数来从string转换程对象是不妨便的,我们来看看BDB为我们准备来什么?
引自使用手册:
DatabaseEntry
can hold any kind of data from simple Java primitive types to complex Java objects so long as that data can be represented as a Java byte
array. Note that due to performance considerations,you should not use Java serialization to convert a Java object to a byte
array. Instead, use the Bind APIs to perform this conversion (see Using the BIND APIs for more information).
看到加粗这段了把,官方推荐我们用Bind
(一)Bind技术
(1)SeriaBinding
先说下序列化的绑定技术,言外之意,我们的数据类型就必须实现Serializable才能进行下面的绑定
Serializing Objects
To store a serializable complex object using the Bind APIs:
-
Implement java.io.Serializable in the class whose instances that you want to store.
-
Open (create) your databases. You need two. The first is the database that you use to store your data. The second is used to store the class information.
-
Instantiate a class catalog. You do this with
com.sleepycat.bind.serial.StoredClassCatalog
, and at that time you must provide a handle to an open database that is used to store the class information. -
Create an entry binding that uses
com.sleepycat.bind.serial.SerialBinding
. -
Instantiate an instance of the object that you want to store, and place it in a
DatabaseEntry
using the entry binding that you created in the previous step.
没看源码,也没什么好展开的,大家就把方法记着吧,以后再一步步来
由于需要用到Class这个类,所以构造函数多了一个value的Class
这样就可以使得我们自己数据完整的存入了
import java.io.File;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.bind.serial.TupleSerialBinding;
import com.sleepycat.bind.tuple.TupleBase;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleTupleBinding;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
public class Main {
/**
* @param args
*/
static Environment env = null;
public static void main(String[] args) {
BDBUtil<Integer, Student> bDB = new BDBUtil<Integer, Student>("testDB",Student.class);
Student s1 = new Student(1,"ylf");
Student s2 = new Student(2,"dsb");
Student s3 = new Student(3,"dbc");
bDB.put(1, s1);
bDB.put(2, s2);
bDB.put(3, s3);
Student s = bDB.get(3);
System.out.println("my name is "+s.getName()+" no is "+s.getNo());
System.out.println(bDB.size());
bDB.close();
}
}
/**
* 我们的BDB工具
* 目前对外提供添加数据和取得数据,删除数据3个接口
* 类似HashMap的使用方法
* 注意:
* 这里的K 和 V两个类都必须实现来Serializable
* 而且也实现来toString
* 使用结束记得调用close()
* @author ylf
*
* 版本2:
* 采用动态的数据类型绑定,不是利用我们的toString()来转换对象和Entry
* 用到EntryBinding 和StoredClassCatalog
* 以及binding.OnjectToEntry() EntryToObject()
*
*/
class BDBUtil<K, V>{
private Environment env = null;
private EnvironmentConfig envCfig = null;
private Database db = null;
private DatabaseConfig dbCfig = null;
private Database classDB = null;
private StoredClassCatalog classCatalog = null;
private EntryBinding valueBinding = null;
private File file = null;
public BDBUtil(String dbName, Class valueClass) {
envCfig = new EnvironmentConfig();
envCfig.setAllowCreate(true);
file = new File("./test/");
env = new Environment(file, envCfig);
dbCfig = new DatabaseConfig();
dbCfig.setAllowCreate(true);
db = env.openDatabase(null, dbName, dbCfig);
//首先创建一个我们类的数据库
classDB = env.openDatabase(null, "classDB", dbCfig);
//实例化一个Catalog
classCatalog = new StoredClassCatalog(classDB);
//实例化一个binding来转换
valueBinding = new SerialBinding(classCatalog, valueClass);
}
public boolean put(K key, V value){
DatabaseEntry keyEntry = new DatabaseEntry(key.toString().getBytes());
DatabaseEntry valueEntry = new DatabaseEntry();
valueBinding.objectToEntry(value, valueEntry);
db.put(null, keyEntry, valueEntry);
return true;
}
public V get(K key){
DatabaseEntry keyEntry;
V value = null;
try {
keyEntry = new DatabaseEntry(key.toString().getBytes("gb2312"));
DatabaseEntry valueEntry = new DatabaseEntry();
if(db.get(null,keyEntry,valueEntry,LockMode.DEFAULT) == OperationStatus.SUCCESS){
value = (V)valueBinding.entryToObject(valueEntry);
return value;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return value;
}
public boolean del(K keyStr){
DatabaseEntry key;
try {
key = new DatabaseEntry(keyStr.toString().getBytes("gb2312"));
if(OperationStatus.SUCCESS == db.delete(null, key))
return true;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return false;
}
public long size(){
return db.count();
}
public void close(){
db.close();
classDB.close();
env.cleanLog();
env.close();
}
}
/**
* 序列化了的类
* 实现toString()
* @author ylf
*
*/
class Student implements Serializable{
/**
*
*/
private static final long serialVersionUID = 7333239714054069867L;
private String name;
private int no;
public Student() {
}
public Student(int no, String name){
this.no = no;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
@Override
public String toString() {
return "Student"+no+":"+name;
}
public void fromString(String str){
int i = str.indexOf(':');
String noStr = str.substring(7,i);
this.no = Integer.parseInt(noStr);
this.name = str.substring(i+1);
}
}
my name is dbc no is 3
3
(2)自定义绑定
官方还推荐使用custom tuple binding
就是我们实现TupleBinding,实现里面的objectToEntry() EntryToObject()
这里就不需要维护第二个数据库了,有木有又回到远点的赶脚!!
还不如上一篇里直接在类里实现转换
只不过官方推荐的这种是用字节流来存储,省流量,我们类里也可以利用ArrayByteInputStream好像是这个。。。我忘了,就字节流来实现就好了
(二)游标cursor
游标操作类似数据库的游标。
这里通过getNext() getPrev()来实现遍历
同时,cursor还提供了查找和插入数据的功能,我这里就不再一一写出来了,具体用到的时候在设计吧,现在全部封装一遍也不见得很好。。
interface BDBIterator{
public Object currentValue();
public Object currentKey();
public boolean hasNext();
public boolean hasPrev();
public void close();
}
这里迭代器实现的是BDBUtil的内部类,迭代器的实现大家可以参考Java的迭代器实现方法。
public BDBIterator getIterator(){
IteratorImpl it = new IteratorImpl();
return it;
}
/**
* 这个内部类模拟iterator遍历器
* @author ylf
* 迭代器使用方法
* 首先利用BDBUtil获得迭代器
* hasNext()如果下一个元素存在,游标下移动,值通过currentKey currentValue获取
* hasPrev()向前移动
* 最后记得close() 虽然我这里已经通过null来保证自动close()但不是线程安全的
*
*/
class IteratorImpl implements BDBIterator{
K currentKey = null;
V currentValue = null;
DatabaseEntry keyEntry = null;
DatabaseEntry valueEntry = null;
public IteratorImpl() {
if(cursor != null){
cursor.close();
cursor = null;
}
cursor = db.openCursor(null, null);//这里不配值CursorConfig了
}
@Override
public void close(){
cursor.close();
cursor = null;
}
@Override
public Object currentKey() {
return currentKey;
}
@Override
public Object currentValue() {
return currentValue;
}
@Override
public boolean hasNext() {
keyEntry = new DatabaseEntry();
valueEntry = new DatabaseEntry();
cursor.getNext(keyEntry, valueEntry, LockMode.DEFAULT);
return has();
}
@Override
public boolean hasPrev() {
keyEntry = new DatabaseEntry();
valueEntry = new DatabaseEntry();
cursor.getPrev(keyEntry, valueEntry, LockMode.DEFAULT);
return has();
}
public boolean has(){
if(keyEntry.getData() == null)
return false;
try {
currentKey = (K) new String(keyEntry.getData(),"gb2312");
currentValue = (V) valueBinding.entryToObject(valueEntry);
if(currentValue != null)
return true;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return false;
}
}
public static void main(String[] args) {
BDBUtil<Integer, Student> bDB = new BDBUtil<Integer, Student>("testDB",Student.class);
Student s1 = new Student(1,"ylf");
Student s2 = new Student(2,"dsb");
Student s3 = new Student(3,"dbc");
bDB.put(1, s1);
bDB.put(2, s2);
bDB.put(3, s3);
BDBIterator it = bDB.getIterator();
Student s = null;
int no=0;
while(it.hasNext()){
s = (Student)it.currentValue();
no = Integer.parseInt((String) it.currentKey());
System.out.println("my name is "+s.getName()+" no is "+s.getNo()+" "+no);
}
while(it.hasPrev()){
s = (Student)it.currentValue();
no = Integer.parseInt((String) it.currentKey());
System.out.println("my name is "+s.getName()+" no is "+s.getNo()+" "+no);
}
it.close();
BDBIterator it2 = bDB.getIterator();
while(it2.hasNext()){
s = (Student)it2.currentValue();
no = Integer.parseInt((String) it2.currentKey());
System.out.println("my name is "+s.getName()+" no is "+s.getNo()+" "+no);
}
it2.close();
System.out.println(bDB.size());
s = bDB.find(2);
System.out.println("find my name is "+s.getName());
bDB.close();
}