前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >db4o使用全解「建议收藏」

db4o使用全解「建议收藏」

作者头像
全栈程序员站长
发布2022-09-14 21:46:09
8420
发布2022-09-14 21:46:09
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。db4o使用全解 db4o是一种纯对象数据库,相对于 传统的关系数据库+ORM,db4o具有以下好处: 1)以存对象的方式存取数据(不过你考虑一下完全以对象的方式去考虑数据的存取对传统的数据库设计思维来说是多么大的颠覆) 2)无需数据库服务器,只需要一个数据文件,且dll大小仅为300多k,非常适合作为嵌入式数据库; 3)提供Query By Sample, Native Query和Simple Object DataBase Access(SODA)三种方式进行数据查询,操作简便且功能强大,和sql说byebye。 同时还有一个叫objectmanager的工具,可用于查看数据文件中保存的对象,不过安装前需要安装jvm。

db4o数据库引擎 /db4o-7.4/bin/net-2.0/Db4objects.Db4o.dll .NET 2.0 framework平台的db4o引擎。 /db4o-7.4/bin/compact-2.0/Db4objects.Db4o.dll .NET 2.0 CompactFramework平台的db4o引擎。

一、首先,Db4objects.Db4o 、Db4objects.Db4o.Query 命名空间是我们所需要关心的。 1.Db4objects.Db4o Db4objects.Db4o 命名空间包含了几乎所有通常所需要用到的功能。 其中有两个值得留意的类:Db4objects.Db4o.Db4oFactory 和Db4objects.Db4o.IObjectContainer。

而Db4oFactory 工厂类是我们进行开发的起始点,该类的静态方法提供了打开数据库文件、启动服务器或者连接到已有的服务器的功能,同时在打开数据库前,你还可以通过它来配置db4o的环境。

IObjectContainer是一个99%的时间都会用到的、最重要的接口:在开发过程中,它就是你的db4o数据库。 – IObjectContainer 可以作为单用户模式的数据库,也可以作为db4o服务器的客户端连接。 – 每个IObjectContainer 持有一个事务,所有的操作都是事务相关的。 当你打开一个IObjectContainer时 ,事务已经开始了,当你commit()或者rollback(),它将会马上启动下一个事务。 – 每个IObjectContainer 会自己管理那些被其存储并实例化的对象的引用。而做这些工作的同时,它还管理这对象的唯一标识,这样是它能够达到很高的性能。 – 在使用IObjectContainer 的过程中,只要你还在使用它,它就会一直保持打开状态。而当你关闭这个ObjectContainers时,所有保存在内存中的对象引用都将被丢弃掉。

2.Db4objects.Db4o.Query Db4objects.Db4o.Query 包中包含了用来构造原生查询的Predicate类。Native Query接口是db4o最主要的查询接口,并且它应该比Soda查询API更常用。

3.Db4objects.Db4o.Ext 每个IObjectContainer 对象同时也是一个IExtObjectContainer对象。你可以把它转换成IExtObjectContainer,或者可以使用.Ext() 方法来获得更高级的特性。

4.Db4objects.Db4o.Config Db4objects.Db4o.Config 命名空间里面包含了配置db4o所需的类型和类

二、例子 //实体类,注意这个类中没有包含任何和db4o有关的代码。 namespace Db4objects.Db4o.Tutorial.F1.Chapter1 { public class Pilot { string _name; int _points; public Pilot(string name, int points) { _name = name; _points = points; } public string Name {get{return _name;}} public int Points {get{return _points;}} public void AddPoints(int points) { _points += points; } override public string ToString() { return string.Format(“{0}/{1}”, _name, _points); } } }

