Berkeley DB XML入门
For dbxml- 2.2.13
第一章 概观
本章内容的以dbxml- 2.2.13 为例,请使用dbxml-2.2.13
基础
BDB xml 以库的形式直接练到用户的应用程序,BDB XML也有一个命令提示符让用户在脱离编程环境的情况下访问xml文档,你可以把命令提示符作为你应用程序的管理工具.
在DBD xml中,所有的数据存储在成为"容器"(containers)的文件中,BDB xml shell
提供了简便的方式操纵"容器"和所有的DBD功能.
容器可以把整个XML文档存储为一个文档,或把其中的节点单独存储,当整体存储时,XML文档就是一个"容器"或一个系统文件,当存储节点时,xml被分割成小块,存储在"容器"中.
在"节点存储"模式下,取出的文档的格式就是你存储的的格式(除非你指定了格式输出),区别就在于你是怎么存储的."节点存储"模式比"整个文档存储"提供更好的性能.所有默认是"节点存储"模式
使用SHELL
shell命令在BDB xml的安装目录的bin目录下,进入bin目录输入dbxml启动shell
如下
Bin>dbxml
d'b'xml>
BDB xml的语句有单行的和多行的.
BDB xml 用容器来存储文档 ,容器包含文档的集合,在一个容器中的多个文档可以结构不同,也可以相同.
开始我们的BDB xml之旅,首先创建一个容器,我们的第一个例子是一个简单的电话本,容器的名字叫phone.dbxml(扩展名不是必须为dbxml,但推荐这么写)
dbxml>createContainer phone.dbxml
因为我们现在所在的目录是bin>,所以phone.dbxml创建在bin目录下
创建完成后,shell会自动打开最后一个创建的容器.
接下来输入xml到phone.dbxml
(以'(单引号)包含输入的数据)
dbxml>putDocument phone1 '
(回车)
'(空格)s(回车)(s表示输入结束)
如果成功,则显示
Document added,name=phone1
再输入一条记录
dbxml> putDocument phone2 '
'(空格)s(回车)
如果成功,则显示
Document added, name = phone2
现在phone.dbxml中有了两条记录,在下面的例子中应用了几个基本的基于XPath 语句的XQuery查询 ,以后会有更复杂的XQuery 语句.
XPath语句是XQuery规范的一个主要部分,就像SELECT语句在sql中一样//注意collection ("phone.dbxml")这个语句,我们也可以简单的写为collection ()表示我们的操作对象是当前打开的容器.
dbxml>query 'collection ("phone.dbxml") /phonebook/name/last/string();
查询成功后显示
2 objects returned for eager expression '
collection("phone.dbxml")/phonebook/name/last/string()'
显示一下找到的last name
dbxml>print
Jones
Smith
至此,第一个小试验已经成功
下面我们找一下 lisa的home phone number
dbxml>query 'collection("phone.dbxml")/phonebook[name/first="Lisa"]/phone[@type="home"]/string()'
查询成功后显示
1 objects returned for eager expression '
collection("phone.dbxml")/phonebook[name/first = "Lisa"]/phone[@type = "home"]/string()'
dbxml> print
420-992-4801
若要显示所有属于420开头的电话号码,可以这样写
dbxml> query 'collection("phone.dbxml")/phonebook/phone[starts-with(.,"420")]/string()'
2 objects returned for eager expression '
collection("phone.dbxml")/phonebook/phone[starts-with(., "420")]/string()'
dbxml> print
420-203-2032
420-992-4801
上面的查询语句返回数据的一部分,就像select语句一样,每一个查询语句包含两部分,一:我们所要查询的容器,我们使用collection("phone.dbxml")来表示查询的容器,二:是XPath语句,如/phonebook/name/last/string();表示要查询所有的last name;
理解XPath是理解XQuery的第一步.
可以在collection中使用("|")表示在多个容器中查询,例如,要查询c1.dbxml和c2.dbxml,可以这样写:
(collection("c1.dbxml")|collection("c2.dbxml"))/name/string();
帮助的使用
dbxml>help显示所有的命令
dbxml> help
Command Summary
---------------
# - 注释。不做任何事情
abort - 终止当前的事务
addAlias - 加入一个别名到默认容器
addIndex - 加入一个索引到默认容器
append - 在查询表达式中添加节点规范
commit - 提交当前的事务, 并且开始一个新的事务
contextQuery - 用最后的结果当作上下文执行查询表达式
cquery - 在默认容器的上下文中执行一个查询表达式
createContainer - 建立一个新容器,它将变成默认的容器
debug - 调试命令 -- 仅内部使用
delIndex - 从默认的容器中删除一个索引
getDocuments - 从默认容器中通过名称得到文档
getMetaData - 从命名的文档中得到元数据
help - 打印帮助信息,用'help commandName'获得扩展帮助
info - 在默认容器上得到信息
insertAfter - 在通过查询表达式选择的节点后面插入新的内容
insertBefore - 在通过查询表达式选择的节点前面插入新的内容
listIndexes - 列出在默认容器中的所有索引
lookupIndex - 在默认的容器中执行索引查询
lookupStats - 在默认容器上查询索引统计
openContainer - 打开一个容器,并用它作为默认的容器
preload - 预加载(打开)一个容器
print - 打印最新的结果
putDocument - 插入文档到默认容器中
query - 在XmlManager上下文中执行一个表达式
queryPlan - 打印为指定的查询表达式的查询计划
quit - 退出程序
removeAlias - 从默认容器中删除一个别名
removeContainer - 删除一个容器
removeDocument - 从默认容器中删除一个文档
removeNodes - 从通过查询表达式指定的文档中删除内容
renameNodes - 重命名通过查询表达式指定的节点
run - 将指定的文件当作脚本运行
setApplyChanges - 在默认更新上下文中修改 "apply changes" 状态
setBaseUri - 在默认上下文中设置基本uri
setLazy - 在默认上下文中设置lazy评估开或关
setMetaData - 为指定的文档设置元数据
setNamespace - 在默认上下文中建立一个prefix->namespace绑定
setReturnType - 在默认上下文中设置返回类型
setTypedVariable - 在默认上下文中设置变量为指定的类型
setVariable - 在默认上下文中设置一个变量
setVerbose - 设置本shell的冗长
transaction - 为所有后续操作建立一个事务
updateNodes - 基于查询表达式和新内容更新节点上下文
upgradeContainer - 更新一个容器到当前的容器格式
也可显示一个命令的帮助
dbxml> help createContainer
createContainer -- Creates a new container, which becomes the default container
Usage: createContainer
Creates a new default container; the old default is closed.
The default is to create a node storage container, with node indexes.
A second argument of "d" creates a Wholedoc storage container, and
"id" creates a document storage container with node indexes.
A second argument of "n" creates a node storage container, and
"in" creates a node storage container with node indexes.
The optional third argument indicates whether or not to validate
documents on insertion
A containerName of "" creates an in-memory container.
This command uses the XmlManager::createContainer() method.
The help text has valuable information about the command and the API calls that are used to implement a particular command. This helps you to find the relevant section of the API documentation where more detail is available and also serves as a way to explore a commonly used subset of the API calls in an interactive fashion.
第二章: XQuery和Berkeley DB XML
本章内容的以dbxml- 2.2.13 为例,请使用dbxml-2.2.13
这一章我们学习一些DBD xml提供的XQuery命令,若对XQuery不熟悉,可以先看一看本文档最后的帮助
1:添加数据
创建一个parts.dbxml
dbxml>crteateContainer parts.dbxml
显示:
Creating node storage container with nodes indexed
此时,新建的容器已经打开;
现在使用putDocument添加数据到 parts.dbxml ,添加一个比较复杂的结构,我们使用循环来添加3000条记录
每条记录的基本结构是
其中有的记录的结构复杂一点
请输入下面的语句添加:
dbxml> putDocument "" '
for $i in (0 to 2999) return
{
if (($i mod 10) = 0)
then
else ""
}
'(空格) q(回车)
如果添加成功,显示每条记录的添加过程
2:结构化查询
我们刚才建的名为parts.dbxml的容器能包含不同结构的文档,这种管理不同结构的数据的能力是xml和传统的关系数据库的重要区别之一.在上面的例子中,一个简单的容器包含了两种不同的结构,两种结构又有一些相同的元素,这种交叠的文档结构是高效查询和公共索引的基础,可以用来模仿关系数据库的联合.结构化查询使用在xml中实现的原生的联合.下面是一些例子.
首先,显示parts.dbxml中的所有包含parent-part结构的记录
dbxml> query 'collection()/part[parent-part]'
显示:
300 objects returned for eager expression '
collection()/part[parent-part]'
显示一下结果:
dbxml> print
...
如果只想显示每个节点parent-part结构,可以这样写
dbxml> query '
collection()/part/parent-part'
300 objects returned for eager expression '
collection()/part/parent-part'
显示一下:
dbxml> print
...
如果只想显示每个节点parent-part结构的内容,可以这样写
dbxml> query '
collection()/part/parent-part/string()'
300 objects returned for eager expression '
collection()/part/parent-part/string()'
dbxml> print
0
0
...
2
2
如果只想显示不包含parent-part结构的节点,可以这样写
dbxml> query ' collection()/part[not(parent-part)]'
2700 objects returned for eager expression '
collection()/part[not(parent-part)]'
dbxml> print
...
xml的结构化查询有点像关系数据库中的关联,但是更容易使用和理解,有些xml的结构化查询是传统的关系数据库不能实现的,因为xml是自描述的.关系就存在于xml结构本身,当使用基于值的查询时这种xml的特性会更明显.
值查询:
XQuery可以使用基于值的查询,下面的例子结合了结构查询和值查询.
如果要选择parts.dbxml中的所有包含parent-part的子节点并且parent-part的子节点值为1的节点,可以这样写:
dbxml> query 'collection()/part[parent-part = 1]'
100 objects returned for eager expression '
collection()/part[parent-part = 1]'
结果如下:
dbxml> print
...
……………
XQuery也提供了从容器中选择的文档的的子集的功能,如果要查询part的number是1070或1032的part节点,可以这样写:
dbxml> query 'collection()/part[@number = 1070 or @number = 1032]'
2 objects returned for eager expression '
collection()/part[@number = 1070 or @number = 1032]'
dbxml> print
这个查询是基于属性的查询,而不是基于值的查询
我们也可以使用大于号和小于号来查询:
dbxml> query 'collection()/part[@number > 100 and @number < 105]'
4 objects returned for eager expression '
collection()/part[@number > 100 and @number < 105]'
dbxml> print
索引介绍:
xml数据库的最大的特点在于它们索引所包含的数据的能力,正确的使用索引能够显著的减少查询需求的时间,在上面的例子中,我们都能赶到时间的延时,因为BDB xml依次查看每个节点,如果用索引,BDB xml能够减少所要查询的节点的个数,使用得当的化,会显著提高查询的性能
为了验证索引的有效性,我们先输入下面的语句,以便能看到语句的执行时间
dbxml> setVerbose 2 2
以下的查询的持续时间依赖于机器的性能.
重新输入以前的一个查询:
query 'collection()/part[parent-part]'
Query - Starting eager query execution
Query - parts.dbxml - U : [3000] 256 512 768 1024 1280 1536 1792 2048
2304 2560 2816 257 513 769 1025 1281 1537 1793 2049 2305 ...
Query - Finished eager query execution, time taken = 2495.82ms
300 objects returned for eager expression '
collection()/part[parent-part]'
注意这次查询的耗时,(译者的耗时是226ms),为2.5秒,这个查询要遍历整个"容器",为了提高性能,我们希望使用索引来限定一下查询的范围为parent-part,而不是整个容器
索引包含4部分:路径类型(path type),节点类型(node type),关键字类型(key type)和单值性类型(
uniqueness).
下面的查询的例子需要索引来限定一下查询的范围,因为我们的模式不是唯一的,所以我们不使用单值性类型(uniqueness).所有,我们的索引要使用node-element-presence-none
下面添加一个索引给parts.dbxml.
dbxml> addIndex "" parent-part node-element-presence-none
Adding index type: node-element-presence-none to node: {}:parent-part
dbxml> query 'collection()/part[parent-part]'
Query - Starting eager query execution
Query - parts.dbxml - P(parent-part) : [300] 2 12 22 32 42 52 62 72 82 92 102 112 122 132 142 152 162 172 182 192 ...
Query - Finished eager query execution, time taken = 173.084ms
300 objects returned for eager expression '
collection()/part[parent-part]'
可以看到,查询耗时变为原来的五分之一,(译者的时间是39ms),随着数据库大小的增加,索引所带来的性能提升会更明显.
下面看看索引给基于值的查找带来的性能提升,
dbxml> query 'collection()/part[parent-part = 1]'
Query - Starting eager query execution
Query - parts.dbxml - P(parent-part) : [300] 2 12 22 32 42 52 62 72 82 92 102 112 122 132 142 152 162 172 182 192 ...
Query - Finished eager query execution, time taken = 223.821ms
100 objects returned for eager expression '
collection()/part[parent-part = 1]'
原来不要索引时的查询时间2.4s,现在为五分之一秒.因为我们现在查询的值是一个整数,
我们可以把值索引成整形来大幅提升性能,用下面的语句:node-element-equality-decimal
dbxml> addIndex "" parent-part node-element-equality-decimal
Adding index type: node-element-equality-decimal to node: {}:parent-part
dbxml> query 'collection()/part[parent-part = 1]'
Query - Starting eager query execution
Query - parts.dbxml - V(parent-part,=,'1') : [100] 12 42 72 102 132 162 192 222 252 282 312 342 372 402 432 462 492 522 552 582 ...
Query - Finished eager query execution, time taken = 69.803ms
100 objects returned for eager expression '
collection()/part[parent-part = 1]'
现在的时间是69ms了,可以看到性能的提升很明显.
下面再看一个例子:
dbxml> query 'collection()/part[@number > 100 and @number < 105]'
Query - Starting eager query execution
Query - parts.dbxml - U : [3000] 256 512 768 1024 1280 1536 1792 2048 2304 2560 2816 257 513 769 1025 1281 1537 1793 2049 2305 ...
Query - Finished eager query execution, time taken = 6938.48ms
4 objects returned for eager expression '
collection()/part[@number > 100 and @number < 105]'
此次查询耗时将近7秒.下面我们添加索引(译者的时间是686ms)
dbxml> addIndex "" number node-attribute-equality-decimal
Adding index type: node-attribute-equality-decimal to node: {}:number
再来查询一下:
dbxml> query 'collection()/part[@number > 100 and @number < 105]'
Query - Starting eager query execution
Query - parts.dbxml - V(@number,>,'100') : [2899] 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 ...
Query - parts.dbxml - V(@number,<,'105') : [105] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...
Query - parts.dbxml - n(V(@number,>,'100'),V(@number,<,'105')) : [4] 103 104 105 106 Query - Finished eager query execution, time taken = 29.967ms
4 objects returned for eager expression '
collection()/part[@number > 100 and @number < 105]'
现在时间为30ms.(译者的时间是120ms)
(译者的机器从686ms下降到120ms,没有原文中的性能提升明显)
BDB XML提供了多种类型的索引.
格式化XML输出:
XQuery可以用来格式化XML的内容,以便输出,像输出为HTML
我们使用原来的parts.dbxml和查询语句来把查询结果格式化为HTML输出:
dbxml> query '
{
for $part in (collection()/part[@number > 100 and @number < 105])
return
}
'
Query - Starting eager query execution
Query - parts.dbxml - V(@number,>,'100') : [2899] 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 ...
Query - parts.dbxml - V(@number,<,'105') : [105] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...
Query - parts.dbxml - n(V(@number,<,'105'),V(@number,>,'100')) : [4] 103 104 105 106 Query - Finished eager query execution, time taken = 22.561ms
1 objects returned for eager expression '
'
{
for $part in (collection()/part[@number > 100 and @number < 105]) return
}
来看一下结果:
dbxml> print
可以直接用来页面显示了:
XQuery 实现了For ,Let ,While,Order by retrun 表达式,简称之为FLOWR.我们在查询中使用了XPath.它是FLWOR整体结构的一部分.
使用DBD XML API来动态的处理XML文档进行输出比现在我们用SHELL命令来输出要有用的多.
排序:
以前的例子中没有讲到排序(Order by),所以输出的结果的顺序就是在容器中存储的顺序.
现在我们再看一个例子,使用了排序,使输出符合我们的要求:
dbxml> query '
{
for $part in (collection()/part[@number > 100 and @number < 105])
order by xs:decimal($part/@number) descending (descending表示要降序排序)
return
}
'
Query - Starting query execution
Query - parts.dbxml - R(@number,>,'100',<,'105') : [4] 103 104 105 106 Query - Finished eager query execution, time taken = 29.869ms
1 objects returned for eager expression '
'
{
for $part in (collection()/part[@number > 100 and @number < 105]) order by xs:decimal($part/@number) descending
return
}
显示一下结果:可以看到结果是降序排列的
dbxml> print
如果从网页中输出,就是这样的:
使用多个容器:
一个应用程序可能同时使用多个容器,BDB XML 和 XQuery 提供了很好的对此类需求的解决方案,首先,创建第二个容器,添加一些数据.
dbxml> createContainer components.dbxml
Creating node storage container with nodes indexed
dbxml> putDocument component1 '
Document added, name = component1
dbxml> putDocument component2 '
Document added, name = component2
dbxml> preload parts.dbxml
dbxml> preload components.dbxml
新建的component.dbxml需要一些parts.dbxml中的数据,为了把两个容器联系起来进行输出,看下面的代码:
dbxml> query ' {$part/description/text()}
{
for $component in collection("components.dbxml")/component
return
Component number: {$component/@number/text()}
{
for $part-ref in $component/uses-part
return for $part in collection("parts.dbxml")/part[@number = $part-ref cast as xs:decimal]
return
}
}
'
Query - Starting query execution {$part/description/text()}
Query - components.dbxml - U : [2] 2 3 Query - parts.dbxml - V(@number,=,'89') : [1] 91 Query - parts.dbxml - V(@number,=,'150') : [1] 152 Query - parts.dbxml - V(@number,=,'899') : [1] 901 Query - parts.dbxml - V(@number,=,'901') : [1] 903 Query - parts.dbxml - V(@number,=,'87') : [1] 89 Query - parts.dbxml - V(@number,=,'189') : [1] 191 Query - Finished eager query execution, time taken = 19.495ms
1 objects returned for eager expression '
{
for $component in collection("components.dbxml")/component
return
Component number: {$component/@number/text()}
{
for $part-ref in $component/uses-part
return for $part in collection("parts.dbxml")/part[@number = $part-ref cast as xs:decimal]
return
}
}
'
XQuery assigns the variable $part-ref the very general XPath number type. The index we defined earlier applies only to decimal values which is a more specific numeric type than number. To get the query to use that index we need to provide some help to the query optimizer by using the cast as xs:decimal clause. This provides more specific type information about the data we are comparing. If we do not use this, the query optimizer cannot use the decimal index because the type XQuery is using and the type of the index is using do not match.
这个查询使用了我们先前创建的索引,XQuery使用了 $part-ref变量做为XPath的通用数值类型, 前面建立的索引仅适用于10进制,要在此次查询中使用这个索引我们需要加上xs:decimal语句,它告诉查询语句我们所使用的是10进制,如果不使用xs:decimal语句,query查询就不能使用前面建立的10进制索引,因为这和XQuery的默认配置不匹配.
下面显示一下查询的结果:
dbxml> print Description of 89 Description of 150 Description of 899 Description of 901 Description of 87 Description of 189
Component number: 1
Component number: 2
下面是在浏览器中的显示:
The BDB XML container model provides a great deal of flexibility because there is no specific XML schema associated with a container. XML documents of varying structures can coexist in a single container . Alternatively, separate containers can contain XML documents that are identical along conceptual lines, or for other purposes. Container and document organization should be tailored to the needs of your application.
BDB XML的容器模型提供了强大的灵活性,容器不依赖于特定的XML结构.各种形式的XML文档都可放在容器中.不同的容器可以包含相同的XML文档,容器和文档的组织形式取决于应用程序的需要.
在特定的文档上操作数据
Previous queries have executed against all the documents in a container, but there are cases where access to data in a single document is the goal. It is possible to isolate a single document component based on the name we assigned to it, and then perform XQuery expressions against it alone.
For example, to select the number attribute from a document named component 1 in the components.dbxml container:
前面的查询例子在容器所包含的所有文档上操作,有时,我们需要在特定的文档上操作,我们可以用文档名来表示我们的操作对象.然后 XQuery 语句就会应用在指定的对象上.
例如,我们从先前的component.dbxml的component1文档中取得数据:
dbxml> query 'doc("components.dbxml/component1")/component/@number'
Query - Starting query execution
Query - components.dbxml - D('component1',U) : [1] 2 Query - components.dbxml - U : [2] 2 3 Query - components.dbxml - D('component1',U) : [1] 2 Query - Finished query execution, time taken = 3.574ms
1 objects returned for eager expression '
doc("components.dbxml/component1")/component/@number'
显示一下:
dbxml> print
{}number="1"
doc语句可以用来操作容器中特定的数据,当HTTP发来了以XML形式的请求时我们可以把请求作为XQuery查询的一部分.
一个web服务有能力查询特定部分的价格,当作单个XQuery FLWOR整合到一个HTML页面.Sleepycat设置了这样的模拟服务来支持例子. 有一个XML文档在xml.sleepycat.com通过web服务提供.可以用XQuery中的doc函数访问价格数据.价格文件的URL是http://xml.sleepycat.com/intro2xml/prices.xml. 文件的内容将提供parts的价格.
在商业应用中,WEB服务器可能需要查询特定部分的价格,通过XQuery查询输出到一个页面,Sleepycat提供了一个在线的XML价格列表. http://xml.sleepycat.com/intro2xml/prices.xml.
下面我们把parts.dbxml和这个价格列表整合起来.
dbxml> query '
{
for $component in collection("dbxml:components.dbxml")/component
return
Component number: {$component/@number/text()}
{
for $part-ref in $component/uses-part
return for $part in collection("dbxml:parts.dbxml")/part[@number = $part-ref cast as xs:decimal]
return } {$part/description/text()} {
doc("http://xml.sleepycat.com/intro2xml/prices.xml")//part[
@number = $part/@number]/text() }
}
'
Query - Starting query execution
Query - components.dbxml - U : [2] 2 3 Query - parts.dbxml - V(@number,=,'89') : [1] 91 Query - parts.dbxml - V(@number,=,'150') : [1] 152 Query - parts.dbxml - V(@number,=,'899') : [1] 901 Query - parts.dbxml - V(@number,=,'901') : [1] 903 Query - parts.dbxml - V(@number,=,'87') : [1] 89 Query - parts.dbxml - V(@number,=,'189') : [1] 191 Query - Finished query execution, time taken = 2098.29ms
1 objects returned for eager expression '
{
for $component in collection("dbxml:components.dbxml")/component
return
Component number: {$component/@number/text()}
{
for $part-ref in $component/uses-part
return for $part in collection("dbxml:parts.dbxml")/part[@number = $part-ref cast as xs:decimal]
return } {$part/description/text()} {
doc("http://xml.sleepycat.com/intro2xml/prices.xml")//part[
@number = $part/@number]/text() }
}
'
显示一下结果:
dbxml> print
Description of 89 19.95 Description of 150 24.95 Description of 899 9.95 Description of 901 15.00 Description of 87 29.95 Description of 189 5.00
下面是在浏览器中的显示:
这种操纵外部XML的能力会给应用程序强大的灵活性.
元数据
元数据是关于数据的数据.提供了额外的关于一个文档的信息,但是它不包含在所描述的文档内.例如,在components.dbxml中添加的文档有一个名字,每个名字就是文档的元数据.其他的公共元数据包含文档被修改的次数或谁修改了此文档.例如,你可能需要是谁最后改变了XML文档,但又不能为了记录这个信息而去修改XML文档,这时就可以使用元数据,你也可以对元数据进行索引.
添加元数据,使用setMeatData命令.
dbxml> openContainer components.dbxml
dbxml> setMetaData component1 '' modifyuser string john
MetaData item 'modifyuser' added to document component1
dbxml> setMetaData component2 '' modifyuser string mary
MetaData item 'modifyuser' added to document component2
Metadata is essentially contained within its own, unique namespace (dbxml:metadata), so queries against metadata must identify this namespace:
元数据包含它所属于的文档的信息和命名空间,所以在请求元数据的时候必须表明它的命名空间(dbxml:metadata).
dbxml> query 'collection("components.dbxml")/component[dbxml:metadata("modifyuser")="john"]'
1 objects returned for eager expression '
collection("components.dbxml")/component[dbxml:metadata("modifyuser")="john"]'
显示结果:
dbxml> print
注意,元数据并不包含在XML文档中,它是作为XML文档的附加数据一同存储在容器中,如果你把XML文档转移到了另一个系统中,元数据不会随同转移.
修改XML文档
XQuery as a language does not yet define any way to modify documents stored within an XML database. When it does, BDB XML will add support for this functionality. Meanwhile, BDB XML does include an excellent API for document modification. Using this API it is possible to add new data into an existing document, modify (replace) existing data in the document, and delete data from a document.
In the BDB XML shell, modifications occur in two steps. First, you select the set of documents you want to work with. In this example only the component1 document is selected:
XQuery没有修改XML文档的能力,BDB XML 添加了相关的功能,BDB XML提供了以一个极好的添加,修改和删除XML文档中数据的API,在BDB XML提供的shell下,修改数据分两不走,1,选择你要操作的文档,在下面的例子中,使用components.dbxml中的component1.
dbxml> query doc('components.dbxml/component1')
1 objects returned for eager expression 'doc('components.dbxml/component1')'
添加一个节点数据:
dbxml> append ./component element uses-part '12'
Appending into nodes: ./component an object of type: element with name: uses-part and content: 12
1 modifications made.
dbxml> print
The append command executes relative to the context of the previous query. That context contains the entire component1 document because we used the doc function to select it. However, modifications only work against nodes within the document so we had to select the root node of the document with the ./component XPath statement.
append语句的执行依赖于前面的query doc('components.dbxml/component1'),修改操作仅对文档下的节点有效,所以我们需要要 ./component来指出根节点.
修改.append语句对当前的文档操作,而不管有多少个文档.
dbxml> query 'collection("components.dbxml")/component'
2 objects returned for eager expression '
collection("components.dbxml")/component'
现在query选择了容器中所有的文档,如果现在用append插入一条新节点的话所有的文档都会被插入 .
dbxml> insertAfter ./uses-part[last()] element uses-part '15'
Inserting after nodes: ./uses-part[last()] an object of type: element with name: uses-part and content: 15
2 modifications made.
dbxml> print
The modification becomes part of the current context . This allows for cascading modifications. Taking advantage of this, the following will remove the nodes added in the last step using the removeNodes command.
可以看到给两个选中的文档都添加了一条数据.我们可以利用此特性进行批量操作.下面我们用 removeNodes命令把刚才添加的节点删除.
dbxml> removeNodes 'uses-part[. = 15]'
Removing nodes: uses-part[. = 15]
2 modifications made.
dbxml> print
Such modifications can also be performed on attributes, processing instructions, comments and text nodes by altering the parameters to the modification operation. The following adds a version attribute to the root node of each component.
修改也可以应用于文档属性,处理过程,注释和节点文本,下面的例子添加了一个版本状态到每个文档.
dbxml> append . attribute version '1.0'
Appending into nodes: . an object of type: attribute with name: version and content: 1.0
2 modifications made.
dbxml> print
修改功能提供了强大和简单的机制来更新XML数据,可以快速的对数据量大的文档进行节点修改.
Schema Constraints
模式约束
XML可以随意的使用模式来强制文档的相似性.大部分数据库支持模式约束.但是BDB XML有自己独一的机制来存储数据,并使用模式来在不同的文档结构中展现数据.这是BDB独有的.在其他的XML数据库中没有.
回想我们的phonebook.dbxml它的结构如下
BDB XML要验证这个结构的有效性需要三步,1需要一个模式,受限于本文档,我们仅使用一个模式,网上有很多关于XML模式的讨论,建议对XML模式步熟悉的读者先看一看.
可以在网上访问此文档URL http://xml.sleepycat.com/intro2xml/phonebook.xsd.
第二步:创建一个容器,打开validation 选项
dbxml> createContainer validate.dbxml d validate
Creating document storage container, with validation
第三步, 在文档上加入模式,放入容器中..
dbxml> putDocument phone1 '
"
' s
Document added, name = phone1
That document was successfully added because it conforms to the schema. Now, try to add an invalid document.
dbxml> putDocument phone2 '
"
' s
stdin:67: putDocument failed, Error: XML Indexer: Parse error in document at line, 10, char 17. Parser message: Unknown element 'cell-phone'
Since the schema doesn't define the cell-phone element and we have schema validation enabled, BDB XML won't allow the document to be added to the container.
第二次插入记录不成功,因为不符合我们提供的模式.XML 模式提供了强大的功能来约束XML文档结构和内容
BDB XML API
BDB XML shell是给最终用户用的,程序员可以使用它提供的API来访问容器.
下面是一个例子:
#include
#include
#include "dbxml/DbXml.hpp"
using namespace std;
using namespace DbXml;
int
main(int argc, char **argv)
{
XmlManager mgr;
// 创建一个容器
XmlContainer cont = mgr.createContainer("phone.dbxml");
// 添加电话条目到容器
XmlUpdateContext uc = mgr.createUpdateContext();
cont.putDocument("phone1", "
cont.putDocument("phone2", "
// 启动XQuary查询
XmlQueryContext qc = mgr.createQueryContext();
XmlResults res = mgr.query("collection('phone.dbxml')/phonebook[name/first = 'Lisa']/phone[@type = 'home']/text()", qc);
// 显示结果
XmlValue value;
while (res.next(value))
cout << "Value: " << value.asString() << endl;
return 0;
}
第三章
当你开始在你的应用程序中使用BDB XML时,你应该仔细看完Getting Started with Berkeley DB XML文档,Getting Started with Berkeley DB XML包含了比本文档更详尽的内容,BDB XML的很多高级特性在Getting Started with Berkeley DB XML中描述.
当你使用XML数据库时,BDB XML是一个不错的选择,"容器"是BDB XML的基础,Berkeley DB提供了如"事务"等原生的特性,以 Berkeley DB为基础的BDB XML提供了XQuery访问,索引,整个文档或某个节点的数据组织能力,W 3C XML标准保证了存储在容器中的XML文档的正确性与合法性,但也允许你选择存储不符合标准的XML文档.
因为BDB XML是一个原生的XML数据库,它直接存储XML文档,这种特性使得在必须使用XML做为最终数据的情况下,比从关系数据库把数据转换成XML要更吸引开发人员.
XML特性:
BDB XML实现了W 3C 的标准,XML的命名空间和最新的XQuery标准.下面BDB XML的特性用来支持XML的管理理和查询,它们都是基于已经存在的标准.
1:容器(Container)一个单独的文件用来存贮多个或单个XML文档以及它们的索引文件和其他元数据.
2:索引:用来快速的选择文档的子集,可以提高查询的速度.
3:完整性:确保文档存储和恢复的一致性
4:元数据:存储在容器中的"关于文档的数据",和文档相关联.
5:更改和修正:更改和修正XML文档.包括添加和删除.
数据库特性;
BDB XML继承了大部分Berkeley DB的特性,这使得BDB XML多年来一直处于领先地位.
1:嵌入式的数据访问:BDB XML像其他的库文件一样使用,它和应用程序是同一进程空间的.这样它访问数据时就不会像服务器/客户模式的数据库那样开销大.
2:最大可管理256TB的数据
3:环境支持:BDB XML支持多数据库,数据cache共享,事务,死锁检测,页面控制,锁控制和加密.BDB XML
可以和Berkeley DB共同使用一个环境.
4:原子操作:使用BDB XML的事务可以把复杂的读或写操作可以封装成一个原子操作,一个原子操作中的语句要么全部完成,要么一条也不完成.
5:单独操作:在事务中的一个单独操作可以忽略事务.就像没有事务一样.
6:可恢复性:事务可以保证在不可预测的系统故障的情况下的数据的有效性.
7:并发连接:通过BDB XML整合的隔离特性和死锁检测,可以保证多线程同时访问数据.
8:复制:BDB XML提供从主数据库复制数据到备份数据库,可以提高容错能力或用于负载平衡系统.
开发语言和平台
官方的BDB XML支持C++,Java,Perl,Python,PHP和Tcl语言.
本文档由HOLIN翻译.水平有限,看不懂的地方请看原文档
附录:
Bdb xml java API sample
//DbXmlTest.java
package basic;
import java.io.FileNotFoundException;
import com.sleepycat.dbxml.XmlContainer;
import com.sleepycat.dbxml.XmlException;
import com.sleepycat.dbxml.XmlManager;
import com.sleepycat.dbxml.XmlQueryContext;
import com.sleepycat.dbxml.XmlQueryExpression;
import com.sleepycat.dbxml.XmlResults;
import com.sleepycat.dbxml.XmlUpdateContext;
import com.sleepycat.dbxml.XmlValue;
import org.w 3c .dom.Node;
public class DbXmlTest {
XmlManager m_mgr = null; //数据库工厂
XmlContainer m_cont = null;//数据文件对象
XmlUpdateContext m_uc = null;//数据更新对象
XmlQueryContext m_qc = null;//数据查询对象
static final private String m_StrDbName = "phone.dbxml";
static final private String m_StrName1 = "phone1";
static final private String m_StrXml1 = "
static final private String m_StrName2 = "phone2";
static final private String m_StrXml2 = "
static final private String m_StrName3 = "phone3";
static final private String m_StrXml3 = "
static final private String m_strquery = "collection('phone.dbxml')/phonebook";
static final private String m_strquery1 = "collection('phone.dbxml')/phonebook[name/first=$name]";
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成方法存根
DbXmlTest dbxml = new DbXmlTest();
try {
dbxml.create(m_StrDbName);
if (dbxml.isEmpty(m_strquery1, "Tom")) {
dbxml.addXmlData(m_StrName1, m_StrXml1);
dbxml.queryXmlData(m_strquery);
}
if (dbxml.isEmpty(m_strquery1, "Lisa")) {
dbxml.addXmlData(m_StrName2, m_StrXml2);
dbxml.queryXmlData(m_strquery);
}
if (dbxml.isEmpty(m_strquery1, "Tom")) {
dbxml.addXmlData(m_StrName3, m_StrXml3);
dbxml.queryXmlData(m_strquery);
} else {
System.out.println("已经存在");
}
dbxml.deleteXmlData(m_StrName2);
dbxml.queryXmlData(m_strquery);
dbxml.editXmlData(m_StrName1, m_StrXml2);
dbxml.queryXmlData(m_strquery);
dbxml.close();
} catch (XmlException e) {
// TODO 自动生成 catch 块
System.out.print("XmlException");
e.printStackTrace();
} catch (FileNotFoundException e) {
// TODO 自动生成 catch 块
System.out.print("未找到数据文件");
}
}
//建立数据库,"XXX.dbxml"
public void create(String strDbName) throws XmlException, FileNotFoundException {
m_mgr = new XmlManager();
if (m_mgr.existsContainer(strDbName) != 0) {
m_cont = m_mgr.openContainer(strDbName);
} else {
m_cont = m_mgr.createContainer(strDbName);
}
m_uc = m_mgr.createUpdateContext();
m_qc = m_mgr.createQueryContext();
}
//关闭数据库
public void close() {
if (m_cont != null)
m_cont.delete();
if (m_mgr != null)
m_mgr.delete();
}
//更新数据
//添加数据
public void addXmlData(String strName, String strXml)
throws XmlException {
m_cont.putDocument(strName, strXml, m_uc);
}
//查询数据,使用xquery
public void queryXmlData(String strQuery) throws XmlException {
XmlQueryExpression expr = m_mgr.prepare(strQuery, m_qc);
XmlResults res = expr.execute(m_qc);
XmlValue value = new XmlValue();
System.out.print("Result: ");
while ((value = res.next()) != null) {
System.out.println("/t" + value.asString());
value.delete();
}
res.delete();
expr.delete();
}
//修改数据
public void editXmlData(String strName, String strNewXml) throws XmlException {
m_cont.deleteDocument(strName, m_uc);
m_cont.putDocument(strName, strNewXml, m_uc);
}
//删除数据
public void deleteXmlData(String strName) throws XmlException {
m_cont.deleteDocument(strName, m_uc);
}
//排重
public boolean isEmpty(String strQuery, String strName) throws XmlException {
m_qc.setVariableValue("name", new XmlValue(strName));
XmlQueryExpression expr = m_mgr.prepare(strQuery, m_qc);
XmlResults res = expr.execute(m_qc);
XmlValue value = new XmlValue();
System.out.print("Result: ");
if ((value = res.next()) != null) {
value.delete();
res.delete();
expr.delete();
return false;
}
res.delete();
expr.delete();
return true;
}
}