db4o使用全解「建议收藏」

db4o使用全解「建议收藏」db4o使用全解 db4o是一种纯对象数据库,相对于传统的关系数据库+ORM,db4o具有以下好处:1)以存对象的方式存取数据(不过你考虑一下完全以对象的方式去考虑数据的存取对传统的数据库设计思维来说是多么大的颠覆)2)无需数据库服务器,只需要一个数据文件,且dll大小仅为300多k,非常适合作为嵌入式数据库;3)提供QueryBySample,NativeQuery和S

大家好,又见面了,我是你们的朋友全栈君。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 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 什么叫侧面指纹识别_正面背面侧面 你手机的指纹识别长在哪?

    什么叫侧面指纹识别_正面背面侧面 你手机的指纹识别长在哪?指纹识别已成为手机行业对热门技术之一,目前市面上常见的指纹识别模块搭载的位置主要有,正面、后背、侧面三种,比如说三星GalaxyS6、OPPOR9集成在正面Home键里,小米Note3、华为Mate8等装在了手机背部,LGV10植入到手机后边的电源键里,索尼XperiaZ5则放在手机侧面。正面背面侧面你手机的指纹识别长在哪?有人说指纹识别在正面最科学,但有些人则认为在背面更合理,此外…

  • XRDP与VNC的关系

    XRDP与VNC的关系

  • vim取消搜索后高亮持续_vue搜索高亮

    vim取消搜索后高亮持续_vue搜索高亮vim搜索以及取消高亮搜索字符串:/abc(不需要输入双引号)按N选择下一个shift+N,选择上一个搜索后打开别的文件,也发现被高亮了,此时可以取消高亮命令模式:setnohlsearchno,即关闭,不要hl,即highlight,高亮的意思search,即搜索…

  • libzplay库

    libzplay目前,非开源,只可以在windows上应用;关于MP3文件播放:通常步骤是:获取MP3相关参数->解码->相关平台播放音频接口播放声音;可以播放解码播放MP3的库很多,如果VLC,ffplay,或者directshow,解码库一般可以用lame,播放播放库可以用SDL,或者Windows上的waveout,directsound等很多方法,这里例举

  • 什么是纠删码_脑疝的常见类型

    什么是纠删码_脑疝的常见类型你能给纠删码一个好的定义吗? EthanMiller:纠删码是在丢失部分数据的情况下根据剩余数据将丢失的数据重建的一组算法。举个例子,如果我想保护六份数据,我会使用一种纠删码算法来产生两份额外的数据,这样总共就会有八份数据。这八份数据中的任意六份数据都能恢复另外两份数据。纠删码的要点是你可以选择对数据做任意数量的分片。我知道一些纠删码可以将数据至多分成200片或者奇数片,你也可以选择校验数

    2022年10月25日
  • 电脑锁屏时钟屏保_翻页时钟怎么设置在手机锁屏

    电脑锁屏时钟屏保_翻页时钟怎么设置在手机锁屏资源链接fliqlo说明:1、简约朴素的电脑屏保,锁屏后呈现翻页时钟效果;2、支持window、mac;效果…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号