对象数据库(ODBMS) db4o试用(Java version)

Author: 西门町学士
Email: steve.chengyu at gmail.com
我觉得我现在中了两种毒:Java和SQL。西谚有云:When you have a hammer, everything looks like a nail。只要动手写程序,头脑中不自觉地就public class...其实很多时候一两行简短的scripts就可以了,即使像在Windows上,我们也可以写jscript、wsh脚本。而在资料储存上,关系数据库更是不二法宝。拿到一个项目,我马上就要将它分解成各个Table,我觉得SQL是如此强大和灵活,以至于一见到O/R映射就觉得厌恶,直到现在我也没有碰过Hibernate。
呵呵,抱怨了半天,与其在O/R映射中苦苦挣扎,不如跳出来看看其它的风景,比如:ODBMS(对象数据库)。学士刚下载了db4o这个所谓对象数据库,试着玩了一下,虽尚不知味之甘苦,勉得以一窥豹斑。
db4o目前是Version 5.0,仅需要一个jar文件,我用的支持JDK5的那个:db4o-5.0-java5.jar; 你如果用的其它版本的JDK,有相对应的jar文件。这个jar文件就是数据库,可不要想象成JDBC Driver之类的东东,JDBC Driver是RDBMS(关系数据库)才需要的东东,呵呵。
好了,闲话不说,我们现在试试这个ODBMS。首先,建一个你想要保存的对象,我写了一个非常简单的Student.java:

public class Student {
    private String name;
    private int points;
    
    
    /** Creates a new instance of Student */
    public Student(String name, int points) {
        this.name = name;
        this.points = points;
    }
    
    public String getName() {
        return name;
    }
    
    public int getPoints() {
        return points;
    }
    
    public void addPoints(int points) {
        this.points += points;
    }
    
    public String toString() {
        return name + "/" + points;
    }
}

这个类非常简单,只有两个instance fields,学生姓名和分数,以及一些简单的methods,完全没有用任何跟db4o相关的代码。然后我们就可以将Student的实例放入数据库操作了:InsertObj2Db4o.java:

import java.io.File;
import java.util.List;

import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.query.*;

/**
 *
 * @author Stevech
 */
public class InsertObj2Db4o {
    
    /** Creates a new instance of InsertObj2Db4o */
    public InsertObj2Db4o() {
    }
    
    public static void main(String[] args) {
        ObjectContainer db = Db4o.openFile("student.yap");
        try {
            storeStudents(db);
            retrieveAllStudents(db);
            retrieveStudentByName(db);
            retrieveStudentByPoints(db);
            updateStudent(db);
            deleteStudent(db);
            descendStudents(db);
            retrievePartStudents(db);
        } finally {
            db.close();
        }
    }
    
    public static void storeStudents(ObjectContainer db) {
        Student bg = new Student("Bill Gates", 119);
        Student sm = new Student("Scott McNealy", 102);
        Student sj = new Student("Steve Jobs", 150);
        Student rs = new Student("Richard Stallman", 500);
        Student le = new Student("Larry Elison", 105);
        Student sc = new Student("Steve Cheng", 95);
        
        db.set(bg);
        db.set(sm);
        db.set(sj);
        db.set(rs);
        db.set(le);
        db.set(sc);
        
        System.out.println("Added " + bg + ", " + sm + ", " + sj);
        System.out.println("Added " + rs + ", " + le + ", " + sc);
    }
    
    public static void retrieveAllStudents(ObjectContainer db) {
        ObjectSet<Student> result = db.get(Student.class);
        listResult(result);
    }
    
    public static void retrieveStudentByName(ObjectContainer db) {
        Student proto = new Student("Bill Gates", 0); // Note: 0 is  default value for int
        ObjectSet<Student> result = db.get(proto);
        listResult(result);
    }
    
    public static void retrieveStudentByPoints(ObjectContainer db) {
        Student proto = new Student(null, 500);
        ObjectSet<Student> result = db.get(proto);
        listResult(result);
    }
    
    public static void updateStudent(ObjectContainer db) {
        ObjectSet<Student> result = db.get(new Student("Steve Cheng", 95));
        Student found = result.next();
        found.addPoints(10);
        db.set(found);
        retrieveAllStudents(db);
        System.out.println("Added 10 points for " + found);
    }
    
    public static void deleteStudent(ObjectContainer db) {
        ObjectSet<Student> result = db.get(new Student("Steve Cheng", 0));
        Student found = result.next();
        db.delete(found);
        retrieveAllStudents(db);
        System.out.println("Deleted " + found);
    }
    
    public static void descendStudents(ObjectContainer db) {
        Query query = db.query();
        query.constrain(Student.class);
        Query descendQuery = query.descend("points").orderDescending();
        ObjectSet<Student> result = query.execute();
        listResult(result);
    }
    
    public static void retrievePartStudents(ObjectContainer db) {
        List<Student> result = db.query(new Predicate<Student>() {
            public boolean match(Student s) {
                return s.getPoints() > 120 && s.getPoints() < 500 || s.getName().equals("Bill Gates");
            }
        });
        listResult(result);
    }
    
    public static void listResult(ObjectSet result) {
        //System.out.println(result.size());
        System.out.println("************************************");
        while(result.hasNext()) {
            System.out.println(result.next());
        }
    }
    
