开发者社区> 问答> 正文

使用IBPP在C++中操作FireBird/Interbase数据库:报错

FireBird是一种小巧的关系型数据库,它有多种版本,包括服务器版(象MySQL),单机版(象Access)以及嵌入式(象SQLite)。而且不管是服务器版还是嵌入式版它都完整支持视图、触发器、存储过程等SQL高级特性。
问题是它提供的C API“不太友好”,不容易掌握(特别是我),所以我一直只会在C++Builder里编程使用FireBird(因为FireBird源于 Borland的InterBase,VCL库自带了InterBase的组件),直到有一天我发现了IBPP...

IBPP是一个 FireBird/Interbase数据库API的C++包装,使用起来也比较简单,只要把IBPP源码目录里的 "core/all_in_one.cpp"加入工程,在代码中包含头文件"ibpp.h",最后根据操作系统预定义一个IBPP_WINDOWS或 IBPP_UNIX的宏即可。
另外,当然还要安装FireBird客户端,如果只是学习,我们可以下载嵌入式版本。IBPP首先要找到FireBird的动态库,在Windows下, IBPP在下面的路径中查找:

  • 在程序目录下查找fbembed.dll
  • 在程序目录下查找fbclient.dll
  • 依据DefaultInstance注册表键值查找fbclient.dll
  • 在系统定义位置查找fbclient.dll
  • 在系统定义位置查找gds32.dll


FireBird主页:http://www.ibphoenix.com
IBPP主页:http://www.ibpp.org
关于FireBird的中文教程我推荐姚启红编著的《Borland InterBase7.0 应用开发指南》(网上一找一大堆)。

例一,基本应用

  1. #include <iostream>
  2. #include <ibpp.h>
  3. using namespace std;
  4.  
  5. //数据库名,根据你的情况更改。
  6. const char* g_szDBName = "d:\\test.fdb";
  7. //服务器名,针对服务器版本。对于嵌入式,它应该是""
  8. const char* g_szServerName = "";
  9.  
  10. //这里的用户名和密码是FireBird默认值,对于服务器版,用你自己的密码
  11. //对于嵌入式,就是这个(FireBird嵌入式版没有加密功能)。
  12. const char*    g_szUserName = "SYSDBA";
  13. const char* g_szPassword = "masterkey";
  14.  
  15. int main()
  16. {
  17.     IBPP::Database db = IBPP::DatabaseFactory(g_szServerName,
  18.         g_szDBName,
  19.         g_szUserName,
  20.         g_szPassword);
  21.     //建立数据库
  22.     db->Create(3);
  23.     //连接数据库
  24.     db->Connect();
  25.  
  26.     IBPP::Transaction tr = IBPP::TransactionFactory(db);
  27.     tr->Start();
  28.     try{
  29.         IBPP::Statement st = IBPP::StatementFactory(db,tr);
  30.         //建立数据表
  31.         st->Execute(
  32.             "CREATE TABLE TESTTABLE("
  33.             "    ID INTEGER NOT NULL PRIMARY KEY,"
  34.             "    RNO VARCHAR(10) NOT NULL UNIQUE,"
  35.             "    SHIFT VARCHAR(5) NOT NULL"
  36.             "        CHECK(SHIFT IN('A','B','C','D')),"
  37.             "    LINE CHAR(20) NOT NULL,"
  38.             "    SL COMPUTED BY(SHIFT||'.'||LINE),"
  39.             "    EMP CHAR(20)"
  40.             "    )"
  41.             );
  42.         tr->CommitRetain();
  43.         //插入数据,华安和小强
  44.         st->Execute(
  45.             "INSERT INTO TESTTABLE(ID,RNO,SHIFT,LINE,EMP)"
  46.             "VALUES(1,'B9527','B','DAZHA','Hua,An')"
  47.         );
  48.         st->Execute(
  49.             "INSERT INTO TESTTABLE(ID,RNO,SHIFT,LINE,EMP)"
  50.             "VALUES(2,'B9528','B','ZHUANGSI','Xiao,Qiang')"
  51.         );
  52.  
  53.         tr->CommitRetain();
  54.         //插入数据,石榴和祝枝山
  55.         st->Execute(
  56.             "INSERT INTO TESTTABLE(ID,RNO,SHIFT,LINE,EMP)"
  57.             "VALUES(3,'B9525','A','DAZHA','Shi,Liu')"
  58.         );
  59.         //SHIFT只能是ABCD,看看写X会有什么情况发生
  60.         st->Execute(
  61.             "INSERT INTO TESTTABLE(ID,RNO,SHIFT,LINE,EMP)"
  62.             "VALUES(4,'B9526','X','DAZHA','Zhu,ZiShang')"
  63.         );
  64.         tr->Commit();
  65.     }
  66.     catch(IBPP::SQLException &e){
  67.         cerr << e.what() << endl;
  68.         tr->Rollback();
  69.     }
  70.  
  71.     tr->Start();
  72.     try{
  73.         IBPP::Statement st = IBPP::StatementFactory(db,tr);
  74.         st->Execute("SELECT RNO, EMP FROM TESTTABLE");
  75.         //显示SELECT得到的数据
  76.         while(st->Fetch())
  77.         {
  78.             string rno, emp;
  79.             st->Get("RNO",rno);
  80.             st->Get("EMP",emp);
  81.             cout << "RNO:" << rno << " EMP:" << emp << endl;
  82.         }
  83.         tr->Commit();
  84.     }
  85.     catch(IBPP::SQLException &e){
  86.         cerr << e.what() << endl;
  87.         tr->Rollback();
  88.     }
  89. }


