- 浏览: 289796 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
SpringJava:
摘过来的
小心使用ArrayList和LinkedList -
jingjing0907:
我要成为第一个赞的人!呵呵,
小心使用ArrayList和LinkedList -
SpringJava:
cilendeng 写道应该用ConcurrentHashMa ...
实现单用户登陆session先进先出(session踢出) -
lingxiajiudu:
不错,完美解决我了的问题,赞一个。
子窗体给父窗体传值 javascript opener -
cilendeng:
应该用ConcurrentHashMap
实现单用户登陆session先进先出(session踢出)
到目前为止,我们在 db4o 中创建并操作对象看起来都比较简单 —— 事实上,甚至有点太 简单了。本文中,热心于 db4o 的 Ted Neward 将超越这些简单对象,他将展示简单对象结构化(引用对象的对象)时发生的操作。此外,他还阐述了包括无限递归、层叠行为以及引用一致性在内的一些话题。
一段时间以来,在 面向 Java 开发人员的 db4o 指南 中,我查看了各种使用 db4o 存储 Java 对象的方法,这些方法都不依赖映射文件。 使用原生对象数据库的其中一个优点就是可以避免对象关系映射(也许这不是重点),但我曾用于阐述这种优点的对象模型过于简单,绝大多数企业系统要求创建并操作相当复杂的对象,也称为结构化对象 ,因此本文将讨论结构化对象的创建。
结构化对象 基本上可以看成是一个引用其他对象的对象。尽管 db4o 允许对结构化对象执行所有常用的 CRUD 操作,但是用户却必须承受一定的复杂性。本文将探究一些主要的复杂情况(比如无限递归、层叠行为和引用一致性),以后的文章还将深入探讨更加高级的结构化 对象处理问题。作为补充,我还将介绍探察测试(exploration test) :一种少为人知的可测试类库及 db4o API 的测试技术。
清单 1 重述了我在介绍 db4o 时一直使用的一个简单类 Person
:
package com.tedneward.model; public class Person { public Person() { } public Person(String firstName, String lastName, int age, Mood mood) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.mood = mood; } public String getFirstName() { return firstName; } public void setFirstName(String value) { firstName = value; } public String getLastName() { return lastName; } public void setLastName(String value) { lastName = value; } public int getAge() { return age; } public void setAge(int value) { age = value; } public Mood getMood() { return mood; } public void setMood(Mood value) { mood = value; } public String toString() { return "[Person: " + "firstName = " + firstName + " " + "lastName = " + lastName + " " + "age = " + age + " " + "mood = " + mood + "]"; } public boolean equals(Object rhs) { if (rhs == this) return true; if (!(rhs instanceof Person)) return false; Person other = (Person)rhs; return (this.firstName.equals(other.firstName) && this.lastName.equals(other.lastName) && this.age == other.age); } private String firstName; private String lastName; private int age; private Mood mood; } |
|
这个简单的 Person
类在用于介绍基本 db4o 存储、查询和检索数据操作时行之有效,但它无法满足真实世界中企业编程的复杂性。
举例而言,数据库中的
Person
有家庭地址是很正常的。有些情况下,还可能需要配偶以及子女。
若要在数据库里加一个 “Spouse” 字段,这意味着要扩展
Person
,使它能够引用 Spouse
对象。假设按照某些业务规则,还需要添加一个 Gender
枚举类型及其对应的修改方法,并在构造函数里添加一个 equals()
方法。在清单 2 中,Person
类型有了配偶字段和对应的 get/set 方法对,此时还附带了某些业务规则:
package com.tedneward.model; public class Person { // . . . public Person getSpouse() { return spouse; } public void setSpouse(Person value) { // A few business rules if (spouse != null) throw new IllegalArgumentException("Already married!"); if (value.getSpouse() != null && value.getSpouse() != this) throw new IllegalArgumentException("Already married!"); spouse = value; // Highly sexist business rule if (gender == Gender.FEMALE) this.setLastName(value.getLastName()); // Make marriage reflexive, if it's not already set that way if (value.getSpouse() != this) value.setSpouse(this); } private Person spouse; } |
清单 3 中的代码创建了两个到达婚龄的 Person
,代码和您预想的很接近:
import java.util.*; import com.db4o.*; import com.db4o.query.*; import com.tedneward.model.*; public class App { public static void main(String[] args) throws Exception { ObjectContainer db = null; try { db = Db4o.openFile("persons.data"); Person ben = new Person("Ben", "Galbraith", Gender.MALE, 29, Mood.HAPPY); Person jess = new Person("Jessica", "Smith", Gender.FEMALE, 29, Mood.HAPPY); ben.setSpouse(jess); System.out.println(ben); System.out.println(jess); db.set(ben); db.commit(); List<Person> maleGalbraiths = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return candidate.getLastName().equals("Galbraith") && candidate.getGender().equals(Gender.MALE); } }); for (Person p : maleGalbraiths) { System.out.println("Found " + p); } } finally { if (db != null) db.close(); } } } |
除了讨厌的业务规则之外,有几个重要的情况出现了。首先,当对象 ben
存储到数据库后,OODBMS 除了存储一个对象外,显然还做了其他一些事情。
再次检索 ben
对象时,与之相关的配偶信息不仅已经存储而且还被自动检索。
|
思考一下,这包含了可怕的暗示。尽管可以想见
OODBMS 是如何避免无限递归
的场景,
更恐怖的问题在于,设想一个对象有着对其他
几十个、成百上千个对象的引用,每个引用对象又都有着
其自身对其他对象的引用。不妨考虑一下模型表示子女、双亲等的情景。
仅仅是从数据库中取出一个 Person
就会导致
追溯到所有人类的源头。这意味着在网络上传输大量对象!
幸运的是,除了那些最原始的 OODBMS,几乎所有的 OODBMS 都已解决了这个问题,db4o 也不例外。
考察 db4o 的这个领域是一项棘手的任务,也给了我一个机会 展示一位好友教给我的策略:探察测试 。(感谢 Stu Halloway,据我所知,他是第一个拟定该说法的人。) 探察测试,简要而言,是一系列单元测试,不仅测试待查的库,还可探究 API 以确保库行为与预期一致。该方法具有一个有用的副作用,未来的库版本可以放到探察测试代码中,编译并且测试。如果代码不能编译或者无法通过所有的探察测试,则显然意味着库没有做到向后兼容,您就可以在用于生产系统之前发现这个问题。
对 db4o API 的探察测试使我能够使用一种 “before”
方法来创建数据库并使用 Person
填充数据库,并使用 “after” 方法来删除数据库并消除测试过程中发生的误判(false positive)。若非如此,我将不得不记得每次手工删除 persons.data
文件。
坦白说,我并不相信自己在探索 API 的时候还能每次都记得住。
我在进行 db4o 探察测试时,在控制台模式使用 JUnit 4 测试库。写任何测试代码前,StructuredObjectTest
类如清单 4 所示:
import java.io.*; import java.util.*; import com.db4o.*; import com.db4o.query.*; import com.tedneward.model.*; import org.junit.Before; import org.junit.After; import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.*; public class StructuredObjectsTest { ObjectContainer db; @Before public void prepareDatabase() { db = Db4o.openFile("persons.data"); Person ben = new Person("Ben", "Galbraith", Gender.MALE, 29, Mood.HAPPY); Person jess = new Person("Jessica", "Smith", Gender.FEMALE, 29, Mood.HAPPY); ben.setSpouse(jess); db.set(ben); db.commit(); } @After public void deleteDatabase() { db.close(); new File("persons.data").delete(); } @Test public void testSimpleRetrieval() { List<Person> maleGalbraiths = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return candidate.getLastName().equals("Galbraith") && candidate.getGender().equals(Gender.MALE); } }); // Should only have one in the returned set assertEquals(maleGalbraiths.size(), 1); // (Shouldn't display to the console in a unit test, but this is an // exploration test, not a real unit test) for (Person p : maleGalbraiths) { System.out.println("Found " + p); } } } |
自然,针对这套测试运行 JUnit 测试运行器会生成预计输出:要么是“.”,要么是绿条,这与所选择的测试运行器有关(控制台或 GUI)。注意,一般不赞成向控制台写数据 ——
应该用断言进行验证,而不是用眼球 ——
不过在探察测试里,做断言之前看看得到的数据是个好办法。如果有什么没通过,我总是可以注释掉 System.out.println
调用。(可以自由地添加,以测试您想测试的其他 db4o API 特性。)
从这里开始,假定清单 4 中的测试套件包含了代码示例和测试方法(由方法签名中的
@Test
注释指明。)。
|
|
存储结构化对象很大程度上和以前大部分做法一样:对对象调用 db.set()
,OODBMS 负责其余的工作。对哪个对象调用 set()
并不重要,因为 OODBMS
通过对象标识符(OID)对对象进行了跟踪(参阅 “
面向 Java 开发人员的 db4o 指南:
查询、更新以及一致性
”),因此不会对同一对象进行两次存储。
Retrieving 结构化对象则令我不寒而栗。如果要检索的对象(无论是通过 QBE 或原生查询) 拥有大量对象引用,而每个被引用的对象也有着大量的对象引用,以此类推。这有一点像糟糕的 Ponzi 模式,不是吗?
不管大多数开发者的最初反应(一般是 “不可能 是这样的吧,是吗?”)如何, 无限递归在某种意义上正是 db4o 处理结构化对象的真正方式。事实上,这种方式是绝大多数程序员希望的,因为我们都希望在寻找所创建的对象时,它们正好 “就在那里”。同时,我们也显然不想通过一根线缆获得整个世界的信息,至少不要一次就得到。
db4o 对此采用了折衷的办法,限制所检索的对象数量,使用称为激活深度(activation depth)
的方法,它指明在对象图中进行检索的最低层。换句话说,激活深度表示从根对象中标识的引用总数,db4o 将在查询中遍历根对象并返回结果。在前面的例子中,当检索
Ben
时,默认的激活深度 5 足够用于检索 Jessica
,因为它只需要
仅仅一个引用遍历。任何距离 Ben
超过 5 个引用的对象将无法
被检索到,
它们的引用将置为空。我的工作就是
显式地从数据库激活那些对象,在 ObjectContainer
使用 activate()
方法。
如果要改变默认激活深度,
需要以一种精密的方式,在 Configuration
类(从 db.configure()
返回)中使用 db4o 的 activationDepth()
方法修改默认值。
还有一种方式,可以对每个类配置激活深度。
在清单 5 中,使用 ObjectClass
为 Person
类型配置默认激活深度:
// See ObjectClass for more info Configuration config = Db4o.configure(); ObjectClass oc = config.objectClass("com.tedneward.model.Person"); oc.minimumActivationDepth(10); |
|
|
更新所关注的是另外一个问题:如果在对象图中更新一个对象,但并没有做显式设置,
那么会发生什么?正如最初调用
set()
时,将存储引用了其他存储对象的相关对象,与之相似,
当一个对象传递到
ObjectContainer
,db4o 遍历所有引用,将发现的对象存储到数据库中,如清单 6 所示:
@Test public void testDependentUpdate() { List<Person> maleGalbraiths = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return candidate.getLastName().equals("Galbraith") && candidate.getGender().equals(Gender.MALE); } }); Person ben = maleGalbraiths.get(0); // Happy birthday, Jessica! ben.getSpouse().setAge(ben.getSpouse().getAge() + 1); // We only have a reference to Ben, so store that and commit db.set(ben); db.commit(); // Find Jess, make sure she's 30 Person jess = (Person)db.get( new Person("Jessica", "Galbraith", null, 0, null)).next(); assertTrue(jess.getAge() == 30); } |
尽管已经对 jess
对象做了变动,
ben
对象还拥有对
jess
的引用。因此内存中 jess Person
的更新会保存在数据库中。
其实不是这样。好的,我刚才是在撒谎。
事实是,探察测试在某个地方出问题了,产生了一个误判。
尽管从文档来看并不明显,
ObjectContainer
保持着已激活对象的缓存,
所以当清单 6 中的测试从容器中检索 Jessica
对象时,返回的是
包含变动的内存对象,而不是写到磁盘上真正数据。
这掩盖了一个事实,某类型的默认更新深度
是 1,
意味着只有原语值(包括 String
)才会在调用 set()
时被存储。为了使该行为生效,我必须稍微修改一下测试,如清单 7 所示:
@Test(expected=AssertionError.class) public void testDependentUpdate() { List<Person> maleGalbraiths = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return candidate.getLastName().equals("Galbraith") && candidate.getGender().equals(Gender.MALE); } }); Person ben = maleGalbraiths.get(0); assertTrue(ben.getSpouse().getAge() == 29); // Happy Birthday, Jessica! ben.getSpouse().setAge(ben.getSpouse().getAge() + 1); // We only have a reference to Ben, so store that and commit db.set(ben); db.commit(); // Close the ObjectContainer, then re-open it db.close(); db = Db4o.openFile("persons.data"); // Find Jess, make sure she's 30 Person jess = (Person)db.get( new Person("Jessica", "Galbraith", null, 0, null)).next(); assertTrue(jess.getAge() == 30); } |
测试时,得到 AssertionFailure
,
说明此前有关对象图中层叠展开的对象更新的论断是错误的。(通过将您希望抛出异常的类类型的 @Test
注释的值设置为 expected
,可以使 JUit 提前预测到这种错误。)
Db4o 仅仅返回缓存对象,而不对其更多地进行隐式处理,这是一个有争议的话题。
很多编程人员认为
要么这种行为是有害的并且违反直觉,要么
这种行为正是 OODBMS 应该做的。不要去管这两种观点优劣如何,
重要的是理解数据库的默认行为并且知道如何修正。在清单 8 中,使用 ObjectClass.setCascadeOnUpdate()
方法为一特定类型改变 db4o 的
默认更新动作。不过要注意,在打开 ObjectContainer
之前,必须
设定该方法为 true
。清单 8 展示了修改后的正确的层叠测试。
@Test public void testWorkingDependentUpdate() { // the cascadeOnUpdate() call must be done while the ObjectContainer // isn't open, so close() it, setCascadeOnUpdate, then open() it again db.close(); Db4o.configure().objectClass(Person.class).cascadeOnUpdate(true); db = Db4o.openFile("persons.data"); List<Person> maleGalbraiths = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return candidate.getLastName().equals("Galbraith") && candidate.getGender().equals(Gender.MALE); } }); Person ben = maleGalbraiths.get(0); assertTrue(ben.getSpouse().getAge() == 29); // Happy Birthday, Jessica! ben.getSpouse().setAge(ben.getSpouse().getAge() + 1); // We only have a reference to Ben, so store that and commit db.set(ben); db.commit(); // Close the ObjectContainer, then re-open it db.close(); db = Db4o.openFile("persons.data"); // Find Jess, make sure she's 30 Person jess = (Person)db.get( new Person("Jessica", "Galbraith", null, 0, null)).next(); assertTrue(jess.getAge() == 30); } |
不仅可以为更新设置层叠行为,也可以对检索(创建值为 “unlimited” 的激活深度)和删除设置层叠行为
—— 这是我最新琢磨的 Person
对象的最后一个应用 。
|
|
从数据库中删除对象与检索和更新对象类似: 默认情况下,删除一个对象时,不删除它引用的对象。 一般而言,这也是理想的行为。如清单 9 所示:
@Test public void simpleDeletion() { Person ben = (Person)db.get(new Person("Ben", "Galbraith", null, 0, null)).next(); db.delete(ben); Person jess = (Person)db.get(new Person("Jessica", "Galbraith", null, 0, null)).next(); assertNotNull(jess); } |
但是,有些时候在删除对象时,希望强制删除其引用的对象。
与激活和更新一样,
可以通过调用 Configuration
类触发此行为。如清单 10 所示:
清单 10. Configuration.setCascadeOnDelete()
@Test public void cascadingDeletion() { // the cascadeOnUpdate() call must be done while the ObjectContainer // isn't open, so close() it, setCascadeOnUpdate, then open() it again db.close(); Db4o.configure().objectClass(Person.class).cascadeOnDelete(true); db = Db4o.openFile("persons.data"); Person ben = (Person)db.get(new Person("Ben", "Galbraith", null, 0, null)).next(); db.delete(ben); ObjectSet<Person> results = db.get(new Person("Jessica", "Galbraith", null, 0, null)); assertFalse(results.hasNext()); } |
执行该操作时要小心,因为它意味着其他引用了被消除层叠的对象的对象将拥有一个对 null 的引用 —— db4o 对象数据库在防止删除被引用对象上使用的引用一致性 在这里没有什么作用。 (引用一致性是 db4o 普遍需要的特性,据说开发团队正在考虑在未来某个版本中加入这一特性。对于使用 db4o 的开发人员来说,关键在于要以一种不违反最少意外原则 的方式实现,甚至某些时候, 即使是在关系数据库中, 打破一致性规则实际上也是一种理想的实践。)
|
|
本文是该系列文章的分水岭:在此之前, 我使用的所有示例都基于非常简单的对象,从应用角度来讲, 那些例子都不现实,其主要作用只是为了使您理解 OODBMS,而不是被存储的对象。 理解像 db4o 这样的 OODBMS 是如何通过引用存储相关对象,是比较复杂的事情。 幸运的是,一旦您掌握了这些行为(通过解释和理解),您所要做的就只是 开始调整代码来实现这些行为。
在本文中,您看到了一些基本例子,通过调整复杂代码来实现 db4o 对象模型。 学习了如何对结构化对象执行一些简单 CRUD 操作,同时,也看到了一些 不可避免的问题和解决方法。
其实,目前的结构化对象例子仍然比较简单,
对象之间还只是直接引用关系。
许多夫妻都知道,结婚一段时间后,孩子将会出现。
本系列的下一文章中,我将继续
探索 db4o 中的结构化对象的创建与操作,看看在引入若干子对象后,
ben
和 jess
对象将发生什么。
发表评论
-
栈的简单应用--单词反转
2014-07-03 16:00 695我们知道栈是一种先进后出,也就是后进先出的数据 ... -
java实现简单的栈
2014-07-01 11:56 643栈--只允许访问第一个数据项即:最后插入的数据。最简单的一句 ... -
小心使用ArrayList和LinkedList
2014-06-30 16:32 785ArrayList内部是使用可増长数组实现的,所以是用ge ... -
有趣的Java算法(3)
2014-06-30 16:29 681给定两个排序后的数组A和B,其中A的末端有足够的空间容纳B ... -
有趣的Java算法(2)
2014-06-30 16:29 1057今天分享一个"将一个整数的每位数分解并按逆序输 ... -
有趣的Java算法
2014-06-20 17:00 747题目及源码分析: /* * 今天在BBS里面看到这 ... -
java 值传递 引用传递
2010-12-17 23:11 2065java方法用的是值传递还是引用传递。你在blogjava上还 ... -
用java代码编写堆栈
2010-05-03 17:39 1233public class Stack { int[] ... -
几种读取属性文件的JAVA实现方式
2010-04-30 19:20 1178转载:http://darkranger.iteye.com/ ... -
Site
2010-04-30 19:20 966http://www.szikao.com/computer/ ... -
JAVA对XML的几种解析方式
2010-04-29 19:53 957对于XML介绍比较全面的还是IBM的专栏: ... -
集合与通用集合
2010-04-29 19:44 1430URL: http://www.ibm.com/develop ... -
HashMap 和TreeMap
2010-04-29 19:41 1276本文重点介绍HashMap。首先介绍一下什么是Map。在数组中 ... -
TreeMap和HashMap的问题
2010-04-29 19:39 2090在一次面试的过程 ... -
实现单用户登陆session先进先出(session踢出)
2010-04-29 19:33 9478首先在系统增加sessionListener 来监听sessi ... -
Java单态模式的实现
2010-04-29 19:23 15801.饿汉式:public class Sing ... -
请教java反射机制里可以调用私有方法吗?
2010-04-27 19:17 1626如题:请教java反射机制里可以调用私有方法吗? Metho ... -
利用java反射机制调用类的私有方法
2010-04-27 18:59 14181.将getInstance()方法设置为private ... -
String是原始数据类型还是引用数据类型
2010-04-26 19:22 1711请教各位高手,String是原始数据类型还是引用数据类型?谢谢 ... -
java中堆(heap)和堆栈(stack)有什么区别
2010-04-26 19:13 2195stack 和 heap 都是内存的 ...
相关推荐
**db4o(Database for Objects)**是一款开源的对象数据库,专为Java开发人员设计,它允许开发者直接在数据库中存储和检索Java对象,无需进行传统的SQL映射。本指南的第三部分将深入探讨如何在db4o中进行数据库重构...
面向Java开发人员的db4o指南主要关注如何利用db4o进行结构化对象和集合的管理,从而提高开发效率和数据存储的灵活性。 **对象数据库** 与传统的关系型数据库(RDBMS)不同,对象数据库直接支持面向对象编程模型。在...
面向Java开发人员的db4o指南db4o中的数据库重构
db4o 的安装非常简单,只需要从官方网站下载最新的版本,并在 Eclipse 中新建 Java 项目,将 db4o 对象数据库引擎包 db4o-5.5-java5.jar 导入进项目。 db4o 支持多种版本的 JDK,因此可以根据需要选择合适的 JAR 包...
Java数据库db4o,全称为“Database for Objects”,是一款开源的对象数据库系统,专门设计用于Java和.NET平台。它提供了一种直接在对象模型上进行数据操作的方式,无需传统的ORM(对象关系映射)层,简化了开发过程...
《db4o 权威指南》是一本深入探讨db4o这一开源面向对象数据库系统的专业书籍,对于Java开发者来说尤其有价值。db4o是Database for Objects的缩写,它允许开发者以自然、直观的方式存储和检索Java对象,无需编写SQL...
1.介绍了面向对象数据库 db4o 的基本特性,并且与传统关系型数据库以及 OR 映射技术做了比较分析,读者可以体验到 db4o 的全新的面向对象存储的理念,并且给出了性能测试数据。 2.介绍了面向对象数据库 db4o 的安装...
DB4O面向对象数据库使用指南
DB4O,全称为“Database for Objects”,是一个开源的对象数据库管理系统,专为Java和.NET平台设计。它允许开发者直接在数据库中存储、检索、更新和删除Java对象,无需中间的ORM(对象关系映射)层。这篇文档是DB4O ...
**db4o (Database for Objects) 是一个开源的、基于Java和.NET的面向对象数据库系统。它允许开发者直接在数据库中存储和检索Java或.NET对象,而无需传统的SQL查询语言,极大地简化了数据管理和持久化的流程。** 在...
db4o是一个专为Java和.NET开发者设计的开源、轻量级的纯面向对象数据库引擎,提供了一种简单易用的方式来实现对象持久化。这款数据库引擎已被验证具备优秀的性能,根据描述中的基准测试,db4o在与传统持久化方案的...
**db4o(Database for Objects)** 是一个开源的对象数据库管理系统(Object-Relational Mapping,ORM),它允许开发者直接在Java或.NET平台上存储和检索Java对象或.NET对象,无需进行SQL查询或者映射过程。db4o的...
**db4o 开发指南与实例详解** **一、db4o 简介** db4o(Database for Objects)是一款开源的对象关系数据库管理系统(Object-Relational Mapping, ORM),它允许开发者直接将Java对象存储到数据库中,无需编写SQL...
Db4o,全称为“Database for Objects”,是一个开源的对象数据库管理系统,主要应用于Java和.NET平台。这个项目专注于提供一种简单的方式来存储和检索Java对象,无需SQL或其他中间映射层。在“Db4o的简单操作项目”...
DB4o(Database for Objects)是一款开源的对象数据库系统,它允许开发者将Java或.NET平台上的对象直接存储到数据库中,而无需进行SQL映射。在本文中,我们将深入探讨如何利用DB4o将一个对象保存到文件中,然后进行...
**db4o-7.2-java** 是一个针对Java平台的开源对象数据库系统,它将传统的关系型数据库理念与面向对象编程相结合,为开发者提供了一种高效、灵活的数据存储解决方案。db4o允许开发者直接存取Java对象,而无需通过SQL...
【db4o6.4-java】是一个针对Java平台的数据库引擎开发包,它包含了db4o的源代码、测试代码以及其他相关资源。db4o全称为"Database for Objects",是一个开源的对象数据库管理系统(ODBMS),它允许开发者直接在Java...
db4o(Database for Objects)是一款开源的对象数据库系统,它允许开发者直接将Java或.NET对象存储到数据库中,无需进行ORM(对象关系映射)。db4o的目标是简化数据管理,提供更接近自然编程的方式,使开发过程更加...
描述:这份资料是关于db4o数据库的使用教程,特别针对Java开发人员。db4o是一款开源的对象数据库,支持Java、.NET以及Mono平台。该教程旨在帮助用户快速上手db4o,并在开发过程中提供可靠的支持。 ### 一、db4o概述...
总之,DB4O是Java开发小型桌面应用程序的一个理想选择,它的简单易用和强大的功能使其在对象数据库领域占有一席之地。通过理解和掌握DB4O,开发者可以更高效地构建数据驱动的应用程序,同时享受对象数据库带来的便利...