- 浏览: 289790 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
SpringJava:
摘过来的
小心使用ArrayList和LinkedList -
jingjing0907:
我要成为第一个赞的人!呵呵,
小心使用ArrayList和LinkedList -
SpringJava:
cilendeng 写道应该用ConcurrentHashMa ...
实现单用户登陆session先进先出(session踢出) -
lingxiajiudu:
不错,完美解决我了的问题,赞一个。
子窗体给父窗体传值 javascript opener -
cilendeng:
应该用ConcurrentHashMap
实现单用户登陆session先进先出(session踢出)
尽管 RDBMS 使用 SQL 作为其查询和检索数据的主要机制,但是 OODBMS 可以使用一些不同的机制。在本系列的第二期文章中,Ted Neward 将介绍一些新方法,包括 Query by Example 以及定制只有 OODBMS 才具有的机制。正如他解释的一样,有些替代方法比 SQL 本身更易于使用。
在 本系列的第一篇文章 中,我讨论了 RDBMS 作为 Java™ 对象存储解决方案的不足之处。正如我所说的,在当今的面向对象世界里,与关系数据库相比,db4o 这样的对象数据库可以为面向对象开发人员提供更多的功能。
|
在本文 及以后的文章中,我将继续介绍对象数据库。我将使用示例来演示这种存储系统的强大之处,它尽可能实现与面向对象编程语言中(本例中为 Java 编程语言)使用的实体形式相同。特别是,我将介绍用于检索、修改并将对象重新存储到 db4o 的各种可用机制。正如您将了解的一样,当您从 SQL 的限制解脱出来后,会对自己能够完成这么多的事情而感到吃惊。
如果您还没有下载 db4o,可能希望 立即下载 。您需要使用它来编译示例。
Query by Example(QBE)是一种数据库查询语言,它允许您通过设计模板(对其进行比较)来创建查询,而不是通过使用谓词条件的语言(如 SQL)。上一次我使用了 db4o 的 QBE 引擎演示了数据检索,这里将快速回顾一下。首先看一下这个绝对简单的数据库。它由一种类型组成,清单 1 列出了其定义:
package com.tedneward.model; public class Person { public Person() { } public Person(String firstName, String lastName, int age) { this.firstName = firstName; this.lastName = lastName; this.age = age; } 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 String toString() { return "[Person: " + "firstName = " + firstName + " " + "lastName = " + lastName + " " + "age = " + age + "]"; } 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; } |
与 POJO 类似,Person
并不是一个复杂的类。它由三个字段和一些基本的支持类似 POJO 行为的方法组成,即 toString()
和 equals()
。(阅读过 Joshua Bloch 的
Effective Java
的读者将注意到我忽略了 hashCode()
实现,很明显这违背了 Rule 8。作者经常使用的典型说法就是,我将 hashCode()
留给 “读者进行练习”,这通常意味着作者不想解释或认为没有必要提供手头的示例。我同样将它留给读者作为练习,请自行判断我们这里的练习属于哪种情况。
在清单 2 中,我创建了 6 个对象,将它们放入了一个文件中,然后使用 QBE 调用名字匹配 “Brian” 模式的两个对象。这种查询使用原型对象(被传入到 get()
调用的对象)来确定对象是否匹配数据库查询,并返回匹配条件的对象的 ObjectSet
(实际上是一个集合)。
import com.db4o.*;
import com.tedneward.model.*;
public class Hellodb4o
{
public static void main(String[] args)
throws Exception
{
ObjectContainer db = null;
try
{
db = Db4o.openFile("persons.data");
Person brian = new Person("Brian", "Goetz", 39);
Person jason = new Person("Jason", "Hunter", 35);
Person clinton = new Person("Brian", "Sletten", 38);
Person david = new Person("David", "Geary", 55);
Person glenn = new Person("Glenn", "Vanderberg", 40);
Person neal = new Person("Neal", "Ford", 39);
db.set(brian);
db.set(jason);
db.set(clinton);
db.set(david);
db.set(glenn);
db.set(neal);
db.commit();
// Find all the Brians
ObjectSet brians = db.get(new Person("Brian", null, 0));
while (brians.hasNext())
System.out.println(brians.next());
}
finally
{
if (db != null)
db.close();
}
}
}
|
由于 QBE 使用原型对象作为其模板来搜索数据,关于其用法有一些简单的规则。当 db4o 针对给定的目标(概念正确但实际实现进行了简化)搜索所有 Person
类型的对象时,要确定数据存储中的某个对象是否满足条件,需要逐个比较字段值。如果原型中的字段值为
“null”,则该值匹配数据存储中的任何值;否则的话,必须精确地匹配值。对于原语类型,由于它不能真正具有 “null” 值,所以使用 0
作为通配符值。(这同样指出了 QBE 方法的一个缺点 —— 不能够有效地使用 0
作为搜索值)。应该指定多个字段值,所有字段的值都应该被数据库中的对象满足,从而使候选对象满足查询条件;实际上,这意味着将字段使用 “AND”
连接起来形成查询谓词。
在前面的示例中,查询所有 firstName
字段等于 “Brian” 的 Person
类型,并且有效地忽略 lastName
和 age
字段。在表中,这个调用基本上相当于 SQL 查询的 SELECT * FROM Person WHERE firstName =
"Brian"
。(虽然如此,在尝试将 OODBMS 查询映射到 SQL 时还是要谨慎一些:这种类比并不完善,并且会对特定查询的性质和性能产生误解)。
查询返回的对象是一个 ObjectSet
,它类似于一个 JDBC ResultSet
(一个简单的对象容器)。使用由 ObjectSet
实现的 Iterator
接口遍历结果非常简单。使用 Person
的特定方法需要对 next()
返回的对象进行向下转换。
|
|
虽然简单的显示数据只和数据本身有关,大多数对象需要进行修改并重新存入数据库中。这可能是使用 OODBMS 最棘手的部分,因为对象数据库使用与关系数据库不同的一致性概念。实际上,这意味着在使用对象数据库时,必须更加谨慎地比较内存中的对象和存储中的对象。
清单 3 所示的简单示例演示了这种不同的一致性概念:
import com.db4o.*; import com.tedneward.model.*; public class Hellodb4o { public static void main(String[] args) throws Exception { ObjectContainer db = null; try { db = Db4o.openFile("persons.data"); Person brian = new Person("Brian", "Goetz", 39); Person jason = new Person("Jason", "Hunter", 35); Person clinton = new Person("Brian", "Sletten", 38); Person david = new Person("David", "Geary", 55); Person glenn = new Person("Glenn", "Vanderberg", 40); Person neal = new Person("Neal", "Ford", 39); db.set(brian); db.set(jason); db.set(clinton); db.set(david); db.set(glenn); db.set(neal); db.commit(); // Find all the Brians ObjectSet brians = db.get(new Person("Brian", null, 0)); while (brians.hasNext()) System.out.println(brians.next()); Person brian2 = new Person("Brian", "Goetz", 39); db.set(brian2); db.commit(); // Find all the Brians ObjectSet brians = db.get(new Person("Brian", null, 0)); while (brians.hasNext()) System.out.println(brians.next()); } finally { if (db != null) db.close(); } } } |
当运行清单 3 中的查询时,数据库将报告三个 Brian
,其中两个是 Brian Goetz。(如果 persons.data
文件已经存在于当前的目录,则会出现类似的结果 —— 创建的所有 Person
将被存储到 persons.data
文件,而查询将返回存储在其中的所有 Brian
)。
|
很明显,这里并不强制使用关于主键的旧规则,那么对象数据库如何处理惟一性概念?
当对象被存储到对象数据库中,将创建一个惟一键,称为 Object identifier
或 OID
(其发音类似于 avoid
的最后一个音节),它惟一地标识对象。OID,和 C# 和 Java 编程中的 this
指针/引用类似,除非显式指定,否则则是隐式的。在 db4o 中,可以通过调用 db.ext().getID()
查找给定对象的 OID。(还可以使用 db.ext().getByID()
方法按照 OID 检索对象。调用该方法具有一些非常复杂的含义,不便在这里讨论,但是它仍然是一种方法)。
在实践中,所有这些意味着由开发人员判断是否一个对象曾经存在于系统中,通常在插入对象前通过查询该对象的容器实现,如清单 4 所示:
// ... as before ObjectContainer db = null; try { db = Db4o.openFile("persons.data"); ... // We want to add Brian Goetz to the database; is he already there? if (db.get(new Person("Brian", "Goetz", 0).hasNext() == false) { // Nope, no Brian Goetz here, go ahead and add him db.set(new Person("Brian", "Goetz", 39)); db.commit(); } } |
在这个特定例子中,假设系统中 Person
的惟一性是其姓名的组合。因此,当在数据库中搜索 Brian
时,只需要对 Person
实例查找这些属性。(或许几年前已经添加过 Brain —— 当时他还不到 39 岁。)
如果希望修改数据库中的对象,那么从容器中检索对象,使用某种方式进行修改,然后将其存储回数据库即可,如图 5 所示:
// ... as before ObjectContainer db = null; try { db = Db4o.openFile("persons.data"); ... // Happy Birthday, David Geary! if ((ObjectSet set = db.get(new Person("David", "Geary", 0))).hasNext()) { Person davidG = (Person)set.next(); davidG.setAge(davidG.getAge() + 1); db.set(davidG); db.commit(); } else throw new MissingPersonsException( "David Geary doesn't seem to be in the database"); } |
db4o 容器在这里并没有出现一致性问题,这是因为有问题的对象已经被标识为来自数据库的对象,即它的 OID 已经被存储在 db4o bookkeeping 基础设施中。相应地,当调用 set
时,db4o 将会更新现有的对象而不是插入新对象。
|
|
特 定于应用程序主键的概念值得经常注意,即使它没有继承 QBE 的概念。您所需要的是使用一种实用方法简化基于标识的搜索。这一节将展示基于 Reflection API 用法的解决方案,我们会将正确的值放在正确的字段,此外,还介绍了针对不同选择和外观对解决方案进行调优的方法。
让我们从一个基本前提开始:我具有一个数据库,其中包含我希望根据一组具有特定值的字段查询的类型(Person
)。在这种方法中,我对 Class
使用了 Reflection API,创建了该类型的新实例(调用其默认构造方法)。然后遍历具有这些字段的 String 数组,取回 Class
中的每个 Field
对象。随后,遍历对应于每个字段值的对象数组,然后调用 Field.set()
将该值放入我的模板对象中。
完成这些操作后,对 db4o
数据库调用 get()
并查看返回的 ObjectSet
是否包含任何对象。这给出了一个基本方法的大概轮廓,如清单 6 所示:
import java.lang.reflect.*; import com.db4o.*; public class Util { public static boolean identitySearch(ObjectContainer db, Class type, String[] fields, Object[] values) throws InstantiationException, IllegalAccessException, NoSuchFieldException { // Create an instance of our type Object template = type.newInstance(); // Populate its fields with the passed-in template values for (int i=0; i<fields.length; i++) { Field f = type.getDeclaredField(fields[i]); if (f == null) throw new IllegalArgumentException("Field " + fields[i] + " not found on type " + type); if (Modifier.isStatic(f.getModifiers())) throw new IllegalArgumentException("Field " + fields[i] + " is a static field and cannot be used in a QBE query"); f.setAccessible(true); f.set(template, values[i]); } // Do the query ObjectSet set = db.get(template); if (set.hasNext()) return true; else return false; } } |
很明显,要对这种方法进行大量的调优以进行尝试,例如捕获所有的异常类型并将它们作为运行时异常重新抛出,或者返回 ObjectSet
本身(而非 true
/false
),甚至返回包含 ObjectSet
内容的数组对象(ObjectSet
的内容使得查看返回数组的长度非常简单)。然而,可以从清单 7 中很明显地看到,这种用法并没有比基本的 QBE 版本简单多少。
// Is Brian already in the database? if (Util.identitySearch( db, Person.class, {"firstName", "lastName"}, {"Brian", "Goetz"}) == false) { db.set(new Person("Brian", "Goetz", 39)); db.commit(); } |
事实上,对于存储的类本身,这种实用方法的实用性 开始变得明显,如清单 8 所示:
public class Person { // ... as before public static boolean exists(ObjectContainer db, Person instance) { return (Util.identitySearch(db, Person.class, {"firstName", "lastName"}, {instance.getFirstName(), instance.getLastName()}); } } |
或者,您可以调整该方法来返回找到的实例,这样 Person
实例使它的 OID 正确地关联,等等。关键要记住可以在 db4o 基础架构之上构建方便的方法,从而使 db4o 更加易于使用。
注意:使用 db4o SODA 查询 API 对存储在磁盘的底层对象执行这类查询是一种更有效的方法,但这稍微超出了本文讨论的范围,所以我将在以后讨论这些内容。
|
|
目前为止,您已经了解了如何查询单个的或者满足特定条件的对象。尽管这使得查询非常简单,但同时也有一些限制:比如,如果需要检索所有姓氏以 G
开头的 Person
,或者所有年龄大于 21 的 Person
,该怎么办?QBE 方法对于这类查询无能为力,因为 QBE 只能执行相等匹配,而无法进行比较查询。
通常,即使是中等复杂程度的比较对于 OODBMS 也是一种弱点,而这正是关系模型和 SQL 的长处。在 SQL 中执行比较查询非常简单,但是在 OODBMS 中执行同样的查询却需要一些不是很吸引人的方法:
- 获取所有对象并自行执行关系比较。
- 扩展 QBE API 以包含谓词。
- 创建一种能够被转换为查询您的对象模型的查询语言。
很 明显,上面所述的第一种方法只能用于最普通的数据库,因为它对能够在实际中使用的数据库的规模有很明显的上限。取回一百万个对象不成问题,甚至可以很轻松 地处理最困难的硬件,尤其是当跨越网络连接时。(这不是对 OODBMS 的控告,顺便提一下,通过网络连接获取一百万行可能仍在 RDBMS 服务器能力之内,但是这将摧毁它所在的网络。)
第二种方法破坏了 QBE 方法的简单性,并且导致了如清单 9 所示的糟糕代码:
Query q = new Query(); q.setClass(Person.class); q.setPredicate(new Predicate( new And( new Equals(new Field("firstName"), "David"), new GreaterThan(new Field("age"), 21) ))); q.Execute(); |
很容易看出,使用这种技术使得中等复杂的查询很快就变得不能工作,尤其是与 SQL 这类查询语言的简单性相比。
第三种方法是创建能够用来查询数据库对象模型的查询语言。过去,OODBMS 开发人员创建了一种标准的查询语言,对象查询语言(Object Query Language),或 OQL,这种语言类似于清单 10 显示的内容:
SELECT p FROM Person WHERE p.firstName = "David" AND p.age > 21 |
表面上看,OQL 非常类似于 SQL,因此它应该和 SQL 一样强大并且易于使用。OQL 的缺点就是它要求返回……什么?类似于 SQL 的语言要求返回列集(元组),与 SQL 相同,但是对象数据库不会以这种方式工作 —— 它希望返回对象,而不是随机集。尤其是在强类型语言中,如 C# 或 Java 编程语言,这些对象类型必须是先验 的,而与 SQL 那种基于集合的概念不同。
|
|
db4o 没有强制开发人员使用复杂的查询 API,也没有引入新的 “-QL” 之类的东西,它提供了一个名为原生查询 的工具,该工具功能强大且易用,如清单 11 所示。(db4o 的查询 API 可以 使用 SODA 形式,这种形式主要用于细粒度查询控制。然而,正如在第二篇看到的一样,SODA 通常只用于手动优化查询。
// ... as before ObjectContainer db = null; try { db = Db4o.openFile("persons.data"); ... // Who wants to get a beer? List<Person> drinkers = db.query(new Predicate<Person>() { public boolean match(Person candidate) { return person.getAge() > 21; } } for (Person drinker : drinkers) System.out.println("Here's your beer, " + person.getFirstName()); } |
之所以说查询是 “原生” 的,是因为它是使用编程语言本身编写的(本例为 Java 语言),而不是必须要转换为其他内容的任意语言。(Predicate API 的非通用版本可用于 Java 5 之前的版本,尽管它使用起来不是很简便。)
考虑一下这一点,您很可能想知道如何精确地实现这种特殊的方法。必须使用源预处理程序将源文件及其包含的查询转换为数据库引擎能够理解的内容(a la
SQL/J
或其他嵌入的预处理程序),或者数据库将所有的 Person
对象发回给对全部集合执行谓词的客户机(换言之,正是早先拒绝使用的方法)
结果证明,db4o 并没有执行任何这些操作;相反,db4o 的基本原理采用有趣并创新的方法进行原生查询。db4o 系统将谓词发送到数据库,在运行时对 match()
的字节码执行字节码分析。如果字节码简单到可以理解的话,db4o 将该查询转换为 SODA 查询以提高效率,这种情况下不需要对传送到 match()
方法的所有对象进行实例化。使用这种方法,程序员可以继续使用他们觉得方便的语言编写查询,而查询本身可以被转换为数据库能够理解并有效执行的内容。(如
果愿意的话,可以称之为 “JQL”—— Java Query Language,不过请不要向 db4o
开发人员转述这个名字,这会给我带来麻烦)。
|
原生查询方法并不是完美的。比如,编写一个足够复杂的原生查询来超越字节码分析器是完全不可能的,因此需要最坏情况的执行模型。在这种最坏情况的场景中,db4o 必须实例化数据库中查询类型的每一个对象,并通过 match()
实现传送每个对象。可以预料到,这将有损查询性能,不过可以在需要的位置安装监听器来解决这一问题。
预见错误并进行优化,直觉并不总是够用,因为代码暗示的原因完全不同。比如,包含一个控制台打印语句(Java 代码中的 System.out.println
,或者 C# 中的 System.Console.WriteLine
)将使 db4o 的 .NET 版本中的优化器发生错误,而 Java 版本则能够对该语句优化。您不能够真正预见这种类型的变化(尽管可以通过经验了解这种变化),所以,最好让系统告诉您发生的错误,正如在极限编程中一样。
简单地对 ObjectContainer
本身注册一个监听器(Db4oQueryExecutionListener
),如果原生查询不能进行优化时将通知您,如清单 12 所示:
// ... as before ObjectContainer db = null; try { db = Db4o.openFile("persons.data"); db.ext().configure().diagnostic().addListener(new DiagnosticListener() { public void onDiagnostic(Diagnostic d) { if (d instanceof NativeQueryNotOptimized) { // could display information here, but for simplicity // let's just fail loudly throw new RuntimeException("Native query failed optimization!"); } } }); } |
很明显,只有在开发过程中这样做才是理想的 —— 在运行时最好将这个错误记录到 log4j 错误流中,或者记录到不会影响用户的类似内容中。
|
|
在 面向 Java 开发人员的 db4o 指南 的第二篇文章中,我使用 OODBMS 的一致性概念作为起点,解释了 db4o 如何存储和检索对象,并简单介绍了它的原生查询工具。
QBE 是进行简单查询的首选机制,因为它是一种更加易于使用的 API,但是它要求您的域对象允许任何或所有包含数据的字段被设置为 null,这将有悖于一些域规则。比如说,如果能够对 Person
对象执行姓和名字的查询将非常好。然而,在 QBE 查询中使用 Person
查询名字,要求姓氏可以是 null,这实际上意味着我们必须选择域约束或者查询能力,而这两者都不能被完全接受。
原生查询为执行复杂查询提供了一种功能强大的方法,而且不需要学习新的查询语言或使用复杂对象结构对谓词建 模。对于 db4o 原生查询不能够满足需要的情况,SODA API(对于任何对象系统,最初以独立的查询系统出现,而且仍然存在于 SourceForge 中)允许对查询进行最细致的调优,其代价就是破坏了简单性。
这种查询数据库方法的多面性可能让您备受挫折,它非常复杂,容易造成混淆,并且与 RDBMS 工作方式完全不同。事实上,这并不是问题:多数大型数据库将 SQL 文本转换为字节码格式(对这种格式进行分析和优化),执行存储在磁盘的数据,汇编回文本,然后返回。db4o 原生查询方法将编译放到了字节码之后,由 Java(或 C#)编译器来处理,因此保证了类型安全并对错误查询语法进行早期检测。(很不幸的是,JDBC 的访问 SQL 的方法丢失了类型安全,因为这是一个调用级别的接口,因此它限制只有在运行时才能检查字符串。对于任何 CLI 都是一样的,而不仅仅是 JDBC;ODBC 和 .NET 的 ADO.NET 也同样受此限制)。在数据库内部仍然执行了优化,但是并不是返回文本,而是返回真正的对象,以供使用。这与 SQL/Hibernate 或其他 ORM 方法形成了显著对比,Esther Dyson 对此做了很好的描述,如下所示:
利用表格存储对象,就像是将汽车开回家,然后拆成零件放进车库里,早晨可以再把汽车装配起来。但是人们不禁要问,这是不是泊车的最有效的方法呢。
发表评论
-
栈的简单应用--单词反转
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 14171.将getInstance()方法设置为private ... -
String是原始数据类型还是引用数据类型
2010-04-26 19:22 1711请教各位高手,String是原始数据类型还是引用数据类型?谢谢 ... -
java中堆(heap)和堆栈(stack)有什么区别
2010-04-26 19:13 2195stack 和 heap 都是内存的 ...
相关推荐
db4o将Java对象直接持久化到磁盘,通过对象的引用关系来处理数据,简化了开发过程,特别是对于面向对象的设计模式来说,可以更好地保持数据的一致性和完整性。 ### 2. db4o的API介绍 db4o提供了一套直观的API来...
面向Java开发人员的db4o指南主要关注如何利用db4o进行结构化对象和集合的管理,从而提高开发效率和数据存储的灵活性。 **对象数据库** 与传统的关系型数据库(RDBMS)不同,对象数据库直接支持面向对象编程模型。在...
面向Java开发人员的db4o指南db4o中的数据库重构
"db4o 使用指南" db4o 是一个开源的面向对象数据库,能够轻松地将 Java 对象持久化到数据库中。本文将详细介绍 db4o 的安装、启动、查询方式、对象持久化、数据库文件结构、主要包结构等知识点。 一、db4o 安装和...
对于更新操作,db4o会自动检测对象的变化,并仅更新实际变化的部分,确保数据一致性。同时,它还提供了版本控制,防止并发修改冲突。 **七、数组和集合** 在db4o中,数组和集合(如List、Set、Map)都可以作为对象...
2. **查询对象**:db4o支持查询对象,类似于Java中的`query-by-example`。你可以创建一个对象实例,设置你想要匹配的属性,然后让db4o找出所有匹配的对象。 3. **版本控制**:db4o可以跟踪对象的历史版本,允许你在...
此外,作为开源项目,db4o的社区支持和版本更新也是重要的部分,可能会介绍如何参与社区,获取最新的开发信息和bug修复。 总的来说,《db4o 权威指南》是一本全面覆盖db4o特性和实践的资源,对于想要深入理解和使用...
同时,DB4O支持事务管理和版本控制,以确保数据的一致性和可恢复性。 - 完整的全源码和注解:为了深入理解DB4O的工作原理和最佳实践,开发者通常需要参考完整的示例代码和详细的注释。这些资源可以帮助你更好地了解...
7. **事务支持**:db4o提供了ACID(原子性、一致性、隔离性和持久性)事务,确保了数据的一致性和完整性。 8. **事件驱动**:db4o支持对象生命周期事件,如对象插入、更新和删除时触发自定义的事件处理器,这为实现...
db4o是一个专为Java和.NET开发者设计的开源、轻量级的纯面向对象数据库引擎,提供了一种简单易用的方式来实现对象持久化。这款数据库引擎已被验证具备优秀的性能,根据描述中的基准测试,db4o在与传统持久化方案的...
2. **事务支持**:提供了更强大的事务管理,确保数据的一致性和完整性,支持ACID(原子性、一致性、隔离性、持久性)原则。 3. **持久化策略**:新增了多种持久化策略,如延迟加载(Lazy Loading)、透明激活...
4. **事务管理**: Db4o支持ACID事务,这意味着你可以确保数据的一致性和完整性。通过`database.transact()`方法,你可以包裹一组数据库操作在一个事务内,当事务成功完成时,所有更改才会被提交。 5. **人事管理...
- **兼容性**:db4o可能与不同的JVM版本和Java库存在兼容性问题,确保你的开发环境与db4o版本匹配。 - **性能优化**:尽管db4o提供了许多便利,但其性能可能不如优化过的SQL数据库。在性能敏感的场景下,需要评估和...
DB4O(Database for Objects)是一款开源的对象数据库管理系统(Object-Relational Mapping,ORM),它专为Java平台设计,提供了一种将对象直接持久化的解决方案,免去了传统的对象关系映射的复杂性。DB4O的目标是...
**db4o-7.2-java** 是一个针对Java平台的开源对象数据库系统,它将传统的关系型数据库理念与面向对象编程相结合,为开发者提供了一种高效、灵活的数据存储解决方案。db4o允许开发者直接存取Java对象,而无需通过SQL...
描述:这份资料是关于db4o数据库的使用教程,特别针对Java开发人员。db4o是一款开源的对象数据库,支持Java、.NET以及Mono平台。该教程旨在帮助用户快速上手db4o,并在开发过程中提供可靠的支持。 ### 一、db4o概述...
**db4o(Database for Objects)** 是一个开源的对象数据库管理系统(Object-Relational Mapping,ORM),它允许开发者直接在Java或.NET平台上存储和检索Java对象或.NET对象,无需进行SQL查询或者映射过程。db4o的...
对于Java开发者而言,能够在Eclipse这样的流行开发环境中集成db4o,可以极大地提升开发效率和项目的可维护性。 在压缩包内的"db4o-8.0.276.16149-java"可能包含了以下组件: 1. **db4o JAR文件**:这是db4o的核心库...
2.介绍了面向对象数据库 db4o 的安装、启动以及三种查询语言,并对三种查询语言做了比较。 3.介绍面向对象数据库 db4o 的修改和删除,并对其中出现的问题进行细致分析,引入了“更新深度(update depth)”这一重要...
DB4O面向对象数据库使用指南