    public static void listResult(java.util.List result){
        //System.out.println(result.size());
        System.out.println("************************************");
        for(int x = 0; x < result.size(); x++)
            System.out.println(result.get(x));
    }    
}

我们一点一点地看:
首先,与RDBMS一样,我们需要连接到数据库。db4o可以运行为C/S模式(或者叫Remote模式,就像Oracle, PostgreSQL, MSSQLSERVER, etc),也可以是local模式(或者叫embed模式)。Borland的JDataStore也与此相似,不同的是JDataStore是RDBMS。我们这里用local模式:

        ObjectContainer db = Db4o.openFile("student.yap");

它打开当前目录下的student.yap文件(也即数据库文件),如果没有,就自动新建一个。然后,就该往里面添数据了:(storeStudents method)

        Student bg = new Student("Bill Gates", 159);
    ...
        db.set(bg);

非常地简单。
同样,取出数据也是一样地简单:(retrieveAllStudents method)

        ObjectSet<Student> result = db.get(Student.class);

先告诉db(一个ObjectContainer实例)我们要取出数据的类型是Student,然后所有的Student类型的数据就存在ObjectSet中了。
现实中,绝大多数时候我们只对那些满足特定条件的数据感兴趣,比如说,我们对Bill Gates同学有极大的兴趣:(retrieveStudentByName method)

        Student proto = new Student("Bill Gates", 0); // Note: 0 is  default value for int
        ObjectSet<Student> result = db.get(proto);

我们先建立一个Student的模板,name为Bill Gates,而points为任意值。然后将这个模板递给db即可。值得注意的是这里points值为0,这并不是我们希望Bill Gates同学的分数为鸭蛋,而仅仅因为0是int的默认值,如果这个参数是Object类型,我们这里就会赋予它默认值null(参见 retrieveStudentByPoints method)当指定的参数为默认值时,意味着我们不对它进行任何限制。这给做模板的方法在db4o中称作QBE (Query by Example),由于显而易见的缺点,实际上往往我们使用的是另一种称作Native Queries的方法来选取数据(见后面retrievePartStudents method)
现在,我们觉得Steve Cheng同学太可怜了,因为只有他的分数在100以下,因此我们决定给他加上10分(上帝保佑你碰到这样的教师):(updateStudent method)

        ObjectSet<Student> result = db.get(new Student("Steve Cheng", 95));
        Student found = result.next();
        found.addPoints(10);
        db.set(found);

我们先将Steve Cheng从数据库中取出来放在一个名为found的Student对象中,然后调用Student的instance method: addPoints(int points)来给found加上10分。然后,将found重新放入db中。这就完成了更新操作。
我们再来看看这个班级里的学生。“Oh, my God!” 有人合不上嘴了,“这个班里全是大腕儿级的人物!But wait, 这个叫Steve Cheng的是什么东西啊?竟然跟Bill Gates (RMS,如果说话的人是GNU fellow的话)列在一起。强烈要求废了他!!”顾客就是上帝,我们只得对不起Steve Cheng了,将他从数据库中删除:(deleteStudent method)

        ObjectSet<Student> result = db.get(new Student("Steve Cheng", 0));
        Student found = result.next();
        db.delete(found);

与更新类似,我们先得选出Steve Cheng放到found对象中,然后,db.delete(found),Steve Cheng就从这个子虚乌有的班级中消失了。
好了,剩下的都是英雄好汉,我们按他们各自的分数来给他们排座次(这至少比梁山好汉搞天降蝌蚪文排座次公平):(descendStudents method)

        Query query = db.query();
        query.constrain(Student.class);
        Query descendQuery = query.descend("points").orderDescending();
        ObjectSet<Student> result = query.execute();

这里的做法不同于QBE或是Native Queriy,叫做SODA Query,是属于比较底层的方法,但还是很容易看明白的。虽然没有Native Queriy那么易于使用,但功能却是很强大的。因此在实际中一些地方还是需要用到SODA Query的。
排了座次,我们要从精英之中选幸运儿去参加由HAL公司赞助的夏令营活动了,只有那些成绩介于120到500之间的才能获得这个天上掉的馅儿饼,(< 500,这不是明显排斥RMS嘛),不过只要你是Bill Gates则不管你成绩如何你都能捡这个馅儿饼吃(??):(retrievePartStudents method)

        List<Student> result = db.query(new Predicate<Student>() {
            public boolean match(Student s) {
                return s.getPoints() > 120 && s.getPoints() < 500 || s.getName().equals("Bill Gates");
            }
        });

这是一个典型的Native Query(这可是db4o大书特书引以为傲的东东),Predicate是定义在import com.db4o.query.*里的一个抽象类:
        public abstract class Predicate<ExtentType>extends java.lang.Objectimplements java.io.Serializable
定义了public abstract boolean match(ExtentType candidate)方法。如果这个方法返回true,则candidate被放入result中。而条件语句也是标准的Java语句(如果使用.Net则是标准的.Net语句)(而不是像RDBMS的SQL),这也是db4o自豪的地方。
更多有关db4o的消息,请访问http://www.db4o.com
============================================
PS: 哦,Steve Cheng是谁?嘿嘿,是学士啦
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页