日期:2014-05-16  浏览次数:20512 次

OMToolkit介绍(4) :Object-Oriented Database 实现
OMToolkit介绍(4) :Object-Oriented Database 实现

1. 概述

  OMToolkit中数据存储的实现主要位于com.omc.data中,说是Object-Oriented Database可能有点夸大了,实际上是采用文本存储Entity的方式,实现方式比较初级。
  存储文件有两个,分别是data/meta和data/data。程序启动时将加载meta文件的内容。meta存储了Entity的id,Entity数据在data文件中的位置,以及Entity的类型。读取一个对象时,先从meata中读取数据所在的位置,再到data文件中获取Entity的数据(各属性的值)。
  程序启动时会加载全部的meta,数据量较大时将占用较大内存;目前的一个思路是可以建立“meta的meata”,从而形成多级的索引。这将在后续版本中实现。
  数据的更新和删除采用“无修改”的方式,及无论是对数据进行更新还是删除,都会在meta文件和data文件的末尾追加值,而不会修改原来的数据。这样一来,这了两个文件就会越来越大了。后续版本将会提供数据清理的工具。
  另一个需要解决的问题是更新锁。即以更新为目的获取对象时,将加锁以防止其他处理过程对数据的更新。这是一个潜在的性能瓶颈。后续的版本将以一定的策略对多个更新进行合并,以避免加锁的操作。

2. DataUtil:数据操作类

  DataUtil类是数据处理的主要类。负责数据的读取、更新和删除。

  根据id获取数据get(...)方法的实现如下:
	public static Entity get(long id, boolean forUpdate) throws Exception {
		if (forUpdate) {
			while (locks.contains(id)) {}
			locks.add(id);
		}

		Entity entity = cache.get(id);
		return entity == null ? loadEntity(id) : entity;
	}
  首先,如果是以更新为目的获取数据,那么需要检查更新锁;然后尝试从cache中获取Entity;如果cache中没有数据,则从数据文件中读取。
  读取Entity数据的loadEntity(...)方法的实现如下:
	private static Entity loadEntity(long id) throws Exception {
		Meta meta = Meta.get(id);
		Entity entity = meta.getEntity();

		loadFields(entity, meta);

		entity.setId(id);
		cache.add(entity);

		return FieldUtil.clone(entity);
	}
  先获取Entity的meta(这些meta以HashMap的形式加载到内存),然后创建Entity加载Entity的属性值,设置Entity的id,保存到cache中,clone一份后返回。之所以要进行clone,是为了保证不同的处理过程中对Entity的修改互不影响。
  加载Entity属性的loadFields(...)方法的实现如下:
	private static void loadFields(Entity entity, Meta meta) throws Exception {
		long position = meta.getPosition();
		int size = meta.getSize();
		String content = FileUtil.read(DATA_FILE, position, size);
		parseFields(entity, content);
	}
  获取数据所在的位置和size,从data文件中读取存储的数据的内容,然后进行解析并为Entity的属性赋值。parseFields(...)是FieldUtil中的一个方法,是通过import static方法导入的。

  保存对象的实现如下:
	public static synchronized void save(Entity entity) throws Exception {
		File dataFile = new File(DATA_FILE);
		long position = dataFile.length();
		saveData(entity);
		int size = (int) (dataFile.length() - position);

		Meta.saveMeta(entity, position, size);
		cache.add(entity);
	}
  这个方法是synchronized的,因此同时更新多个对象并保存时需要等待。逻辑是将Entity以一定的规则序列化后保存到data文件中,同时把起始位置和长度保存在meta中,并将entity放入cache。

  删除对象的实现如下:
	public static synchronized void delete(long id) throws Exception {
		Meta.delete(id);
		cache.delete(id);
	}
  该方法也是synchronized的。仅需要id作为参数,向meta中写入一个删除记录,并从cache中将该Entity移除。

3. FieldVisitor:属性访问类

  FieldVistor类仅有两个名称都为eachField的方法,同时还包含了两个接口:
	public static interface Getable {
		public Object get(Field f) throws Exception;
	}

	public static interface Operator {
		public void operate(Field f) throws Exception;
	}
  接口Getable用于封装获取Field值的方法,Operator用于封装对Field进行操作的方法。

  由于第一个eachField(...)方法调用了第二个eachField(...)方法,我们就先看看的二个eachField(...)方法的实现:
	public static void eachField(Class<?> clz, Class<?> upper, Operator operator)
			throws Exception {
		do {
			for (Field f : clz.getDeclaredFields()) {
				f.setAccessible(true);
				operator.operate(f);
			}
			clz = clz.getSuperclass();
		} while (!clz.equals(upper));
	}
  该方法将遍历指定的clz的所有属性,并递归遍历父类属性,直到指定的“上界”为止;遍历的过程中,可以对每个属性进行操作;操作的过程以接口Operator进行封装。
  这是一个对指定类的所有属性进行操作的通用方法。