首页 > Berkeley DB, Chao Huang, SQL > Oracle Berkeley DB 支持SQL啦!

Oracle Berkeley DB 支持SQL啦!

2010年3月24日 chaohuang

Oracle Berkeley DB将于2010年3月底发布最新版本Oracle Berkeley DB 11g release 2,具体版本号为 11.2.5.0.xx (xx代表具体的patch版本号)。

除了对原有Oracle Berkeley DB的功能进行了一定的改进和增强(比如提升了数据压缩功能、性能优化、C/C++中系统资源自动管理功能等等),本次发布的版本中最引人瞩目的变化是我们引入了一个有用的新特性——Oracle Berkeley DB SQL,简称DBSQL (纠正:将原BDBSQL纠正为DBSQL)。这是自Berkeley DB诞生20多年来第一次支持SQL接口。这无论是对开源社区,还是对嵌入式数据库行业来说,都将是一件喜事。在此也感谢整个Oracle Berkeley DB 研发团队的努力工作和大家的不断支持。

新的版本,新增的SQL接口,值得期待。 :-)

Oracle Berkeley DB SQL接口简介

从Oracle Berkeley DB (简称BDB)诞生以来,它一直扮演着一个嵌入式、提供API调用的、高性能、非关系型的数据库引擎的角色, 被广泛应用于存储、金融、互联网、电子商务、汽车、消费电子、航空及国防等领域。简言之,它是一个1M大小的C语言类库,提供了基于键/值对(key/value pair)形式的并发和事务操作的API给C/C++/Java/C#/PHP等编程语言调用。由于BDB灵活高效的特点,它特别适合一些大数据量的、或者任务密集型的、或者硬件资源受限而性能要求的嵌入式、跨平台等等的应用需求。

但我们发现,在很多场合对于关系数据库和SQL的需求是大量存在的。在 “edge”(如消费电子)或者一些大型的企业应用(如ERP)中,一个即时高效、并发的,支持SQL的、本地化嵌入式数据库通常是首选。因此,从 Oracle Berkeley DB 11g release 2开始,我们在保留原有基于key/value操作的API的同时,新增加了对SQL的支持。

DBSQL完全兼容SQLite(著名的嵌入式开源关系数据库)原有的编程接口。以往运行在SQLite上的程序和应用都可以无缝的、方便的迁移到 Oracle Berkeley DB这个更加强大的引擎。并且,Oracle Berkeley DB和SQLite还将进行长期的官方层面的合作,保证了Oracle Berkeley DB的SQL接口和SQLite保持一致,免除了用户的后顾之忧。此外,Berkeley DB SQL完美支持很多第三方的SQLite工具,如JDBC,ODBC,FireFox 3及其SQLite Manager 插件等。

就具体实现来说,在DBSQL中,我们将原SQLite的底层存储引擎替换为Berkeley DB的数据库引擎。如下图所示:

Oracle Berkeley DB SQL 架构图

Oracle Berkeley DB SQL 架构图

从上面的DBSQL实现图可以看到,Oracle Berkeley DB引入了SQLite的SQL层:包括用户接口(sqlite3(), ODBC, JDBC等)和SQL语言处理层(Tokenizer、Parser及Generator),而底层引擎(虚拟机)则使用了BDB的存储引擎。从而,将原来SQLite基于数据库级别的并发提升一个级别 - 至BDB的基于页(Page)级别的并发,并可以利用BDB的更好的内存管理、数据和事务恢复功能、更多的扩展(如Berkeley DB的db_hotbackup、db_stat、db_archive等一系列命令行工具)。

Oracle Berkeley DB 11g release 2在主流平台经过了严格的、多重的测试和认证(每个平台涵盖以十万计的测试案例),测试平台包括Solaris、*nix、Windows系列(XP, 7, Mobile)、Android 2.0(及以上)、VxWorks等等。此外,我们还提供了大量工具和资料帮助用户熟悉这套最新接口。随版本一起发布的,包括SQLite到Oracle Berkeley DB数据迁移指南、JDBC/ODBC使用指南、全文检索(Full Text Search)向导、空间数据库(R*Tree)向导、Android平台上使用向导、数据库使用手册及调优向导、数据管理员手册、测试集、代码示例等完善的资料,进一步帮助用户放心地来使用我们的产品。了解更多SQLite的语法、API、命令行等帮助,还可以参考SQLite.org上的文档。