1.打开、关闭数据库(YAP文件) IObjectContainer db = Db4oFactory.OpenFile(“data.yap”); //打开本地数据库data.yap,如果该文件不在,则自动创建数据库文件。 try{// do something with db4o} finally{ db.Close(); //关闭} /*要访问或新建一个db4o数据库文件,把文件存储路径作为参数调用Db4oFactory.OpenFile()方法获取一个 IObjectContainer 实例。IObjectContainer 就代表”数据库”,它将是你操作db4o的主要接口。调用#close()方法来关闭IObjectContainer ,这样做也将关闭数据库文件并且释放所有关联的资源。*/

2.保存对象 //要保存对象,只需在数据库上调用Store()方法,以任意对象为参数传入即可。 Pilot pilot1 = new Pilot(“Michael Schumacher”, 100); db.Store(pilot1); //再来存一个 Pilot pilot2 = new Pilot(“Rubens Barrichello”, 99); db.Store(pilot2);

3.查询对象 Db4o提供三种不同的查询机制,样本查询(QBE),原生查询(NQ)和SODA查询接口(SODA)。推荐使用NQ

– 原生查询的目标是成为db4o的首要接口,因此它应该作为首选。 – 鉴于当前原生查询优化器的状态,某些查询使用SODA风格能够获得更快的执行速度,因此它被用于对应用进行优化。SODA对于在运行时构造动态查询也是非常方便的。 – 样本查询是非常简单的单行查询,但在功能上存在局限。如果你喜欢这种方式,并且它能够满足你的应用要求的话,仍可以使用。

3.1使用QBE查询 在使用QBE时,你要创建一个原型对象作为你要读取对象的样本。db4o会读取所有指定类型的对象,这些对像含有与样本一样(并非缺省)的属性值。结果以 IObjectSet 实例的形式返回。

也就是说,在使用QBE时,你需要提供给db4o一个模板对象。db4o将返回所有匹配此模板的无默认字段值(non-default field values)。这些工作是通过反射所有字段、创建查询表达式来完成的,在查询表达式中所有的无默认值字段都被AND表达式连接在一起。

使用QBE方式查询存在着一些明显的局限性: – db4o必须反射所有样本对象的成员。 – 你不能执行高级的查询表达式。(AND,OR,NOT等) – 你不能针对象0(整数)这样的字段值、””(空白字符串)或者null(引用类型)进行约束限制。因为它们都将被解释为不受约束限制的。 – 你必需能够使用无初始化字段的方式来创建对象,这意味着你不能在字段声明时对其进行初始化。最难的是,你不能强行约定某个类的对象只能处于这种定义良好的初始化状态。 – 你需要一个没有任何初始化字段的构造函数来创建对象。

我们这里写一个ListResult()方法,用来显示查询结果对象集中的内容。 public static void ListResult(IObjectSet result) { Console.WriteLine(result.Count);

foreach (object item in result) {Console.WriteLine(item);} }

要从数据库获取所有赛车手,我们可以给一个空的原型: //retrieveAllPilotQBE Pilot proto = new Pilot(null, 0); IObjectSet result = db.QueryByExample(proto); ListResult(result); //注意我们设定分数为0,但是我们的查询结果并没有受此约束,因为0是int类型的缺省值。

db4o还提供一个快捷方式来获取一个类的所有实例: //retrieveAllPilots IObjectSet result = db.QueryByExample(typeof(Pilot)); ListResult(result);

.NET 2.0也有一个泛型快捷方式,这样使用query方法: IList <Pilot> pilots = db.Query<Pilot>(typeof(Pilot));

使用名字查询赛车手,代码如下: // retrievePilotByName Pilot proto = new Pilot(“Michael Schumacher”, 0); IObjectSet result = db.QueryByExample(proto); ListResult(result);

使用给定的分数查询赛车手,代码如下: // retrievePilotByExactPoints Pilot proto = new Pilot(null, 100); IObjectSet result = db.QueryByExample(proto); ListResult(result);

3.2使用NQ查询 原生查询NQ是db4o的主要查询接口,它是查询数据库的推荐方式。由于原生查询简单地使用了编程语言的语法,因此它是非常标准化的,并且是一种面向未来的安全选择。

