Berkeley DB Xml 入门系列之三: 使用Berkeley DB Xml Shell
1. 简介
Berkeley DB Xml Shell(以下简称DbXml Shell)是包含在发行包里的工具,可运行于UNIX家族平台(Linux, Solaris, HP-UX, Mac OS, Cygwin, AIX, FreeBSD…)和Windows平台上。DbXml Shell包含Berkeley DB XML的绝大多数常用功能,可以让我们在不编写一行代码的情况下,通过指令快速完成多种常用操作如创建/删除容器、插入/删除文档、查询、验证Query Plan等等,实为居家旅行必备佳品。要是在周五下午遭受紧急任务袭击,就更得靠它来拯救周末了。有时为了解决开发中遇到的问题,只要写一个非常简短的DbXml Shell脚本就可以实现预期功能或是重现环境,极大地提高了劳动生产率。
在使用DbXML shell之前,应当正确编译好Berkeley DB Xml库。具体编译过程请参考博客上的相关文章:
Berkeley DB Xml 入门系列之二: 在Linux下编译和使用Berkeley DB Xml
使用Visual C++ 2008 Express Edition 编译Oracle Berkeley DB XML
2. 运行
UNIX下的可执行文件为<dbxml>/install/bin/dbxml,Windows环境下为<dbxml>/bin/dbxml.exe。钻进DbXml Shell以前,先瞄一眼说明(中文是翻译的,发行版是E文的)
rucong@debian:~/dbxml/install/bin$ ./dbxml –?
选项:-c 在-h选项所指定的路径中创建一个新的Berkeley DB环境。
-h <home> 使用指定的路径作为Berkeley DB环境(默认为当前路径)。
-P <password> 设置数据库的密码
-s <script> 执行一个或多个脚本文件
-t 打开事务功能(transaction mode)
-V 打印版本号
-v verbose模式,连用两次进入更详细的verbose模式.
-x 安全模式–禁止在XQuery中直接访问本地文件和网络
-z <size> 设置环境Cache大小为<size>Mb (默认: 64)
-? 本说明
3. 指令说明
进入shell:
rucong@debian:~/dbxml/install/bin$./dbxml
嗯,已经进入DbXml Shell环境了。还是请help为我们向导一下(嗯…中文是翻译的,发行版是E文的。排序是按名称的,名词是不译的。列出的是常用的,实际还有更多的)
| # | 注释符,在脚本中可用#来做注释 |
| abort | 取消当前事务 |
| addAlias | 为默认Container增加一个别名 |
| addIndex | 为默认Container增加一个Index |
| commit | 提交当前Transaction,同时自动创建一个新Transaction |
| compactContainer | 压缩Container |
| contextQuery | 使用上一次查询结果作为上下文,可限定后续查询在已返回的结果中进行 |
| cquery | 在默认Container中进行查询,本指令和query的不同之处在于省略了显式collection() |
| createContainer | 创建一个新的Container,并将其作为默认Container |
| delIndex | 在默认Container中删除一条Index |
| getDocuments | 获得Container里的所有文档 |
| getMetaData | 获得指定Document里的metadata |
| help | 获得帮助信息。可以对某条指令进行特殊说明,如help putdocument |
| info | 获得默认Container的信息 |
| listIndexes | 列出所有Index |
| lookupIndex | 在默认Container中查找Index |
| lookupStats | 在默认Container中查看Index状态 |
| openContainer | 打开一个Container并设置为默认Container |
| prepare | 把指定的XQuery表达式预处理为默认Query表达式(default pre-parsed query),这样就可以快速地重复执行该Query |
| 输出执行的结果,可输出到文件 | |
| putDocument | 往Container里插入XML文档 |
| query | 执行指定的XQuery表达式,如无特殊指定,执行的就是prepare指令中预处理好的XQuery表达式 |
| queryPlan | 输出query plan。用来查看被优化后的表达式执行计划,同时可检查设置的Index是否生效 |
| quit | 退出程序。如果是用-s执行脚本文件,切记在结尾处加上这一句,不然程序会赖着不退出的 |
| removeAlias | 删除一个Container的别名 |
| removeContainer | 删除一个Container |
| removeDocument | 在Container中删除一个Document |
| run | 执行指定的脚本文件 |
| setBaseUri | 设置/获取 默认context的base URI |
| setLazy | 设置Query为Lazy还是Eager |
| setMetaData | 为指定的Document设置一个metadata |
| setNamespace | 为默认Context创建一个"prefix->namespace"绑定 |
| setQueryTimeout | 设置Query的超时时间 |
| setVerbose | 设置Verbose级别 |
| time | 计时。可用来计算指令的执行时间 |
| transaction | 创建一个事务,用于所有后续操作 |
| upgradeContainer | 把老版本的XmlContainer升级到现版本 |
接下来让我们小试牛刀,运行几个常用命令试试:
创建一个空Container
dbxml> createcontainer ""
Creating node storage container
插入一个文档test.xml:
dbxml> putdocument test.xml ‘<root><a><b/></a></root>’
Document added, name = test.xml
看看文档插入了没:
dbxml> getdocuments
1 documents found
检查文档名称:
dbxml> printname
test.xml
检查文档内容:
dbxml> print
<root><a><b/></a></root>
做个简单查询,并计时:
dbxml> time query collection()/root/a
1 objects returned for eager expression ‘collection()/root/a’Time in seconds for command ‘query’: 0.04848
输出查询结果:
dbxml> print
<a><b/></a>
退出
dbxml> print
<a><b/></a>
4.范例
上一节我们在DbXml Shell里做了一些简单的交互式操作,实际上我们采用DbXml Shell脚本更方便。下面是一个简单的DbXml Shell脚本,高级内容参见源代码包里的dbxml/docs/intro_xml/BerkeleyDBXML-Intro.pdf
createContainer "" #创建一个空Container
#插入一个文档
putdocument apples.xml ‘
<product>
<category>fruits</category><item>Apples</item>
<inventory>
<sku>Applfrui1q6lpq</sku>
<price>1.10</price>
<inventory>535</inventory>
</inventory>
<vendor>Off the Vine</vendor>
</product>
‘
getdocuments #获得所有文档
printnames #输出文档名
print #输出文档内容
#使用XQuery查询价格,并计量查询时间
time query ‘collection()/product/inventory/price/string()’echo "Price is:"
exit #退出
执行并输出结果:
rucong@debian:~/dbxml/install/bin$ ./dbxml -s example1.script
apples.xml<product>
<category>fruits</category><item>Apples</item>
<inventory>
<sku>Applfrui1q6lpq</sku>
<price>1.10</price>
<inventory>535</inventory>
</inventory>
<vendor>Off the Vine</vendor>
</product>
Time in seconds for command ‘query’: 0.008049
Price is:1.10
5.使用技巧
rlwrap是个好东西
UNIX用户初用DbXml Shell时,会悲愤地发现bash标配的命令行历史和左右箭头键居然统统废掉。呃…这意味着键入命令的过程中不能犯错,还不能重复历史指令,极不人道。因此该问题也凭借强大的怨念名列FAQ。其实只要使用rlwrap这个小工具便可以完美地解决这一问题(没有就装一个吧),rlwrap程序可以添加命令历史和命令编辑功能。用法:rlwrap ./dbxml。这体现了UNIX世界的哲学:把工具们连接在一起,而不是重复实现。
下面这条是OTN上荣列BestPractices的一条经验(作者是Greg Fausak):
/usr/bin/rlwrap -m -c -H ~/.rlhistory -l ~/dbxml.log -r -m -P ‘openC customer.dbxml \ adda L \ setN "test" "http://mytest.com/"’ /usr/local/bin/dbxml -h ~/sn/db
-H : 把命令执行历史存储在文件.rlhistory
-l : 把产生的所有的输入和输出存储在文件~/dbxml.log’-m : 使能多行命令,和下面的-P选项连用。 -P "…" 里面的反斜杠\就是分行标识,用来分隔多个命令。
-P : 在执行操作前,自动输入一批指令。由于DbXml Shell可以执行脚本文件,所以总的说来rlwrap的"-m"和"-P"选项对我们用处不大,可以无视。
-h 这个是DbXml Shell的参数,指定Berkeley DB环境路径
创建一个临时Container
如果只是做个临时实验,不想大费周章地创建Container文件和DB环境,那么只需要创建一个名字为空的Container就行了。DbXml会在内存中创建一个临时的Container,有需要的话还可以用addAlias为这个空名Container增加别名。
不用输入完整指令
Shell会自动补完符合唯一性的指令:比如createc=createContainer,listi=listIndex…
修改Shell代码
Shell不是万能的,如果发现Shell的功能比实际需求就恰巧少了那么一点点,又不想从头编写一套C++/Java程序,那么就Open Source精神来武装头脑指导实践推动工作 — 杀进DbXml Shell源代码里面修改吧!Shell的源程序在dbxml/src/util/shell目录下,组织得非常规范,只要有初步DbXml编程经验就可以对Shell进行修改和增强。修改之后进入dbxml/build_unix,重新"make; make install"即可。Visual Studio环境下就更方便,重新编译Shell的Project就行了。比如OTN上曾有用户问如何用Shell获得整个Container的MetaData(Shell的getMetaData只能获得一个Document的MetaData),解决办法就是修改dbxml/src/utils/shell/GetMetaDataCommand.cpp,增加一条循环调用就行了。
看到的所有例程xml都是英文,如果是带中文字符的xml文档如何在BDB XML中来存储和检索
@LEE
Berkeley DB XML内部使用UTF8编码存储文档的,你可以输入UTF8编码的中文。
我在Linux平台上使用dbxml 2.4.16.
XQuery查询语句中包含中文,我试过将中文用GB2312和UTF8编码。程序在检索过程中都异常退出了。提示信息为:
terminate called after throwing an instance of ‘DbXml::XmlException’
what(): Error: nsFromUTF8: bad utf-8 encoding File: NsUtil.cpp Line: 249
Aborted
Teminal显示两种编码形式的查询语句,查询条件是event的值为“测试”。分别是:
1.GB2312
for $wholeXml in collection(‘EventsContainer.bdbxml’)/Video,$event in $wholeXml/EventName where (contains(“测试”, $event) return $wholeXml
2.UTF8
for $wholeXml in collection(‘EventsContainer.bdbxml’)/Video,$event in $wholeXml/EventName where (contains(“娴嬭瘯”, $event) return $wholeXml
请问我该如何修正呢?
hi, 请问你是在程序中遇到这个错误还是在dbxml shell里面。 如果是shell, 你能不能把查询保存成一个utf8的文件,然后用shell去执行这个文件,看有没有错误。
@蔡瀛
你好,我碰到的这个错误不是在dbxml shell里面,而是在程序中我通过调用类似以下的代码时出现的。querystring包含中文。
…
XmlQueryContext context = _manager->createQueryContext();
XmlResults res = _manager->query(querystring,context);
…
另外,我写了一个包含中文的存储例子,测试结果该记录不能被存入。
string theDoc = “aac node”;
XmlUpdateContext theContext = theManager.createUpdateContext();
theContainer.putDocument( “”, theDoc, theContext, DBXML_GEN_NAME );
@ecli
hi, 如果是程序的话你可能需要把源代码文件保存成utf8格式编译。在我的机器上环境是“zh_CN.UTF-8”, 保存中文查询中文都没有问题。
@蔡瀛
按照你的建议把源代码文件保存成utf8格式编译。保存和查询中文已经没有问题了。谢谢~
Hi,你好。我写了一个多线程的例子来添加文档(通过DBXML.getInstance().insertXMLData(key);)。报出如下错误。单线程访问不会有问题,不知是哪里有问题?
异常:
com.sleepycat.dbxml.XmlException: Error: DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock, errcode = DATABASE_ERROR
at com.sleepycat.dbxml.dbxml_javaJNI.XmlContainer_putDocument__SWIG_7(Native Method)
at com.sleepycat.dbxml.XmlContainer.putDocument(XmlContainer.java:917)
at com.sleepycat.dbxml.XmlContainer.putDocument(XmlContainer.java:173)
at com.wondersgroup.dbxml.DBXML.insertXMLData(DBXML.java:119)
at com.wondersgroup.dbxml.InsertXmlData.run(InsertXmlData.java:46)
2010-2-25 10:28:12 com.wondersgroup.dbxml.InsertXmlData run
代码片段:
public class DBXML {
private static DBXML dbxml = null;
private Environment environment;
private XmlManager xmlManager;
private XmlContainer xmlContainer;
private DBXML() {
setEnvironment();
setXmlManager();
setXmlContainer();
}
public static synchronized DBXML getInstance() {
if (dbxml == null) {
dbxml = new DBXML();
}
return dbxml;
}
public void setEnvironment() {
System.out.println(“setEnvironment……”);
File file = new File(“E:\\testing\\data”);
EnvironmentConfig envConf = new EnvironmentConfig();
envConf.setAllowCreate(true); // If the environment does not
envConf.setInitializeCache(true); // Turn on the shared memory
envConf.setInitializeLocking(true); // Turn on the locking subsystem.
envConf.setInitializeLogging(true); // Turn on the logging subsystem.
envConf.setTransactional(true);
envConf.setLogInMemory(true);
envConf.setLogBufferSize(30*1024*1024);
envConf.setCacheSize(50*1024*1024);
envConf.setLockDetectMode(LockDetectMode.DEFAULT);
try {
environment = new Environment(file, envConf);
} catch (DatabaseException ex) {
Logger.getLogger(DBXML.class.getName()).log(Level.SEVERE, null, ex);
} catch (FileNotFoundException ex) {
Logger.getLogger(DBXML.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void setXmlManager() {
System.out.println(“setXmlManager……”);
XmlManagerConfig managerConfig = new XmlManagerConfig();
managerConfig.setAdoptEnvironment(true);
managerConfig.setAllowAutoOpen(true);
managerConfig.setAllowExternalAccess(true);
try {
xmlManager = new XmlManager(environment, managerConfig);
} catch (XmlException ex) {
Logger.getLogger(DBXML.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void setXmlContainer() {
System.out.println(“setXmlContainer……”);
XmlContainerConfig xmlContainerConfig = new XmlContainerConfig();
xmlContainerConfig.setTransactional(true);
xmlContainerConfig.setAllowCreate(true);
xmlContainerConfig.setNodeContainer(true);
xmlContainerConfig.setIndexNodes(true);
try {
if (xmlManager.existsContainer(“InstartXmlData.dbxml”) == 0) {
xmlContainer = xmlManager.createContainer(“InstartXmlData.dbxml”, xmlContainerConfig);
} else {
xmlContainer = xmlManager.openContainer(“InstartXmlData.dbxml”, xmlContainerConfig);
}
} catch (XmlException ex) {
Logger.getLogger(DBXML.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void insertXMLData(String key) {
XmlTransaction xt = null;
try {
xt = xmlManager.createTransaction();
XmlUpdateContext updateContext = xmlManager.createUpdateContext();
XmlInputStream theStream = xmlManager.createInputStream(new FileInputStream(“E:\\testing\\source\\cdmdetails171.xml”));
xmlContainer.putDocument(xt, key, theStream, updateContext, null);
theStream.delete();
xt.commit();
} catch (FileNotFoundException ex) {
Logger.getLogger(DBXML.class.getName()).log(Level.SEVERE, null, ex);
} catch (XmlException ex) {
Logger.getLogger(DBXML.class.getName()).log(Level.SEVERE, null, ex);
if (xt != null) {
try {
xt.abort();
} catch (XmlException ex1) {
Logger.getLogger(InsertXmlData.class.getName()).log(Level.SEVERE, null, ex1);
}
}
}
}
public void cleanup() {
try {
if (xmlContainer != null) {
xmlContainer.close();
}
if (xmlManager != null) {
xmlManager.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
您好:
我想问问有没有关于介绍Berkeley DB XML中对于XQuery是如何优化和执行方面的资料。谢谢
您好:
还有一个问题,在Shell命令下输入命令qplan然后接一个QXuery查询,显示出来的是什么?谢谢
@jianghua
请查看《Getting Started with Berkeley DB XML》queryplan 那一小节
我最近在看dbxml,发现查询过程中没有找到类似MAX,MIN这样的语法,如果我想要查找一个某个包含最小值的项,只能使用类似下面的order,再获取第一条?
std::string query = “for $item in collection(‘” + container.getName() + “‘)/fruits:item ”
“for $inventory in $item/inventory/inventory ”
“order by $inventory ”
“return $item”;
我在xquery下的grammer.cpp也没有找到类似的语法,那么dbxml到底能不能支持这样的操作?
DB XML 支持XQuery中的内建max函数, 具体请参考:
http://www.w3.org/TR/xpath-functions/#func-max