Db4O数据库之运用(原创),请大家多批评指教.

Db4O数据库之运用(原创)

文/junglesong

注:欢迎与我讨论(junglesong@gmail.com)

Db4O全名DataBase For Object,是一个对象数据产品,它的数据输入输出全是对象,完全消除了以前面向对象的系统和关系型数据库之间阻抗不匹配的问题,也省却了OR Mapping工具的烦琐配置,是我们更能把注意力集中到项目上.关于Db4O的细节可以参考这个网址:http://developer.db4o.com/blogs/chinese/,或者直接去其大本营http://www.db4o.com/.

一.如何在项目中使用Db4O.
这非常简单,到Db4O大本营http://www.db4o.com/下载最新的开发包并加入自己的Lib即可,我选择的是db4o-5.2-java1.2.jar.
以下将对我在项目中使用Db4O的过程进行一下讲述.

二.构建DataBaseForObject基本类.
构建DataBaseForObject基本类的基本目的是为调用DB4O的个个Service类提供统一的借口,它是Adapter模式的一种体现.
此外,为加快查询速度,我还加入了Jakerta的对象池支持.
public class Db4OFactory implements PoolableObjectFactory {
private static String DataFilePath = "";

public static final String DataFileName = "DBFile.dat";

public Object makeObject() throws Exception {
Object obj = Db4o.openFile(DataFilePath);
//System.err.println("Making Object " + obj);
return obj;
}

public void activateObject(Object obj) throws Exception {
//System.err.println("Activating Object " + obj);
}

public void passivateObject(Object obj) throws Exception {
//System.err.println("Passivating Object " + obj);
}

public boolean validateObject(Object obj) {
return true;
}

public void destroyObject(Object obj) throws Exception {
ObjectContainer db=(ObjectContainer)obj;
db.close();
//System.err.print ln("Destroying Object " + obj);
}

public static void setDataFilePath(String dataFilePath) {
DataFilePath = dataFilePath;
}

public static String getDataFilePath() {
return DataFilePath;
}
}