总结一下: DBSQL接口是一个1M大小的C语言类库,是一个高效并发的嵌入式数据库。它支持in-memory cache选项,某些场合可作为内存数据库的一个替代方案。它支持C/C++/Java/PHP等语言接口和通过JDBC/ODBC等驱动程序访问。它运行于Unix/POSIX、Windows家族、VxWorks、QNX、Android等平台。和SQLite一样,支持SQL92标准。

Berkeley DB SQL和SQLite使用上的区别

a) 对于用户和开发人员来说,这两个产品是没有区别的。它们在SQL语法、API、命令行交互、PRAGAMAs 等方面都是一致的。我认为,用户可以体验的显著区别有可能是性能和并发了 - 由于SQLite提供的是数据库级别的锁,而Berkeley DB SQL是页(Page)级别的锁,因此后者在绝大多数测试中都会快很多 (如Insert, Update, Delete, 并发操作等)。但是,由于DBSQL提供的细粒度锁的机制,它又会带来一些额外的开销,一些极端的测试用例下会比SQLite慢上少许(但不明显)。并且对于这些极端测试的案例,我们一直在进行性能优化。

b) 对于已有的SQLite应用程序和工具而言,由于这两者在调用接口都是一致的,因而都可以无缝支持。

c) 对于DBA人员来说,除了可以继续使用SQLite原来的管理工具,您还可以使用BDB提供的db_hotbackup、db_stat、db_archive等一系列命令行工具来备份,监控,升级等。另外,您还可以联系Oracle寻求支持。

总体而言,我们有充分理由相信Oracle Berkeley DB SQL将会比SQLite更快,更稳定。同时,我们也将会提供更好的支持服务。

DBSQL接口和BDB key/value接口的比较

我们举个例子说明这两套API在开发上的区别。

假设有一个employee的BDB数据库(注:一个BDB的数据库,即相当于关系型数据库中的一张二维表),我们定义其主键字段为:key{empId, email},定义其value包含字段:value{empName, sex, age, startDate, status}。相应的,对应于Oracle 关系型数据库的表结构为:

TABLE: employee {
empId PRIMARY KEY,
email PRIMARY KEY,
empName,
sex,
age,
startDate,
status
}

接下来,对于数据库的操作,在BDB的语法大致为:
-
要插入一条记录,employeeDatabase.put(key{1000, john.smith@foo.com}, value{'John Smith', 'male', 30, 2005-01-01, 'OK'});
- 随后,如果要更新上面那条记录:
employeeDatabase.put(key{1000, john.smith@foo.com}, value{'John Smith', 'male', 60, 2005-01-01, 'Retired'});
- 如果删除该条记录,则:
employeeDatabase.delete(key{1000, john.smith@foo.com}); // 此处只需要提供key即可
- 查询记录:
employeeDatabase.get(key{1000, john.smith@foo.com}); // 此处只需要提供key即可

在DBSQL中,上面的增删改操作都可以通过标准的SQL语言来进行。如更新一条记录,可写成:

update employee set age=60, status='Retired' where empId=1000 and email='john.smith@foo.com';

总结

最后,需要强调的是,DBSQL是作为对BDB已有功能的一个补充而不是替代。它只是本次BDB发布产品的一个新特性,并且将会像Hash、Queue、集群等功能一样持续下去。它的出现在一定程度上更丰富了BDB的应用场景。用户可以根据自己的需要,选择适合的BDB接口:

  • 当用户需要非常高的性能,管理非关系型数据,或者以Queue、Hash等方式来组织和访问数据的时候,可以继续选择 Oracle Berkeley DB的既有key/value API。如大型企业系统中单点登录、消息队列、工作流等模块,如管理XML、声音、照片、视频等场合,如SOA中的BAM模块、业务规则引擎,如云计算或者云存储节点上,等等。
  • 而在用户需要一个本地持久化的支持SQL的嵌入式数据库时,DBSQL将成为优先的选择。比如,手机的通讯录、个人web站点、桌面端 的应用(如股票软件、浏览器客户端的缓存和存储)及开发工具(如IDE)、中小企业的数据库系统、企业实时系统的一些缓存模块、小型的关系型内存数据库等 等。