要让程序正确执行,就得保证程序能找到FireBird的动态链接库。对于嵌入式版本来说,下载后把压缩包中的fbembed.dll, firebird.conf,firebird.msg,ib_util.dll以及intl文件夹解压到你的程序目录中就可以运行了。对于服务器版,只要安装客户端并把fbclient.dll放到程序目录里就行(当然权限也要有啦)。
上例中使用IBPP的步骤是:

  1. 用工厂函数IBPP::DatabaseFactory生成IBPP::Database实例,然后调用它的Connect()成员变量连接数据库。
  2. 如果要建立数据库的话,使用Database::Create(int dialect)。其中dialect一般取值为3,它的含义如下:
SQL Dialect含义
1兼容InterBase 5.5及更早版本。
21和3的过渡版本。
3InterBase 6.0及以后版本,可以使用分隔符,精确数字和时间格式。
  1. 使用工厂函数IBPP::TransactionFactory生成IBPP::Transaction实例,然后用它的Start()开始事务,最后用Commit()来提交事务或Rollback()撤销事务。
  2. 使用工厂函数IBPP::StatementFactory生成IBPP::Statement实例,然后用它的Execute()来执行SQL命令。
  3. 值 得注意的是,IBPP中的Database,Transaction,Statement等由工厂函数返回的数据都是智能指针类型,它们能够在退出有效域 时自动执行一些保证完整性的操作。(比如我们使用tr->Start()后即便不调用tr->Commit(),在超出tr变量作用域后系统 也会自动调用Commit的)
  1. #include <iostream>
  2. #include <algorithm>
  3. #include <assert.h>
  4. #include <ibpp.h>
  5. using namespace std;
  6.  
  7. //数据库名,根据你的情况更改。
  8. const char* g_szDBName = "d:\\test.fdb";
  9. //服务器名,针对服务器版本。对于嵌入式,它应该是""
  10. const char* g_szServerName = "";
  11.  
  12. //这里的用户名和密码是FireBird默认值,对于服务器版,用你自己的密码
  13. //对于嵌入式,就是这个(FireBird嵌入式版没有加密功能)。
  14. const char*    g_szUserName = "SYSDBA";
  15. const char* g_szPassword = "masterkey";
  16.  
  17. int main()
  18. {
  19.     IBPP::Database db;
  20.     db = IBPP::DatabaseFactory(g_szServerName,
  21.         g_szDBName,
  22.         g_szUserName,
  23.         g_szPassword);
  24.     //连接数据库
  25.     db->Connect();
  26.  
  27.     IBPP::Transaction tr = IBPP::TransactionFactory(db);
  28.     tr->Start();
  29.     try{
  30.         IBPP::Statement st = IBPP::StatementFactory(db,tr);
  31.         //建立数据表
  32.         st->ExecuteImmediate(
  33.             "CREATE TABLE BlobTable("
  34.             "    ID INTEGER NOT NULL,"
  35.             "    RES BLOB)"
  36.             );
  37.         //生成器
  38.         st->ExecuteImmediate(
  39.             "CREATE GENERATOR BlobTable_ID_Gen"
  40.             );
  41.         //触发器
  42.         st->ExecuteImmediate(
  43.             "CREATE TRIGGER BlobTable_BI_ID FOR BlobTable "
  44.             "ACTIVE BEFORE INSERT POSITION 0 "
  45.             "AS "
  46.             "BEGIN "
  47.             "    IF(NEW.ID IS NULL) THEN "
  48.             "        NEW.ID = GEN_ID(BlobTable_ID_Gen, 1); "
  49.             "END"
  50.         );
  51.         tr->CommitRetain();
  52.        
  53.         st->Prepare("INSERT INTO BlobTable(RES) VALUES(?)");
  54.         assert(1 == st->Parameters());
  55.         //插入Blob,一块100字节的内存
  56.         {
  57.             IBPP::Blob res = IBPP::BlobFactory(db,tr);
  58.             res->Create();
  59.             char buf[100];
  60.             for(int i=0; i<100; i++) buf[i]=i;
  61.             res->Write(buf,sizeof(buf));
  62.             res->Close();
  63.             st->Set(1,res); //第一个问号
  64.             st->Execute();
  65.         }
  66.         //插入Blob,一串字符串
  67.         {
  68.             st->Set(1,string("HAHAHAHAHAHA..."));
  69.             st->Execute();
  70.         }
  71.         tr->Commit();
  72.     }
  73.     catch(IBPP::SQLException &e){
  74.         cerr << e.what() << endl;
  75.         tr->Rollback();
  76.     }
  77.  
  78.     tr->Start();
  79.     try{
  80.         IBPP::Statement st = IBPP::StatementFactory(db,tr);
  81.         st->Execute("SELECT * FROM BlobTable");
  82.         //显示SELECT得到的数据
  83.         while(st->Fetch())
  84.         {
  85.             int id;
  86.             IBPP::Blob res = IBPP::BlobFactory(db,tr);
  87.  
  88.             st->Get(1,id);
  89.             st->Get(2,res);
  90.            
  91.             //显示Blob内容
  92.             res->Open();
  93.             int size;
  94.             res->Info(&size,NULL,NULL);
  95.             cout << id << " size is " << size << endl;
  96.             char c;
  97.             while(res->Read(&c,1))
  98.             {
  99.                 cout << (int)c << ' ';
  100.             }
  101.             cout << endl;
  102.         }
  103.         tr->Commit();
  104.     }
  105.     catch(IBPP::SQLException &e){
  106.         cerr << e.what() << endl;
  107.         tr->Rollback();
  108.     }
  109. }