原生查询具备根据某个类的所有实例来运行一行或是多行代码的能力。原生查询表达式返回true来表示结果集中存在着某些特定实例。db4o将尝试优化原生查询表达式,并依靠索引运行表达式,而无需实例化实际的对象。 缺点:在内部,db4o尝试分析原生查询并将其转换为SODA。

//C# .NET 2.0 IList <Pilot> pilots = db.Query <Pilot> (delegate(Pilot pilot){return pilot.Points == 100;});

//C# .NET 1.1 IList pilots = db.Query(new PilotHundredPoints()); public class PilotHundredPoints : Predicate { public boolean Match(Pilot pilot) {return pilot.Points == 100;} }

请注意在上面的语法中:对于所有不支持类属的方言,NQ遵守约定工作。某个扩展自com.db4o.Predicate的类需要有一个boolean Match()方法,此方法接受一个描述类范围的参数:bool Match(Pilot candidate);

*在使用NQ时,不要忘记如果你使用模板和自动完成技术的话,可用现代化集成开发环境(IDEs)来帮你完成所有原生表达式相关的代码录入工作。

下面的示例展示了如何同一个查询在不同语言中使用原生查询语法的相似性,它们完全可以使用自动完成功能、重构和其他IDE特性,并在编译时作检查: //C# .NET 2.0 IList <Pilot> result = db.Query<Pilot> (delegate(Pilot pilot) { return pilot.Points > 99 && pilot.Points < 199 || pilot.Name == “Rubens Barrichello”;});

基本上,这就是NQ之所以能够被高效使用的原因。原则上,你可以将任意代码作为NQ来运行,但你需要非常小心某些方面的影响-尤其是那些可能对持久化对象发生作用的影响。 让我们运行一个示例来调用更多可用的语言特性: using Db4objects.Db4o.Query; namespace Db4objects.Db4o.Tutorial.F1.Chapter1 { public class ArbitraryQuery : Predicate { private int[] _points; public ArbitraryQuery(int[] points) { _points=points; } public bool Match(Pilot pilot) { foreach (int points in _points) { if (pilot.Points == points) {return true;} } return pilot.Name.StartsWith(“Rubens”); } } }

3.3使用SODA查询 SODA查询API是db4o的低级查询API,它允许直接访问查询图表(query graphs)的节点。由于SODA使用字符串标识字段,因此它并不是非常类型安全的,也不是编译时可检查的,并且编写的代码冗长。 对于大多数应用来讲,原生查询将是更好的查询接口。

以前面QBE查询转换为SODA为例,一个新的Query对象通过ObjectContainer的query()方法创建,我们可用在其上添加Constraint类实例。为了找到所有的Pilot实例,我们对Pilot类对象的查询进行了约束限制。 // retrieveAllPilots IQuery query = db.Query(); query.Constrain(typeof(Pilot)); IObjectSet result = query.Execute(); ListResult(result);

我们正在将”真正的”原型(prototype)交换为我们希望捕捉到的对象元描述(meta description):一个查询图表由多个查询节点和约束构成。每个查询节点都是一个存放候选对象的地方,每个约束则标识了是否从结果集中添加还是排除候选者。

而我们使用”descend”的目的是将 附加的约束条件增加到表达式树中以判断我们的候选对象,即添加约束 // retrievePilotByName //满足这个查询的候选对象需要是Pilot类型并且其数据成员”name”必须与给出的字符串相匹配才能加入到结果集中。 IQuery query = db.Query(); query.Constrain(typeof(Pilot)); query.Descend(“_name”).Constrain(“Michael Schumacher”); //添加_name为Michael Schumacher的约束 IObjectSet result = query.Execute(); ListResult(result);

// retrievePilotByExactPoints IQuery query = db.Query(); query.Constrain(typeof(Pilot)); query.Descend(“_points”).Constrain(100); //添加_points为100的约束 IObjectSet result = query.Execute(); ListResult(result);

