db4o使用全解

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 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值