Java之BerkeleyDB(三)

这里简单再介绍下Berkeley的二级数据库的知识,

最近没怎么接触又忘了,本来是在想,这个数据库是基于内存的,那持久化问题怎么解决呢?是否有什么API可以实现存储到磁盘上呢?后来发现,其实BerkeleyDB已经为我们实现了自动存储,只需要我们正确close(),数据就会存储起来了,于是乎,就再看看二级数据库。。

二级数据库的来源:

Usually you find database records by means of the record's key. However, the key that you use for your record will not always contain the information required to provide you with rapid access to the data that you want to retrieve. For example, suppose your Database contains records related to users. The key might be a string that is some unique identifier for the person, such as a user ID. Each record's data, however, would likely contain a complex object containing details about people such as names, addresses, phone numbers, and so forth. While your application may frequently want to query a person by user ID (that is, by the information stored in the key), it may also on occasion want to locate people by, say, their name.


正如官方文档所述,二级数据库是为了让我们能够不仅仅只能通过key来索引数据库,同时还为我们建立了另一个索引途径,所以我这里更喜欢叫他二级索引数据库。

例如我们现在有Student这个类,我们之前都是通过id来进行索引,这个不方便的地方就是如果我们想找Jimmy,怎么实现呢?过去我们可以通过遍历,一个个比较name==Jimmy,相等则返回结果。

 In JE, the Database that contains your data is called a primary database. A database that provides an alternative set of keys to access that data is called a secondary database

二级索引则帮助我们改善了上述做法,具体实现我也不是很清楚,还是得看源码,不过下面我会给大家说下我的一些想法。

其实建立这个二级索引并不是很难,大家就想象成再次建立一个数据库罢了,只不过这个数据库的Key需要我们自己定义,这就是为什么它能够根据这个索引来定位Primary数据库的记录。

Primary databases must not support duplicate records. 

Secondary records point to primary records using the primary key, so that key must be unique.

So to open (create) a secondary database, you:

  1. Open your primary database.

  2. Instantiate your key creator.

  3. Instantiate your SecondaryConfig object.

  4. Set your key creator object on your SecondaryConfig object.

  5. Open your secondary database, specifying your primary database and your SecondaryConfigat that time.

看下官方给的流程,除了那个KeyCreator相关的,其他都和创建主数据库一致

这里的KeyCreator就是我们自己为二级索引库建立索引,正如Primary的索引也是我们自己定义的数据类型,这里二级索引只不过是让我们提供另一个我们也希望可以查询数据用的索引。

这样理解就简单了,如果我们希望通过Student的name来查找学生,那我们需要自己实现一个KeyCreator的类来实现这个Key对象的生成,并且把它交给二级索引

这个Key生成器的类实现必须实现SecondaryKeyCreator的接口,同时实现里面的方法

class KeyCreator implements SecondaryKeyCreator{
		private EntryBinding valueBinding = null;
		public KeyCreator(EntryBinding binding) {
			valueBinding = binding;//用来实现Entry--object 转换
		}
		@Override
		public boolean createSecondaryKey(SecondaryDatabase sdb,
				DatabaseEntry keyEntry, DatabaseEntry valueEntry, DatabaseEntry resultEntry) {
			
			V value = (V)valueBinding.entryToObject(valueEntry);
			String data = value.toString();
			System.out.println("in createSKey..."+data);
			try {
				resultEntry.setData(data.getBytes(charset));
				return true;
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			return false;
		} 
		
	}
可以看到,我这里建立的二级索引用的是toString()这个字符串来对数据进行另一中途径的索引。

实现了我们自己的索引key之后,剩下就是和主数据库创建过程一样了

/**
		 * 创建二级索引数据库
		 */
		String secDbName = "secDbName";
		secCfig = new SecondaryConfig();
		secCfig.setAllowCreate(true);
		secCfig.setSortedDuplicates(true);//二级库可以有重复
		
		KeyCreator keyCreator = new KeyCreator(valueBinding);
		secCfig.setKeyCreator(keyCreator);
		secDb = env.openSecondaryDatabase(null, secDbName, db, secCfig);

这里的valueBinding就是用来实现DatabaseEntry--Object转换的绑定技术,具体实现参见

java之BerkeleyDB(二)--绑定(Binding)技术、游标(Cursor)

这个二级索引库的使用方法见如下所示,同样也可以采用cursor游标,这些都和主数据库一致的,大家注意下,我们是不可以通过二级库来修改主数据库的内容,只能删除,不能更改!!

/**
	 * 查找
	 */
	public V getBySec(String searchContent){
		try{
			DatabaseEntry searchEntry = new DatabaseEntry(searchContent.getBytes(charset));
			DatabaseEntry keyEntry = new DatabaseEntry();
			DatabaseEntry valueEntry = new DatabaseEntry();
			secDb.get(null, searchEntry, keyEntry, valueEntry, LockMode.DEFAULT);
			if(valueEntry.getData()!=null){
				V value = (V)valueBinding.entryToObject(valueEntry);
				return value;
			}
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}


public static void main(String[] args) {
		BDBUtil<Integer, Student> bDB = new BDBUtil<Integer, Student>("testDB",Student.class);
		Student s1 = new Student(1,"ylf");
		
		BDBIterator it = bDB.getIterator();
		while(it.hasNext()){
			Student t = (Student)it.currentValue();
			System.out.println(it.currentKey()+t.toString());
		}
		it.close();
		
		String searchContent  = s1.toString();
		Student find = bDB.getBySec(searchContent);
		
		if(find != null)
			System.out.println("find:"+find.toString());
		bDB.close();
	}

大家可以看见我在main函数执行先遍历所有结果,然后查找s1这个学生显示如下:

1 Student1:ylf
2 Student2:dsb
3 Student3:dbc
4 Student1:ylf
5 Student5:zjq
6 Student6:zjq
7 Student3:dbc
Find:Student1:ylf

可以找到student1:ylf

不过在实验中我发先,如果某个学生是在二级数据库之前就已经存在的,那么这个学生是无法被这个二级索引发现的例如第二条记录student2:dsb

String searchContent  = s2.toString();
		Student find = bDB.getBySec(searchContent);

执行后返回find=null

这就是说,这个二级索引库其实是在我们插入数据的时候通过某个触发器,在主数据库创建新插入数据以后,二级库也为其建立所以,而这个索引的Key就是keyCreator我们定义的,所以这个过程会调用一次我们实现的接口方法createSecondaryKey(),至于这个二级索引库放的value(data)我觉得应该是一个指向Primary某个条记录的指针之类的结构,所以这个二级库也就很好理解了,是利用空间换时间的一个方法,这些都是我的猜想。

我接下来在已经使用了二级索引库的这个对象里再次插入新数据显示如下:
insert a new Student:
in createSKey...Student9:Jimmy
insert OK, now find him by secDB:
Find:Student9:Jimmy

main()函数如下:
System.out.println("insert a new Student:");
		Student s9 = new Student(9,"Jimmy");
		bDB.put(9, s9);
		System.out.println("insert OK, now find him by secDB:");
		
		String searchContent  = s9.toString();
		Student find = bDB.getBySec(searchContent);
		
		if(find != null)
			System.out.println("Find:"+find.toString());

in createSKey...Student9:Jimmy
这句是在接口方法 createSecondaryKey()里打印出来的,这个过程和上面的猜想基本符合的,我也不是想说明什么问题,只是这种理解方便我自己理解罢了,如果有问题大家多多交流交流




你可能感兴趣的:(database,primary,secondary,BerkeleyDB)