本例连接例一建立的数据库,然后新建一个名为BlobTable的表,里面只有ID和RES字段,而且还为ID字段建立了一个生成器和触发器,这样就可以 让FireBird自动为ID赋值了,具体内容可以看FireBird教程。与例一不同的是这里使用了IBPP::Statement的 ExecuteImmediate()方法,它立即执行其中的SQL语句,而Execute()则有一个先准备、再执行的过程,对于只执行一次的SQL, 可以使用ExecuteImmediate(),如果执行次数较多,则建议使用Execute()。
RES字段是一个Blob型字段,从本例可以看出给Blob字段赋值的方法是:

  1. 用IBPP::Statement的Prepare()方法准备SQL插入语句,其中的Blob字段使用问号?代替。
  2. 用工厂函数IBPP::BlobFactory生成一个IBPP::Blob实例,然后分别调用它的Create(),Write()和Close()输入数据,最后用IBPP::Statement的Set()方法写入。
  3. 所有数据准备就绪后,执行IBPP::Statement的Execute()方法。

如果只想往Blob中存放文本数据,也可以直接用IBPP::Statement的Set()方法写入std::string类型。
从Blob字段取值的方法正好相反:

  1. 用IBPP::Statement的Execute()方法或ExecuteImmediate()方法执行SELECT语句。
  2. 使用IBPP::Statement的Fetch()方法提取出当前行的数据。
  3. 使用IBPP::Statement的Get()方法取出指定列的数据,如果是Blob类型,则使用IBPP::Blob实例作为输入。
  4. 最后分别调用IBPP::Blob的Open(),Read()和Close()方法取出数据(IBPP::Blob也是一个智能指针类型,所以上例中没有显式地使用Close()关闭)。

展开
收起
kun坤 2020-06-06 13:49:18 1119 0
1 条回答
写回答
取消 提交回答
  • "在代码中包含头文件"ibpp.h",最后根据操作系统预定义一个IBPP_WINDOWS或 IBPP_UNIX的宏即可。"
    这句话是如何实现的?

    ######顶!很有实际操作价值!
    2020-06-06 13:49:26
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
2022 DTCC-阿里云一站式数据库上云最佳实践 立即下载
云时代的数据库技术趋势 立即下载
超大型金融机构国产数据库全面迁移成功实践 立即下载