下面是其主要方法:
public class DataBaseForObject {
// 删除一个对象
public static synchronized void remove(Db4OStorable obj) {
PoolableObjectFactory factory = new Db4OFactory();
ObjectPool pool = new StackObjectPool(factory);
ObjectContainer db = null;

try {
db = (ObjectContainer) pool.borrowObject();

Query query = db.query();
query.descend("id").constrain(obj.getId());

ObjectSet objSet = query.execute();
if (objSet.hasNext()) {
db.delete(objSet.next());
}

db.commit();
db = null;

} catch (Exception e) {
System.out.println("从Db4Obj数据库删除对象错误!");
e.printStackTrace();
} finally {
try {
if (db != null) {// 避免将一个对象归还两次
pool.returnObject(db);
}
pool.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

// 存储或更新一个对象
public static void save(Class objClass, Db4OStorable obj) {
PoolableObjectFactory factory = new Db4OFactory();
ObjectPool pool = new StackObjectPool(factory);
ObjectContainer db = null;

try {
db = (ObjectContainer) pool.borrowObject();

Query query = db.query();
query.constrain(objClass);
query.descend("id").constrain(obj.getId());

ObjectSet objSet = query.execute();

if (objSet.hasNext()) {
Db4OStorable found = (Db4OStorable) objSet.next();
found.update(obj);
db.set(found);
} else {
db.set(obj);
}
db.commit();
db = null;

} catch (Exception e) {
System.out.println("从Db4Obj数据库更新对象错误!");
e.printStackTrace();
} finally {
try {
if (db != null) {// 避免将一个对象归还两次
pool.returnObject(db);
}
pool.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

// 进行对象查询
public static ArrayList searchBy(Predicate predicate) {
PoolableObjectFactory factory= new Db4OFactory();
ObjectPool pool = new StackObjectPool(factory);
ObjectContainer db = null;

ArrayList retval = new ArrayList();

try {
db=(ObjectContainer)pool.borrowObject();

ObjectSet objSet= db.query(predicate);

while (objSet.hasNext()) {
Object obj = objSet.next();
retval.add(obj);
}

db=null;
} catch (Exception e) {
System.out.println("从Db4Obj数据库查询对象错误!");
e.printStackTrace();
} finally {
try {
if (db != null) {// 避免将一个对象归还两次
pool.returnObject(db);
}
pool.close();
} catch (Exception e) {
e.printStackTrace();
}
}

return retval;
}

}

使用这三个函数就可以对各种对象进行CRUD操作了.

为了区别同类型的不同对象,它们需要唯一的标志Id,所以我让每种需要进行持久化的类都实现了Db4OStorable接口,代码如下:
public interface Db4OStorable{
public String getId() ;
public void setId(String id) ;
public void update(Object obj);
}

这样一来,各个类就有了统一的接口,代码就可以进行统一实现了,避免为一个类加一种判断.

二.构建各Service类.
为了实现对各种类型的不同处理,我们需要为每种对象提供一个Service类,让它来充当DB4O数据与对象之间的桥梁作用,这些类可以根据进行各种复杂的处理而不影响DataBaseForObject 的纯洁性.

下面是一个Service类的示例:

public class ProjectService{
public static boolean save(Project project) {
DataBaseForObject.save(Project.class,project);
return true;
}

public static boolean delete(Project project){
project.getFile().delete();
DataBaseForObject.remove(project);
return true;
}

public static Project getObjById(String id) {

final String Id=id;
Predicate predicate = new Predicate() {
public boolean match(Project prj) {
boolean retval = prj.getId().equals(Id);
return retval;
}
};

ArrayList list=DataBaseForObject.searchBy(predicate);

if(list.size()==0){
return null;
}

Project prj=(Project)list.get(0);
return prj;
}

public static ArrayList getAllDatum(Predicate predicate,SortBean sortBean){
ArrayList list=DataBaseForObject.searchBy(predicate);
Collections.sort(list,new ProjectComarator(sortBean));
return list;
}
}

可以看出,上述四个方法对于Project对象的CRUD操作更加方便了.

三.对象的具体操作.
1)Create创建操作
Project project=new Project();
project.setId(Util.getIdbyTime());// 注意这里是根据时间取出唯一ID,还可以有别的规则
project.setXXX(xxx);
.......
ProjectService.save(project);

2)Delete删除操作
ProjectService.delete(project);

3)Update更新操作
project.setXXX(xxx);
.......
ProjectService.save(project);
这里要多说几句,更新操作首先要ID一致,这里就不能改编ID值,否则就变成了创建;其次更新操作依赖于对象对Db4OStorable的update方法的实现,彻底更新一个对象需要用新对象的所有成员替代旧对象的所有成员,下面是我的一个实现例子:
public void update(Object obj){
Project another=(Project)obj;

this.id=another.id;
this.pid=another.pid;
this.path=another.path;
this.title=another.title;
this.type=another.type;
this.status=another.status;
this.concept=another.concept;
this.creator=another.creator;
this.chief=another.chief;
this.members=another.members;
this.file=another.file;
}
这个方法没实现完全的话,对象不能完全更新,切记.

4)Search查询操作.
DB4O提供了多种查询方式,经过一段时间后我决定采用精确灵活的predicate查询方式,
请看其使用:
Predicate myprojectsPredicate = new Predicate() {
public boolean match(Project prj) {
boolean retval = true;

retval = retval
&& (prj.getType().equals(Consts.ProjectType_Project));
retval = retval && (prj.getPid().equals("0"));
retval = retval
&& (prj.getChief().equals(Username) || Util.strContain(
prj.getMembers(), Username));

return retval;
}
};
ArrayList projectResult = (ArrayList) ProjectService.getAllDatum(myprojectsPredicate, new SortBean("title","asc"));

很明显,由于查询方式是Java语言,它更自然,易懂和容易修改,而且不会把业务耦合到SQL中,相对冗长复杂的SQL要强很多,比HibernateHQL也简单不少.
不过美中不足的是数据的排序,我们需要一个实现了Comparator接口的类来实现排序,下面是我的实现过程,如果你对Comparator比较熟悉的话可以掠过.
你也可以不写得这样负复杂.

public class SortBean{
private String field;
private String order;

public SortBean(String field,String order){
this.field=field;
this.order=order;
}

public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getOrder() {
return order;
}
public void setOrder(String order) {
this.order = order;
}
}

public final class ProjectComarator implements Comparator{
private SortBean sortBean;

public ProjectComarator(SortBean sortBean){
this.sortBean=sortBean;
}

public int compare(Object arg0, Object arg1) {
Project op1=(Project)arg0;
Project op2=(Project)arg1;

if(sortBean==null){
return op2.getId().compareTo(op1.getId());
}

if(sortBean.getField().equals("title") && sortBean.getOrder().equals("asc")){
return op1.getTitle().compareTo(op2.getTitle());
}
else if(sortBean.getField().equals("title") && sortBean.getOrder().equals("desc")){
return op2.getTitle().compareTo(op1.getTitle());
}
else if(sortBean.getField().equals("type") && sortBean.getOrder().equals("asc")){
return op1.getType().compareTo(op2.getType());
}
else if(sortBean.getField().equals("type") && sortBean.getOrder().equals("desc")){
return op2.getType().compareTo(op1.getType());
}
else if(sortBean.getField().equals("status") && sortBean.getOrder().equals("asc")){
return op1.getStatus().compareTo(op2.getStatus());
}
else if(sortBean.getField().equals("status") && sortBean.getOrder().equals("desc")){
return op2.getStatus().compareTo(op1.getStatus());
}
else if(sortBean.getField().equals("concept") && sortBean.getOrder().equals("asc")){
return op1.getConcept().compareTo(op2.getConcept());
}
else if(sortBean.getField().equals("concept") && sortBean.getOrder().equals("desc")){
return op2.getConcept().compareTo(op1.getConcept());
}
else if(sortBean.getField().equals("creator") && sortBean.getOrder().equals("asc")){
return op1.getCreator().compareTo(op2.getCreator());
}
else if(sortBean.getField().equals("creator") && sortBean.getOrder().equals("desc")){
return op2.getCreator().compareTo(op1.getCreator());
}
else if(sortBean.getField().equals("chief") && sortBean.getOrder().equals("asc")){
return op1.getChief().compareTo(op2.getChief());
}
else if(sortBean.getField().equals("chief") && sortBean.getOrder().equals("desc")){
return op2.getChief().compareTo(op1.getChief());
}
else if(sortBean.getField().equals("members") && sortBean.getOrder().equals("asc")){
return op1.getMembers().compareTo(op2.getMembers());
}
else if(sortBean.getField().equals("members") && sortBean.getOrder().equals("desc")){
return op2.getMembers().compareTo(op1.getMembers());
}
else if(sortBean.getField().equals("id") && sortBean.getOrder().equals("asc")){
return op1.getId().compareTo(op2.getId());
}
else if(sortBean.getField().equals("id") && sortBean.getOrder().equals("desc")){
return op2.getId().compareTo(op1.getId());
}
else{
return op2.getId().compareTo(op1.getId());
}
}

public SortBean getSortBean() {
return sortBean;
}

public void setSortBean(SortBean sortBean) {
this.sortBean = sortBean;
}
}

从以上查询代码可以看出,我们的业务全都在Java语言中实现,不需要Sql语句.这对系统的可移植性可修改性都有很好的帮助.


上面就是我在当前项目中使用DB4O对象数据库的全部过程,请大家多批评指教.

总得来说值得肯定,试图简化ORM的配置,关键是,ORM模式中有一个类关联的持久化,这是必须实现的,Hibernate也就是这块复杂了,没有办法啊,你是怎么解决的呢?

首先感谢Banq大哥的能指点敝贴.

关于类关联我没有特别的解决办法,基本是用Java代码分步实现的,比如人员-部门-公司这三个;

类时,查询人所在的公司我是先查人所在的部门,再查部门所在的公司,每个下级类存储一个paren

tID记住其上级类,当然这个标记是唯一的.再比如多个公司年龄小于30的员工数我是把各个公司

小于30的员工数累加起来.
对于类关联我没想太多,因为我觉得ORM,SQL都要实现这样日然的过程,不同的是他们简单方便,我

的复杂些;但我的业务流程却清晰些,也更容易被修改.您认为呢?

说明你在这方面思考过,已经不简单了,鼓励ing.

我讲的类关联除了1:N普通关联外,还有一种聚合,按照OO设计方式或Evans DDD的方法,我们建立一个模型可能必然聚合一些子部分,比如窗户这个模型对象,内部必然由玻璃和窗筐子,汽车模型有子对象车轮 发动机,这些都是高聚合形式存在父对象中,如何将整体和部分的聚合关系持久化到数据库是一个关键。

谢谢夸奖,很多想法都是从您这里学的,真是获益非浅.其中不对的地方还请批评.

我觉得聚合的话直接对窗户或汽车进行存取就可以了,它的内部成员会直接带出带入,不需要额外的处理,很方便的.这一点DB4O确实做得好.

我的项目中Project带附件就是在Project里面放了一个NetFile成员这么做的,存取一个Project的实例就等于存取了一个NetFile的示例,没有多余处理.