伯克利DBJavaAPI全面解析
发布时间: 2025-08-16 00:51:06 阅读量: 1 订阅数: 4 


Berkeley DB深入解析与实践指南
# 伯克利DB Java API 全面解析
## 1. 读取使用 TupleSerialBinding 接口存储的记录
在使用 `TupleSerialBinding` 接口存储记录后,我们可以使用以下代码来读取这些记录:
```java
Iterator i = personSet_.iterator();
try
{
while(i.hasNext())
{
System.out.println(i.next().toString());
}
}
finally
{
StoredIterator.close(i);
}
```
### 操作步骤:
1. 获取 `personSet_` 的迭代器 `i`。
2. 使用 `while` 循环遍历迭代器,只要 `i.hasNext()` 返回 `true`,就继续循环。
3. 在循环中,使用 `i.next()` 获取下一个元素,并调用其 `toString()` 方法进行输出。
4. 最后,使用 `StoredIterator.close(i)` 关闭迭代器。
## 2. 可序列化实体类
在之前的代码中,我们多次定义了 `Person` 类。最初,我们将 `Person` 类用作串行条目绑定中的值,因此需要实现 `Serializable` 接口,以便将 `Person` 对象序列化后存储在数据库中。后来,我们将其用作实体绑定的实体类,此时不需要实现 `Serializable` 接口,因为实体类不会直接存储在数据库中。
我们还注意到,在实体绑定的代码示例中,键和值部分存在冗余类。`PersonKey` 和 `PersonData` 类中包含的所有信息都可以在 `Person` 类中找到。为了消除这些冗余类,我们可以让 `Person` 类实现 `Serializable` 接口,并将表示键的成员变量声明为 `transient`。当一个成员被声明为 `transient` 时,它将被排除在序列化对象之外。以下是具体的代码实现:
```java
import java.io.Serializable;
public class Person implements Serializable
{
transient private Long ssn_;
private String name_;
private String dob_;
public Person()
{}
public Person(Long ssn,
String name,
String dob)
{
ssn_ = ssn;
name_ = name;
dob_ = dob;
}
public void setSSN(Long ssn)
{
ssn_ = ssn;
}
public void setName(String name)
{
name_ = name;
}
public void setDOB(String dob)
{
dob_ = dob;
}
public Long getSSN()
{
return ssn_;
}
public String getName()
{
return name_;
}
public String getDOB()
{
return dob_;
}
public String toString()
{
return "Person: ssn = " + ssn_ + " name = " + name_
+ " dob = " + dob_;
}
}
```
### 操作步骤:
1. 在 `Person` 类中,将 `ssn_` 成员变量声明为 `transient`。
2. 实现 `Serializable` 接口。
3. 提供必要的构造方法和访问器方法。
以下是修改后的绑定类代码:
```java
private static class PersonTupleSerialBinding extends
TupleSerialBinding
{
private PersonTupleSerialBinding(ClassCatalog catalog,
Class dataClass)
{
super(catalog, dataClass);
}
public Object entryToObject(TupleInput keyIn, Object dataIn)
{
Long ssn = keyIn.readLong();
Person p = (Person) dataIn;
p.setSSN(ssn);
return p;
}
public void objectToKey(Object o, TupleOutput to)
{
Person p = (Person) o;
to.writeLong(p.getSSN());
}
public Object objectToData(Object o)
{
return o;
}
}
```
### 操作步骤:
1. 创建一个继承自 `TupleSerialBinding` 的 `PersonTupleSerialBinding` 类。
2. 实现 `entryToObject` 方法,从键输入中读取 `ssn`,并设置到 `Person` 对象中。
3. 实现 `objectToKey` 方法,将 `Person` 对象的 `ssn` 写入键输出。
4. 实现 `objectToData` 方法,直接返回对象。
通过这种方式,我们完全消除了冗余的 `PersonKey` 和 `PersonData` 类。虽然声明键字段为 `transient` 不是必需的,但如果键的大小较大,将其排除在值部分可以节省空间。
## 3. 事务
`com.sleepycat.db.Transaction` 类封装了数据库事务,类似于 C++ API 中的 `DbTxn` 类。我们不能直接实例化这个类,而是需要使用 `Environment.beginTransaction` 方法来创建一个实例。以下是创建事务的示例代码:
```java
Transaction txn = null;
try
{
txn = env_.beginTransaction(null, null);
Person p = null;
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
Person manish = new Person(new Long(111223333),
"Manish Pandey", "jan 13 1971");
LongBinding.longToEntry(manish.getSSN(), key);
binding.objectToEntry(manish, data);
personDb_.put(null, key, data);
txn.commit();
}
catch (DatabaseException e)
{
txn.abort();
System.err.println("addPersonEntries: " + e.toString());
throw e;
}
```
### 操作步骤:
1. 初始化 `Transaction` 对象为 `null`。
2. 使用 `env_.beginTransaction(null, null)` 创建一个事务。
3. 创建 `DatabaseEntry` 对象用于存储键和值。
4. 创建 `Person` 对象并设置其属性。
5. 使用 `LongBinding.longToEntry` 将 `Person` 对象的 `ssn` 转换为键。
6. 使用 `binding.objectToEntry` 将 `Person` 对象转换为值。
7. 使用 `personDb_.put` 将键和值插入数据库。
8. 使用 `txn.commit()` 提交事务。
9. 如果发生异常,使用 `txn.abort()` 回滚事务,并输出错误信息。
## 4. 事务和存储集合 API
在伯克利 DB 集合 API 中,`Transaction` 对象不会被显式使用。事务通过 `com.sleepycat.Collections.TransactionRunner` 类来执行。我们可以使用 `TransactionRunner.run()` 方法启动一个事务,并通过 `TransactionWorker.doWork()` 方法指定要在事务中执行的操作。以下是使用 `TransactionWorker` 执行事务的示例代码:
```java
private class LoadDbs implements TransactionWorker
{
public void doWork() throws Exception
{
javaEnv_.loadEntries(bindingType_);
}
}
try
{
TransactionRunner runner =
new TransactionRunner(javaEnv_.getEnv());
runner.setMaxRetries(3);
runner.run(new DumpDbs());
}
catch(Exception e)
{
System.err.println(
"Exception caught while running transaction");
e.printStackTrace();
}
```
### 操作步骤:
1. 创建一个实现 `TransactionWorker` 接口的 `LoadDbs` 类,并实现 `doWork` 方法,在该方法中调用 `javaEnv_.loadEntries(bindingType_)`。
2. 创建 `TransactionRunner` 对象,并使用 `runner.setMaxRetries(3)` 设置最大重试次数。
3. 使用 `runner.run(new DumpDbs())` 启动事务。
4. 如果发生异常,捕获异常并输出错误信息。
### 事务执行流程
```mermaid
graph LR
A[开始] --> B[创建 TransactionRunner]
B --> C[设置最大重试次数]
C --> D[启动事务]
D --> E{是否成功}
E -- 是 --> F[结束]
E -- 否 --> G{是否达到最大重试次数}
G -- 是 --> H[输出错误信息]
G -- 否 --> D
```
## 5. 数据库操作
基本的数据库操作包括 `Database.get`、`Database.put` 和 `Database.delete`。此外,还有 `Database.putNoOverwrite` 和 `Database.putNoDupData`,在 C++ API 中,它们通过 `DB_NOOVERWRITE` 和 `DB_NODUPDATA` 标志来指定。
### 数据库操作类型
| 操作类型 | 描述 |
| ---- | ---- |
| `Database.get` | 获取数据库中的记录 |
| `Database.put` | 插入或更新数据库中的记录 |
| `Database.delete` | 删除数据库中的记录 |
| `Database.putNoOverwrite` | 插入记录,若记录已存在则不覆盖 |
| `Database.putNoDupData` | 插入记录,若记录重复则不插入 |
## 6. 二级索引
二级索引是一个使用与主数据库不同的键来查找记录的数据库。例如,我们可以创建一个基于 `Person` 记录的姓名字段的二级数据库,以便通过姓名查找 `Person` 记录。以下是创建二级索引的示例代码:
```java
private static class PersonByNameKeyCreator
extends TupleSerialKeyCreator
{
private PersonByNameKeyCreator(ClassCatalog catalog,
Class valueClass)
{
super(catalog, valueClass);
}
public boolean createSecondaryKey(TupleInput primaryKeyInput,
Object valueInput,
TupleOutput indexKeyOutput)
{
String name = primaryKeyInput.readString();
indexKeyOutput.writeString(name);
return true;
}
}
```
### 操作步骤:
1. 创建一个继承自 `TupleSerialKeyCreator` 的 `PersonByNameKeyCreator` 类。
2. 实现 `createSecondaryKey` 方法,从主键输入中读取姓名,并写入索引键输出。
3. 返回 `true` 以确保库正确维护二级索引。
以下是打开二级数据库的代码:
```java
SecondaryConfig secConfig = new SecondaryConfig();
secConfig.setTransactional(true);
secConfig.setAllowCreate(true);
secConfig.setType(DatabaseType.BTREE);
secConfig.setSortedDuplicates(true);
secConfig.setKeyCreator(new PersonByNameKeyCreator(classCatalogDb_,
Person.class));
SecondaryDatabase personByNameDb_ = null;
personByNameDb = env_.openSecondaryDatabase(null,
"PersonByNameDb",
null,
personDb_,
secConfig);
```
### 操作步骤:
1. 创建 `SecondaryConfig` 对象,并设置相关属性,如事务性、允许创建、数据库类型、是否允许重复等。
2. 使用 `secConfig.setKeyCreator` 设置二级键创建器。
3. 使用 `env_.openSecondaryDatabase` 打开二级数据库。
最后,我们可以使用二级数据库通过姓名查询 `Person` 记录:
```java
String name = "Manish Pandey";
DatabaseEntry secKey = new DatabaseEntry(name.getBytes("UTF-8"));
DatabaseEntry primaryKey = new DatabaseEntry();
DatabaseEntry value = new DatabaseEntry();
OperationStatus retVal = personByNameDb_.get(null, secKey,
primaryKey,
value,
LockMode.DEFAULT);
```
### 操作步骤:
1. 创建 `DatabaseEntry` 对象用于存储二级键、主键和值。
2. 将姓名转换为字节数组并存储在二级键中。
3. 使用 `personByNameDb_.get` 方法进行查询,并获取操作状态。
## 7. 完整代码示例
为了帮助大家更好地理解各种绑定,我们提供了一个完整的代码示例。该
0
0
相关推荐








