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进行封装。
这是一个对指定类的所有属性进行操作的通用方法。
第一个eachField(...)的实现如下:
public static boolean eachField(final Entity entity, Class<?> upper,
final Getable getable) throws Exception {
final Wrapper<Boolean> warapper = new Wrapper<Boolean>(false);
Operator operator = new Operator() {
public void operate(Field f) throws Exception {
Object obj = getable.get(f);
if (obj == null) {
return;
}
if (obj instanceof OMField<?>) {
OMField<?> omField = (OMField<?>) obj;
omField.setEntity(entity);
warapper.set(true);
}
f.set(entity, obj);
}
};
eachField(entity.getClass(), upper, operator);
return warapper.get();
}
该方法在第二个eachField(...)方法的基础上,实现对属性的赋值,其逻辑为:先利用传入的Getable对象获取属性的值;如果值不为空,检查它是非为OMField,如果为OMField则设置其enttiy,并记录更新;然后对属性进行赋值。
之所以要记录更新,是因为一旦更新了对象,在事务提交时就可能需要将更新过的对象重新保存到数据文件中。
该方法是一个为指定类的属性进行赋值的通用方法。
那么,这些方法是如何被运用的?这就需要看看属性操作类FieldUtil的实现了。
4. FieldUtil:属性操作类
FieldUtil提供对Entity的属性进行操作的方法,包括新创建的Entity的属性的初始化、从Map中解析属性并为Entity的属性赋值、充数据文件保存的数据中解析属性值、获取排序后的属性名称、将指定Entity的属性写入指定的writer中、对Entity进行clone等。这里只举两个例子来说明FieldUtil的实现方式。
首先,是获取排序后的属性值的getSortedFields(...)方法:
final Queue<String> fields = new PriorityQueue<String>();
Operator operator = new Operator() {
public void operate(Field f) {
if (ReflectUtil.isSubclass(f.getType(), OMField.class)) {
fields.offer(f.getName());
}
}
};
eachField(clz, Entity.class, operator);
return fields;
这个方法利用了FieldVisitor的eachField(...)方法,将获取属性值并添加到返回的Queue中的操作封装在Operator中传入,从而实现了对指定类的属性的遍历。排序是通过PriorityQueue本身的特性实现的,同时也限定了被添加的属性的类型必须为OMField的子类。
然后,看看从数据文件中读取属性的parseFields(...)方法:
public static void parseFields(Entity entity, String content)
throws Exception {
Queue<String> fields = getSortedFields(entity.getClass());
for (String value : StringUtil.split(content, '\0')) {
loadField(entity, fields.poll(), value);
}
}
private static void loadField(Entity entity, String field, String value)
throws Exception {
Field f = entity.getClass().getDeclaredField(field);
f.setAccessible(true);
OMField<?> omField = (OMField<?>) parseField(f.getType(), value);
omField.setEntity(entity);
f.set(entity, omField);
}
private static Object parseField(Class<?> type, String value)
throws Exception {
if (ReflectUtil.isSubclass(type, OMField.class)) {
if (value.isEmpty()) {
return type.getConstructor().newInstance();
} else {
return type.getConstructor(String.class).newInstance(value);
}
} else {
return ReflectUtil.parseField(type, value);
}
}
首先,将数据内容按“\0”进行分隔,“\0”这个字符因其特殊性被作为各属性值的分隔符;然后,对于每个属性值,则根据值是否为空,选择调用不同的构造函数实例化(见parseField(Class<?> type, String value));最后,将实例化的Field设置到Entity中。
5. Cache:缓存
Cache的实现比较简单,它用ConcurrentHashMap<Long, Entity>保存了一个Entity的集合,包含add()、delete()等充缓存中增删Entity的方法,以及获取Enity的get(long id)方法。
此外Cache有继承了OMThread,将在程序启动的同时启动一个清理Entity的线程:
protected void doRun() throws Exception {
Thread.sleep(Cfg.cache());
while (true) {
Thread.sleep(Cfg.cache());
clear();
}
}
private void clear() {
long deadLine = System.currentTimeMillis() - Cfg.cache();
Iterator<Entity> it = map.values().iterator();
while (it.hasNext()) {
if (it.next().touched() < deadLine) {
it.remove();
}
}
}
清理的间隔时间可以在Cfg.cfg中进行配置。
6. Meta:元数据/数据索引
Meta类保存了Entity在数据文件中的位置、长度和类型名称,并封装了一些操作meta据文件的方法。
加载meta文件到内存的方法如下:
public static void readMeta() throws Exception {
long max = 1;
BufferedReader reader = FileUtil.reader(META_FILE);
String line;
while ((line = reader.readLine()) != null) {
String[] parts = line.split(",", 2);
long id = Long.parseLong(parts[0]);
max = Math.max(max, id);
if (parts.length == 1) {
metaMap.remove(id);
} else {
metaMap.put(id, new Meta(parts[1]));
}
}
idCounter.set(max);
}
meta数据时分行存储的,每行按“,”分隔,分别为id、position、size和className,内容大致如下:
1,0,1,Database
2,1,25,User
1,26,2,Database
2,28,26,User
1,54,3,Database
3,57,128,Text
4,185,367,Text
5,552,39,Article
1,591,5,Database
7,596,397,Text
通过使用Meta,可以起到延迟加载Entity的目的,但目前的做法是程序一启动就加载所有的Meta,这可能仍然要占用较多空间,目前的思路是采用“多级Meta”或按一定策略延迟加载Meta;如何做到初始加载的数据量最小,同时又能保证读取时的效率,仍是一个难题。
7. 总结
OMToolkit中的数据存储方式还比较初级,在后续版本中还需要持续修改。
如何控制启动时加载的数据量?如何避免数据文件大小的膨胀?如何避免更新锁成为性能瓶颈?除此之外,Database不仅仅是读写数据而已,如何使得排序、筛选的功能更加高效便捷?这些都是需要考虑的。
但是,直接在数据文件中读写Entity,而不是进行SQL解析,这个思路是不会改变的。这一切,都是为了是数据操作的部分与面向对象编程更加有效地结合。
数据存储部分的讲解就到此为止了,接下来的一篇文章将对前面提到的Web Server、Web Framework、Database部分做一个总结;不会有太多的代码,而是计划以图形和原理的讲解为主。
谢谢。(附件是目前最新的 OMSimpleBlog 和 OMToolkit 源码)
分享到:
相关推荐
Java 3: Object-oriented programming Software Development 第一版(2017年,英文版)PDF格式
Systems Analysis and Design: An Object-Oriented Approach with UML, 5th Edition by Dennis, Wixom, and Tegarden captures the dynamic aspects of the field by keeping students focused on doing SAD while ...
If you've used a more traditional object-oriented language, such as C++ or Java, JavaScript probably doesn't seem object-oriented at all. It has no concept of classes, and you don't even need to ...
《Object-Oriented Programming with Object C》是一本深入探讨面向对象编程(OOP)与Objective-C语言的专业书籍。Objective-C是Apple开发的一种强大的、面向对象的编程语言,主要用于iOS和macOS的应用程序开发。这...
4. **微控制器编程实践**:这部分内容可能会涵盖具体的编程实例,展示如何在不同的微控制器平台上实现高效的实时应用。 5. **高级主题探讨**:除此之外,本书还可能涉及一些更高级的主题,例如低功耗设计、多核微...
Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and the Unified Process, Second Edition
It is a rapidly growing and evolving programming methodology that some feel may eventually replace object-orientation as the dominant programming paradigm. The first public release of the AspectJ ...
Object-C:object-c实现的flutter组件.zip
Real-Time C++ Efficient Object-Oriented and Template Microcontroller Programming(3rd) 英文无水印原版pdf 第3版 pdf所有页面使用FoxitReader、PDF-XChangeViewer、SumatraPDF和Firefox测试都可以打开 本...
这是ibm的课件:Mastering Object-Oriented Analysis and Design with UML,中文的在CSDN已有同学上传~建议中英文对照着看,因为中文有的翻译不是很恰当
Maximizing ASP.NET Real World, Object-Oriented Development(2005)
Upon completion of an object-oriented design, you are faced with a troubling question: "Is it good, bad, or somewhere in between?" Seasoned experts often answer this question by subjecting the design ...
About This Book This book has been updated to cover all the new object-oriented features introduced in ECMAScript 6 It makes object-oriented programming accessible and understandable to web ...
Data Structures And Algorithms With Object-oriented Design Patterns In Java.chm
设计模式:可复用面向对象软件的基础(中文)Design Patterns:Elements of Reusable Object-Oriented software
1. **面向对象的基本概念**:介绍面向对象编程的基础理论,包括类、对象、继承、封装、多态等核心概念。 2. **统一建模语言(UML)**:详细讲解UML的各种图示方法及其应用场景,如用例图、类图、顺序图、状态机图等。 ...
《面向对象思维过程,第五版》是一本由Matt Weisfeld所著的面向对象编程(Object-Oriented Programming,OOP)方法论的书籍。这本书对于理解面向对象的设计和编程提供了深入浅出的指导,旨在帮助读者理解如何运用...