SODA另外高级查询 query.Descend(“_name”).Constrain(“Michael Schumacher”).Not(); //_name不是Michael Schumacher的pilot的约束

query.Descend(“_name”).OrderAscending(); //排序asc 升序 query.Descend(“_name”).OrderDescending(); //排序desc 降序

IConstraint constr = query.Descend(“_name”).Constrain(“Michael Schumacher”); query.Descend(“_points”).Constrain(99).And(constr); //AND 与

IConstraint constr = query.Descend(“_name”).Constrain(“Michael Schumacher”); query.Descend(“_points”).Constrain(99).Or(constr); //OR 或

query.Descend(“_points”).Constrain(99).Greater(); //大于99 即 >99 query.Descend(“_points”).Constrain(99).Smaller(); //小于99 即 <99

query.Descend(“_points”).Constrain(10).Greater().equal(); //大于等于10 即 >=10 query.Descend(“_points”).Constrain(10).Smaller().equal(); //小于等于10 即 <=10

query.Descend(“_name”).Constrain(“est”).like(); //模糊查询,_name里包含”est”的约束

4.更新对象 更新对象和存储对象一样容易,实际上,你使用相同的Store()方法去更新你的对象,在你修改任何对象后只需要再次调用Store()就可以了。

注意我们先查询得到要更新的对象,这点和重要。当你调用Store()去修改一个存储对象时,如果这个对象不是持久化对象(在前面已经存储过或者在当前会话中读取到的对象),db4o将会插入一个新对象。db4o这么做是因为它不会自动比较要存储的对象和先前存储过的对象。它认为你是想再存储一个属性值一样的对象。 // updatePilot IObjectSet result = db.QueryByExample(new Pilot(“Michael Schumacher”, 0)); Pilot found = (Pilot)result.Next(); found.AddPoints(11); db.Store(found); Console.WriteLine(“Added 11 points for {0}”, found); RetrieveAllPilots(db);

5.删除对象 使用Delete()方法,数据将被从数据库中删除。 和更新对象一样,要删除的对象也必须是持久对象,只是提供一个属性值一样的对象是不可以成功删除的。 // deleteFirstPilotByName IObjectSet result = db.QueryByExample(new Pilot(“Michael Schumacher”, 0)); Pilot found = (Pilot)result.Next(); db.Delete(found); Console.WriteLine(“Deleted {0}”, found); RetrieveAllPilots(db); //我们再来删除另一个对象, // deleteSecondPilotByName IObjectSet result = db.QueryByExample(new Pilot(“Rubens Barrichello”, 0)); Pilot found = (Pilot)result.Next(); db.Delete(found); Console.WriteLine(“Deleted {0}”, found); RetrieveAllPilots(db);

==================================================== 目前db4o处理普通struct及enum还不尽如意。 对于普通的struct及enum,db4o不能辨别待储存/更新的实例与数据库中原有实例是否同一实例,因此当update时,即使值没有变动,db4o也会将它new一个出来,储存入数据库。如果仅仅只是这样,不过浪费了一些无谓的IO操作,更大的问题是它储存进去一个新值,却不删除原有的值,导致数据库文件中存在大量的垃圾数据。 .net下,Int32也是一种struct,然而,从上例日志中却未发现新建Int32 Code,我猜测是db4o对Int32这些常用struct进行了特殊处理。 为了避免垃圾数据,使用db4o时最好慎用struct。 ========================================================== *对类进行重构,如果只是添加属性,貌似不需要修改,提取时自动的设为null

Db4o官方网站 http://www.db4o.com/ JackyXu 的博客有数篇关于db4o的笔记 http://www.cnblogs.com/JackyXu/archive/2006/11.html db4o 中的数据库重构 http://www.ibm.com/developerworks/cn/java/j-db4o3.html db4o tutorial翻译文 http://www.cnblogs.com/dotdty/category/156928.html 敏捷开发利刃(简介) http://www.cnblogs.com/xiaotie/archive/2008/10/17/1313218.html

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/162570.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档