正式版本的Oracle Berkeley DB 11g release 2将于2010年3月底发布。Oracle Berkeley DB整个产品家族继续以开源形式发布,并对开源社区提供支持。更多关于Oracle Berkeley DB 11g release 2的新功能和特性,请关注我们的官方网站(http://www.oracle.com/database/berkeley-db/index.html)或者本博客的后续文章。

请至官方网站下载最新版本的Oracle Berkeley DB数据库系列产品,包括Oracle Berkeley DB (及SQL),Oracle Berkeley DB Java版和Oracle Berkeley DB XML数据库。

更多需求和反馈,欢迎留言或者给我发邮件:chao.huang[at]oracle.com。

分类: Berkeley DB, Chao Huang, SQL 标签: ,
  1. arrowcat
    2010年3月25日20:51 | #1

    关注。。。

  2. ken
    2010年3月29日09:34 | #2

    看到这个消息,想起来David曾经写过的一篇博文:
    “…于是斗胆问经理:我们什么时候会加入SQL支持,经理的回答很简练:Never! 大而全不一定就是最好的,最重要的是有自己的特色!…”

  3. chaohuang
    2010年3月29日13:13 | #3

    @ken
    看来你是有心人,谢谢。欢迎留言。

  4. reer
    2010年3月29日15:45 | #4

    关注!

  5. xxx
    2010年4月2日21:57 | #5

    最近在用BDB,有一问题。
    在程序中创建了一个数据库pdbp,key为随机数的组成的字符串,data为随机数。再为pdbp创建一个副库sdbp,key为pdbp的data。并调用set_flags为sdbp设定DB_DUPSORT标识。然后用游标查询sdbp,当data为小于100的随机数时,查询到的结果是正确的,即按从小到大的顺序排列。当data为小于1000的随机数时,查询结果就错了。比如应该是(34,37,39,821,833,899,923,987,999),现在却成了(34,821, 923,37,833,987,39,899,999),请问这是为什么?那个地方调用错了,请指点?

  6. rexzhou
    2010年4月5日22:18 | #6

    能不能这样做:用BDBSQL创建的db文件,然后用Key/Value API访问。或者反过来进行?

  7. chaohuang
    2010年4月8日11:42 | #7

    @rexzhou
    我不推荐这么做。

    DBSQL(更正一下:不是叫BDBSQL,前面我写错了)虽然还是用的原来Key/Value的数据引擎,但是增加很多用来存取metadata(比如表结构信息等)的记录。在DBSQL和Key/Value API间的互操作,有可能会破坏DBSQL,从而无法继续使用DBSQL。当然,你可以使用BDB原来自带的命令行工具,如db_dump将所有DBSQL数据库记录导出。

  8. Emily Fu
    2010年4月8日14:37 | #8

    @xxx
    请根据你的需要设置DB的比较函数,否则BDB会使用默认的比较函数。
    * 设置BTree的比较函数,使用DB->set_bt_compare(),具体请参考http://www.oracle.com /technology/documentation/berkeley-db/db/api_reference/C /dbset_bt_compare.html。
    * 设置Hash的比较函数,使用DB->set_h_compare(),具体请参考http://www.oracle.com /technology/documentation/berkeley-db/db/api_reference/C /dbset_h_compare.html。
    * 设置重复项的比较函数,使用DB->set_dup_compare(),具体请参考http://www.oracle.com /technology/documentation/berkeley-db/db/api_reference/C /dbset_dup_compare.html。

  9. xxx
    2010年4月9日08:53 | #9

    问题解决,非常感谢!!!

  10. stephon
    2010年4月15日13:35 | #10

    请问支持跨平台访问的吧

  11. stephon
    2010年4月15日13:36 | #11

    请问支持跨平台访问的吧?

  12. gaohui
    2010年4月21日14:30 | #12

    首先恭喜研发团队的成员们!
    其次,我有个问题:
    听说BDB是进程安全的,于是我用php多进程对同一个BDB文件进行读和写以及更新,但是却发生了错误:
    Warning: db4::put(): DB_RUNRECOVERY: Fatal error, run database recovery in /data/test.php on line 61
    Warning: db4::put(): DB_RUNRECOVERY: Fatal error, run database recovery in /data/test.php on line 61
    Warning: db4::put(): DB_RUNRECOVERY: Fatal error, run database recovery in /data/test.php on line 61

    请问这是为什么呢?

  13. 赵汝聪
    2010年4月22日14:23 | #13

    @gaohui
    你好,一般这种现象是因为没有Transacton保护导致的。可参考DB源代码目录下的php_db4/samples/transactional_counter.php编写多进程访问代码。

  14. 赵汝聪
    2010年4月22日14:34 | #14

    @stephon
    BDB支持多种主流平台。Unix, Linux, Windows, Android… 不过不知道您指的跨平台访问具体是什么场景,能稍微详细说说吗?谢谢!

  15. gaohui
    2010年4月22日16:42 | #15

    @赵汝聪
    好的,谢谢啦。

  16. stephon
    2010年4月23日09:38 | #16

    @赵汝聪

    是这样的,我们现在想在Linux平台上搭建BDB环境,然后通过windows平台上的应用去连接它。本来timesten的方案可以解决,但是因为考虑到client方式使用timesten性能不高,不知道BDB能否实现类似timesten的功能,谢谢!

  17. 半湖
    2010年4月25日18:33 | #17

    我想让表里的记录按Key值从大到小排列,最大的在第一个,请问如何做到? 我是个新手,还没怎么入门,请高人指教!

  18. chaohuang
    2010年4月26日10:11 | #18

    @半湖
    如果是用的DBSQL,请参考SQL的order by 语法; 如果用的是BDB native API,请参考BDB文档(游标等章节)。

  19. cherry_yue
    2010年5月4日23:54 | #19

    您好!
    我看到官网上Berkeley DB 11gR2已经可以下载了,想问两个问题:
    1.有支持VxWorks平台的版本吗?是哪一个?
    2.如果我有oracle10.2g的数据库表及数据,有什么方法可以将它导入到berkeleydb数据库中?有没有相关的转换软件?
    谢谢!

  20. chaohuang
    2010年5月12日10:02 | #20

    1. 我们产品是以源代码(开源)发布。请参考Vxworks相关章节的文档,从而编译生成Berkeley DB 11gR2的库文件。 文档地址: http://www.oracle.com/technology/documentation/berkeley-db/db/installation/build_vxworks_intro6x.html

    2. 方法1: 你可以参考 – http://www.bdbchina.com/2010/04/berkeley-db-11g-release-2-jdbc编译方法/。 自己写一段Java程序,通过JDBC来导入到Berkeley DB 11gR2。
    方法2: 从oracle10.2g中将数据以文本格式导出, 然后通过Berkeley DB 11gR2的命令行,运行.load命令导入文本数据进到BDB。

  21. BDBDeveloper
    2010年5月15日13:14 | #21

    你好。
    我是刚接触这个数据库,可能问的问题不够档次,见谅。
    我想在C#开发中用SQL查询,我引用了Berkeley命名空间,也引用了11g的dll文件,也安装了客户端。但是我没有找到像SQLite那样的SQLConnection进行连接,然后用SQLCommand这样的类来执行查询语句。我不知道在DBD中如果我写了一个查询语句,要用那个类的哪个方法来执行。

  22. Emily Fu
    2010年5月17日14:21 | #22

    @BDBDeveloper
    你好,目前BDB C# API尚不支持SQL查询,不过BDB DBSQL与SQLite的ADO.Net,所以,建议你使用ADO.Net.

  23. ooseven
    2010年5月21日10:33 | #23

    vc2008编译速度太慢了,我的工程在release下编译一次要一个多小时,最近想升级vc2010,不知道berkeleydb在vc2010下能不能正常使用?

  24. chaohuang
  25. ooseven
    2010年5月21日12:11 | #25

    @chaohuang
    收到,谢谢啦

  26. 半湖
    2010年5月22日11:00 | #26

    请问可以在db文件里写入特定的字符串吗? 写入的字符串不要影响key/value记录. 第二次打开db文件时要能读出写入的字符串

  27. chaohuang
    2010年5月25日18:06 | #27

    @半湖
    不明白你的意思..

    是在自己程序中打开DB文件并写入字符串吗?如果是这样,那就破坏了DB文件的完整性,可能导致数据库无法读取和恢复。

  28. 徐明松
    2010年5月26日10:52 | #28

    我想问一下,BDB Java到J2se上可以运行,那么可以到j2me的类库上运行吗??????

  29. chaohuang
    2010年5月26日13:05 | #29

    @徐明松
    暂时不能在J2ME上使用BDB的Java接口或者BDB Java版。

  30. 半湖
    2010年6月1日19:21 | #30

    我对key/value做了结构化处理, 我改造的BDB支持SQL查询, 我做的查询接口比用原来的dotnet接口速度快了25倍. 现在我的问题是,表结构我只能在每次打开DB文件时,加入db结构(我自己做的接口), 我想把表结构写入DB文件, 这样DB文件就可以成为完整的数据库了, 我现在还不知道如何把表结构信息写入DB文件中@chaohuang

  31. liao5930
    2010年6月2日00:31 | #31

    请问C#用SQL语句操作时,用哪些类连接数据库和执行SQL语句?
    System.Data.OleDb、System.Data.SqlClient还是SQLite?

  32. chaohuang
    2010年6月2日09:31 | #32

    @liao5930
    BDB SQL 暂时还不完全支持ADO.NET API。你可以参考一下http://sourceforge.net/projects/sqlite-dotnet2/,用它来操作BDB SQL。 如果遇到问题,欢迎反馈。

  33. chaohuang
    2010年6月2日09:34 | #33

    @半湖
    听上去不错!但是你想得到什么帮助呢?是关于如何读/写表结构信息吗?你可以直接给我们发邮件: chao.huang[at]oracle[dot]com。

  34. liao5930
    2010年6月2日10:18 | #34

    请问DBD能不能设置密码?通过密码才能访问数据
    另外你上面给的网址http://sourceforge.net/projects/sqlite-dotnet2/
    不知道是我的网络原因还是网址无效
    谢谢!

  35. liao5930
    2010年6月2日16:51 | #35

    BTreeDatabase btreeDB;
    BTreeDatabaseConfig btreeConfig;
    string dbFileName = “”;

    private void InitDataBase()
    {
    try
    {
    dbFileName = “data.db”;
    btreeConfig = new BTreeDatabaseConfig();
    btreeConfig.Duplicates = DuplicatesPolicy.SORTED;
    btreeConfig.ErrorPrefix = “ex_csharp_access”;
    btreeConfig.Creation = CreatePolicy.IF_NEEDED;
    btreeConfig.CacheSize = new CacheInfo(0, 64 * 1024, 1);
    btreeConfig.PageSize = 8 * 1024;
    btreeDB = BTreeDatabase.Open(dbFileName, btreeConfig);
    }
    catch(Exception err)
    {
    MessageBox.Show(err.Message);
    }
    }

    private void button2_Click(object sender, EventArgs e)
    {
    Thread t = new Thread(new ParameterizedThreadStart(tempFon));
    t.IsBackground = true;
    Thread tt = new Thread(new ParameterizedThreadStart(tempFon));
    tt.IsBackground = true;
    t.Start(1);
    tt.Start(2);
    }

    private void tempFon(object obj)
    {
    for (int i = 0; i < 50000; i++)
    {

    DatabaseEntry key = new DatabaseEntry(Encoding.Default.GetBytes(obj.ToString() + i.ToString()));
    DatabaseEntry value = new DatabaseEntry(Encoding.Default.GetBytes(obj.ToString() + i.ToString()));
    try
    {
    btreeDB.Put(key, value);
    }
    catch
    {

    }
    }
    }
    我用两个线程测试写入数据,总是会出错:尝试读取或写入受保护的内存。这通常指示其他内存已损坏。
    我用的是最新版本的BDB,官网上说已经支持多线程、多进程了啊,可为什么还会出现这种错误呢?
    又尝试在线程内Open数据库,发现只有其中一个线程写入了数据,请问这是怎么回事?该如何处理?谢谢!

  36. chaohuang
    2010年6月3日10:45 | #36

    @liao5930
    对你上一个评论的回复:

    BDB数据库不提供授权/权限控制,需要通过自己程序来实现。

    如果那个网站你打不开,试试这个 – http://sqlite.phxsoftware.com/。两者一样的。

  37. Emily Fu
    2010年6月3日11:24 | #37

    对于多线程操作数据库,请使用FreeThreaded属性,具体请参考http://www.oracle.com/technology/documentation/berkeley-db/db/csharp/html/P_BerkeleyDB_BaseDatabase_FreeThreaded.htm。

  38. iamdty
    2010年6月6日11:49 | #38

    DBSQL 使用了BDB的底层引擎,现在是否支持DB API实现复制功能?

  39. chaohuang
    2010年6月7日11:44 | #39

    @iamdty
    SQL接口暂时不提供Replication选项。你的问题很好,我们有计划支持Replication。请关注。

  40. iamdty
    2010年6月8日22:45 | #40

    @chaohuang

    我的问题可能没有说清楚,是我的应用程序用SQL接口,利用BDB底层的复制把数据库备份到备机。
    否则的话我只能用原始的SQL序列自己做日志同步。

  41. guang
    2010年7月15日14:17 | #41

    请问现在DBD支持ADO.NET操作不?像SQLite那样

  42. chaohuang
    2010年7月16日09:45 | #42

    @guang
    你指的是哪个ADO.NET 实现?你试过了吗?有什么问题吗?

    我们测试过
    http://sourceforge.net/projects/sqlite-dotnet2/ ,可以work。

  43. guang
    2010年7月16日18:06 | #43

    chaohuang :@guang 你指的是哪个ADO.NET 实现?你试过了吗?有什么问题吗?
    我们测试过http://sourceforge.net/projects/sqlite-dotnet2/ ,可以work。

    你前几楼说不完全支持ADO。NET。我是用C#操作的

  44. chaohuang
    2010年7月19日11:43 | #44

    @guang
    是的,不完全支持是指:有极端的一个测试情况会失败。我觉得它可能不会影响到你的使用;如果有,欢迎反馈。

    另外,我们有计划解决这个问题。但是暂时给不了具体时间。

  45. 菜鸟BDB
    2010年8月16日12:39 | #45

    请问下各位大虾,我目前碰到一个问题,使用BDB的时候采用了单线程写,多线程读这样一种机制,但是在运行的时候会经常性出现冲突的情况,导致程序无法运行,请问这种问题是什么原因造成的?应该如何解决?

  46. chaohuang
    2010年8月17日09:29 | #46

    @菜鸟BDB
    Hi, 你提供的信息比较有限。请问你是用的什么版本的BDB,使用什么接口,冲突的错误信息是什么?我的初步建议是你可以参考一下BDB文档关于事物处理及死锁等章节,及相关示例代码。

  47. 菜鸟BDB
    2010年8月17日17:29 | #47

    我用的版本是BDB4.8.24,做的操作是一个线程不停的向库中写入数据,写入数据的时候会到库中查询一下是否存在相同的数据(由于库具有一些特殊性,不能将不允许重复的数据当作Key,所以需要通过游标查找一下某个范围的数据是否存在重复的),由于数据量很大,因此这个写入和查询基本上在不停的进行。同时,另外有监听线程监听不同用户的查询请求,一旦有用户请求数据,就替用户查询出相关结果返回给用户。但是这样实现就经常会出现读取数据时发生冲突,导致程序崩溃。我的程序是用C语言写的,接口也是BDB提供的那套C程序接口。
    我也考虑过使用锁机制实现一读一写的情况(这种情况不会发生冲突),但是由于入库操作过于频繁,并且入库前都会查询一下,如果这样实现基本上用户检索请求都无法响应。(入库数据大概有每秒上万条数据吧)所以我不能在检索的时候锁定库。

  48. chaohuang
    2010年8月18日09:46 | #48

    @菜鸟BDB
    请给我发邮件: chao[dot]huang[at]oracle[dot]com. 把具体的描述及错误信息附上,有源代码最好。

  49. 大菜鸟
    2010年9月25日21:33 | #49

    我想用ADO.NET操作DBD,你们提到可以用SQLite的ADO.NET接口,具体的使用方法是什么呢?该如何操作呢?如果有示例代码最好,十分感谢!

  50. linchunsun
    2010年10月12日17:02 | #50

    @大菜鸟
    System.Data.SQLite( http://sqlite.phxsoftware.com/) 是一个第三方的基于SQLite的ADO.NET Provider, 因为BDB SQLite完全兼容SQLite的接口,所以这套Provider也可以工作在BDB SQL上。
    请下载源码包,将其中的sqlite3.c替换成为BDB的sqlite3.c, 并重新编译工程即可得到一个以BDB为底层引擎的System.Data.SQLite库。你可以通过这个库提供的ADO.NET接口来操作BDB。

评论分页
1 2 862
本文的评论功能被关闭了.
Դ