当C++遇到IOS应用开发---SQLITE篇

 

当C++遇到IOS应用开发---SQLITE篇

分类: ios   4088人阅读  评论(7)  收藏  举报

      大约是一年多前开始接触OBJECT-C并进行IOS开发,一上来就被OBJECT里那种近似于“丑陋”的方法命名及其[]调用方式给“强暴”了一把,所以在后来的开发过程中,开始思考如何能使用C++方式来混编开发。经过了近半年多的代码积累和开发调试,开始有了这个系列的内容。本系列BLOG的主要方向是引导IOS开发者特别是之前用C#和C++的朋友,可以一步步搭建属于拥有.net风格的基本类库,并快速进行IOS应用的开发。不过前提是读者和开发者有一定的C++开发经验,以免遇到一些诡异问题时,能够快速找出解决方案。

     好了,开始今天的内容吧!
    
     在XCODE开发时,可以很方便的引入SQLITE库来开发基于支持本地数据库的应用。但网上查到的大部分内容都只是介绍一个简单的示例而已。众所周知,微软的ADO.NET很好很强大,且已发展多年,其使用方式也很灵活多样。如果将其哪怕部分实现方式“移植”到IOS上,那即使是.NET新手也可以很快适应。在网上基于C++实现连接SQLITE的示例代码多如牛毛,但封装的却不甚理想。最后笔者在CODEPROJECT上终于挖出一个老项目,里面基本上定义了一些实现方式和框架且实现得短小精悍,且利于扩充,所以我就在其基础上,逐步加入了一些新的功能。所以就有了今天的内容。

     在ADO.NET中定义了一些基本的数据库访问组件,比如DataSet, DataTable,DataRow,以及事务等。下面就来看一下用C++实现这些对象的方式。

      首先是DataSet:

复制代码
[cpp]  view plain copy
  1. typedef class CppSQLite3DB  
  2. {  
  3. public:  
  4.   
  5.     CppSQLite3DB();  
  6.      
  7.     CppSQLite3DB(const char* szFile);  
  8.   
  9.     virtual ~CppSQLite3DB();  
  10.   
  11.     void open(const char* szFile);  
  12.   
  13.     void close();  
  14.   
  15.     bool tableExists(const char* szTable);  
  16.   
  17.     int execDML(const char* szSQL);  
  18.         
  19.     //该方法为execNoQuery的封装  
  20.     int execNoQuery(const char* szSQL);  
  21.   
  22.     CppSQLite3Query execQuery(const char* szSQL);  
  23.   
  24.     int execScalar(const char* szSQL);  
  25.   
  26.     CppSQLite3Table getTable(const char* szSQL);  
  27.   
  28.     CppSQLite3Statement compileStatement(const char* szSQL);  
  29.   
  30.     sqlite_int64 lastRowId();  
  31.   
  32.     void interrupt() { sqlite3_interrupt(mpDB); }  
  33.   
  34.     void setBusyTimeout(int nMillisecs);  
  35.   
  36.     static const char* Version() { return SQLITE_VERSION; }  
  37.   
  38. public:  
  39.   
  40.     CppSQLite3DB(const CppSQLite3DB& db);  
  41.     CppSQLite3DB& operator=(const CppSQLite3DB& db);  
  42.   
  43.     sqlite3_stmt* compile(const char* szSQL);  
  44.   
  45.     void checkDB();  
  46.   
  47.     sqlite3* mpDB;  
  48.     int mnBusyTimeoutMs;  
  49. } DB;  


复制代码


      需要注意的是这里使用的某些方法名称是基于笔者以前开发DISCUT!NT时使用的DBHelper.cs类时使用的名称,这也是让团队里的老成员能很快适应这个框架的一个原因。

     上面基本上就是对数据库及数据表(DataTable)进行基本操作的封装。而数据表及数据行的定义如下:

复制代码
[cpp]  view plain copy
  1. typedef class CppSQLite3Table  
  2. {  
  3. public:  
  4.   
  5.     CppSQLite3Table();  
  6.   
  7.     CppSQLite3Table(const CppSQLite3Table& rTable);  
  8.   
  9.     CppSQLite3Table(char** paszResults, int nRows, int nCols);  
  10.   
  11.     virtual ~CppSQLite3Table();  
  12.   
  13.     CppSQLite3Table& operator=(const CppSQLite3Table& rTable);  
  14.   
  15.     int fieldsCount();  
  16.   
  17.     int rowsCount();  
  18.   
  19.     const char* fieldName(int nCol);  
  20.   
  21.     const char* fieldValue(int nField);  
  22.     const char* operator[](int nField);  
  23.   
  24.     const char* fieldValue(const char* szField);  
  25.     const char* operator[](const char* szField);  
  26.          
  27.     int getIntField(int nField, int nNullValue=0);  
  28.     int getIntField(const char* szField, int nNullValue=0);  
  29.   
  30.     double getFloatField(int nField, double fNullValue=0.0);  
  31.     double getFloatField(const char* szField, double fNullValue=0.0);  
  32.   
  33.     const char* getStringField(int nField, const char* szNullValue="");  
  34.     const char* getStringField(const char* szField, const char* szNullValue="");  
  35.   
  36.     bool fieldIsNull(int nField);  
  37.     bool fieldIsNull(const char* szField);  
  38.   
  39.     void setRow(int nRow);  
  40.     const CppSQLite3TableRow getRow(int nRow);  
  41.   
  42.     void finalize();  
  43.   
  44. private:  
  45.   
  46.     void checkResults();  
  47.   
  48.     int mnCols;  
  49.     int mnRows;  
  50.     int mnCurrentRow;  
  51.     char** mpaszResults;  
  52. } Table;  
  53.   
  54. typedef class CppSQLite3TableRow  
  55. {  
  56. private:  
  57.     Table& inTable;  
  58. public:  
  59.     const char* operator[](int nField);  
  60.     const char* operator[](const char* szField);  
  61.     CppSQLite3TableRow( Table& table):inTable(table){}  
  62.     virtual ~CppSQLite3TableRow(void)  
  63.     {};  
  64.   
  65. } Row;  


复制代码

     注意:关于Row的实现是老代码中所没有的,因为要考虑到尽量逼成ADO.NET的对象结构,所以这里加入了进来;
    有了上面的对象支持,接下来就可以写一个封装类DBHelper.h来实现常用数据访问操作了,如下:

复制代码
[cpp]  view plain copy
  1. #ifndef DBHelper_h  
  2. #define DBHelper_h  
  3.   
  4. #include "SQLiteHelper.h"  
  5.   
  6. //#include <ctime>  
  7. #include <iostream>  
  8.   
  9. using namespace std;  
  10. using namespace SQLiteWrapper;  
  11.   
  12. namespace SQLiteWrapper {  
  13. class DBHelper  
  14. {  
  15.      
  16.         
  17. private:  
  18.     DBHelper()  
  19.     {}  
  20.     virtual ~DBHelper(void)  
  21.     {}  
  22.      
  23. public:  
  24.     static DB db;  
  25.    
  26.     static DB loadDb()  
  27.     {  
  28.         DB database;  
  29.         {  
  30.             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  
  31.             NSArray *arr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory/*NSCachesDirectory*/, NSUserDomainMask, YES);  
  32.             NSString *path = [arr objectAtIndex:0];  
  33.             path = [path stringByAppendingPathComponent:@"MySqlLitePath"];  
  34.   
  35.             // create directory for db if it not exists  
  36.             NSFileManager *fileManager = [[NSFileManager alloc] init];  
  37.             BOOL isDirectory = NO;  
  38.             BOOL exists = [fileManager  fileExistsAtPath:path isDirectory:&isDirectory];  
  39.             if (!exists) {  
  40.                 [fileManager createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:nil];  
  41.                 if (![fileManager fileExistsAtPath:path]) {  
  42.                     [NSException raise:@"FailedToCreateDirectory" format:@"Failed to create a directory for the db at '%@'",path];  
  43.                 }  
  44.             }  
  45.             [fileManager release];  
  46.             // create db object  
  47.             NSString *dbfilePath = [path stringByAppendingPathComponent:@"Blogs"];  
  48.             std::string dbpathstr =[dbfilePath UTF8String];  
  49.   
  50.          
  51.             const char *dbpath = dbpathstr.c_str();//"/Users/MySqlLitePath/Blogs";  
  52.             database.open(dbpath);  
  53.             [pool release];  
  54.         }  
  55.         return database;  
  56.     }  
  57.      
  58.      
  59.     static bool tableExists(const char* szTable)  
  60.     {  
  61.        return db.tableExists(szTable);  
  62.     }  
  63.      
  64.         
  65.     static int execNoQuery(const char* szSQL)  
  66.     {  
  67.         return db.execDML(szSQL);  
  68.     }  
  69.      
  70.     static int execNoQuery(const NSString* szSQL)  
  71.     {  
  72.         return db.execDML([szSQL UTF8String].c_str());  
  73.     }  
  74.      
  75.     static Query execQuery(const char* szSQL)  
  76.     {  
  77.         return db.execQuery(szSQL);  
  78.     }  
  79.      
  80.     static Query execQuery(const NSString* szSQL)  
  81.     {  
  82.         return db.execQuery([szSQL UTF8String].c_str());  
  83.     }  
  84.      
  85.     static Query execScalar(const char* szSQL)  
  86.     {  
  87.         return db.execQuery(szSQL);  
  88.     }  
  89.      
  90.     static int execScalar(const NSString* szSQL)  
  91.     {  
  92.         return db.execScalar([szSQL UTF8String].c_str());  
  93.     }  
  94.      
  95.     static Table getTable(const char* szSQL)  
  96.     {  
  97.         return db.getTable(szSQL);  
  98.     }  
  99.      
  100.     static Table getTable(const NSString* szSQL)  
  101.     {  
  102.         return db.getTable([szSQL UTF8String].c_str());  
  103.     }  
  104.      
  105.     static Statement compileStatement(const char* szSQL)  
  106.     {  
  107.         return db.compileStatement(szSQL);  
  108.     }  
  109.      
  110.     static Statement compileStatement(const NSString* szSQL)  
  111.     {  
  112.         return db.compileStatement([szSQL UTF8String].c_str());  
  113.     }  
  114.      
  115.     static sqlite_int64 lastRowId()  
  116.     {  
  117.         return db.lastRowId();  
  118.     }  
  119.      
  120.     
  121.     static void setBusyTimeout(int nMillisecs)  
  122.     {  
  123.         db.setBusyTimeout(nMillisecs);  
  124.     }  
  125.         
  126. };  
  127. }  
  128.   
  129. DB DBHelper::db = DBHelper::loadDb(); //在全局区进行对象初始化操作。  


复制代码


       这里要注意的一点就是,在其静态方法loadDb()中,要使用Object-C中的NSAutoreleasePool 对象来“框住”数据库的加载逻辑代码,否则会在下面这一样产生内存泄露:
     
[cpp]  view plain copy
  1. NSArray *arr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory/*NSCachesDirectory*/, NSUserDomainMask, YES);  


    
           
     下面我们来看如何使用它,上DEMO, 呵呵。

     判断当前数据库中是否存在某个TABLE,如存在则删除,之后创建新表:
[cpp]  view plain copy
  1. if(DBHelper::tableExists("emp")){  
  2.     DBHelper::execNoQuery("drop table emp;");  
  3. }  
  4. DBHelper::execNoQuery("create table emp(empno int, empname char(20));");    


       

      接着向新表中接入数据并返回插入成功的条数,如下:
[cpp]  view plain copy
  1. int nRows = DBHelper::execNoQuery("insert into emp values (7, 'David Beckham');");  
  2. cout << nRows << " rows inserted" << endl;  




      然后进行UPDATE,DELETE操作并返回操作的记录条数:
[cpp]  view plain copy
  1. nRows = DBHelper::execNoQuery("update emp set empname = 'Christiano Ronaldo' where empno = 7;");  
  2. cout << nRows << " rows updated" << endl;  
  3.       
  4. nRows = DBHelper::execNoQuery("delete from emp where empno = 7;");  
  5. cout << nRows << " rows deleted" << endl;  




       基于事务属性进行批量操作,以提升性能。
复制代码
 
[cpp]  view plain copy
  1. int nRowsToCreate(50000);  
  2.   cout << endl << "Transaction test, creating " << nRowsToCreate;  
  3.   cout << " rows please wait..." << endl;  
  4.   DBHelper::execNoQuery("begin transaction;");  
  5.         
  6.   for (int i = 0; i < nRowsToCreate; i++)  
  7.   {  
  8.       char buf[128];  
  9.       sprintf(buf, "insert into emp values (%d, 'Empname%06d');", i, i);  
  10.       DBHelper::execNoQuery(buf);  
  11.   }  
  12.   DBHelper::execNoQuery("commit transaction;");  


复制代码


      进行select count操作:
[cpp]  view plain copy
  1. cout << DBHelper::execScalar("select count(*) from emp;") << " rows in emp table in ";  



      使用Buffer进行SQL语句构造:
复制代码
 
[cpp]  view plain copy
  1. Buffer bufSQL;  
  2.  bufSQL.format("insert into emp (empname) values (%Q);""He's bad");  
  3.  cout << (const char*)bufSQL << endl;  
  4.  DBHelper::execNoQuery(bufSQL);  
  5.  DBHelper::execNoQuery(bufSQL);  
  6.        
  7.  bufSQL.format("insert into emp (empname) values (%Q);", NULL);  
  8.  cout << (const char*)bufSQL << endl;  
  9.  DBHelper::execNoQuery(bufSQL);  

复制代码


      遍历数据集方式1:
复制代码
 
[cpp]  view plain copy
  1. Query q = DBHelper::execQuery("select * from emp order by 1;");  
  2.        
  3.  for (int fld = 0; fld < q.fieldsCount(); fld++){  
  4.      cout << q.fieldName(fld) << "(" << q.fieldDeclType(fld) << ")|";  
  5.  }  
  6.  cout << endl;  
  7.        
  8.  while (!q.eof()){  
  9.      cout << q.fieldValue(0) << "|" ;  
  10.      cout << q.fieldValue(1) << "|" << endl;              
  11.      cout << q.fieldValue("empno") << "||" ;  
  12.      cout << q.fieldValue("empname") << "||" << endl;  
  13.   
  14.      //或使用[]索引,效果同q.fieldValue  
  15.      cout << q[0] << "|" ;  
  16.      cout << q[1] << "|" << endl;  
  17.      cout << q["empno"] << "||" ;  
  18.      cout << q["empname"] << "||" << endl;  
  19.      q.nextRow();  
  20.  }  

复制代码
   
      遍历数据集方式2:
复制代码
  
[cpp]  view plain copy
  1. Table t = DBHelper::getTable("select * from emp order by 1;");  
  2.          
  3.    for (int fld = 0; fld < t.fieldsCount(); fld++){  
  4.        cout << t.fieldName(fld) << "|";  
  5.    }  
  6.    for (int row = 0; row < t.rowsCount(); row++){    
  7.        Row r= t.getRow(row);  
  8.        cout << r["empno"] << " " << r["empname"] << "|";  
  9.        cout << endl;  
  10.    }  


复制代码


      预编译Statements测试(使用场景不多):
复制代码
  
[cpp]  view plain copy
  1. cout << endl << "Transaction test, creating " << nRowsToCreate;  
  2.    cout << " rows please wait..." << endl;  
  3.    DBHelper::execNoQuery("drop table emp;");  
  4.    DBHelper::execNoQuery("create table emp(empno int, empname char(20));");  
  5.    DBHelper::execNoQuery("begin transaction;");  
  6.    Statement stmt = DBHelper::compileStatement("insert into emp values (?, ?);");  
  7.    for (int i = 0; i < nRowsToCreate; i++){  
  8.        char buf[16];  
  9.        sprintf(buf, "EmpName%06d", i);  
  10.        stmt.bind(1, i);  
  11.        stmt.bind(2, buf);  
  12.        stmt.execDML();  
  13.        stmt.reset();  
  14.    }  
  15.          
  16.    DBHelper::execNoQuery("commit transaction;");  


复制代码


      最后我们只要找一个相应的 .m 文件改成 .mm 后缀,将上面测试语句执行一下,就可以了。

     好了,今天的内容就先到这里了。

      下面是源码下载地址:

     http://files.cnblogs.com/daizhj/DbHelper_ios.zip

 

     原文链接:http://www.cnblogs.com/daizhj/archive/2012/11/06/2756521.html

     作者: daizhj, 代震军  
     微博: http://weibo.com/daizhj
     Tags:ios, c++, sqlite

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在C++中更新SQLite3中的二进制数据,您可以使用SQLite3提供的BLOB类型。以下是一个简单的示例代码,它演示如何更新一个名为"my_table"的表中的二进制数据列"my_blob_column": ```c++ #include <sqlite3.h> #include <iostream> #include <fstream> using namespace std; int main() { // Open database sqlite3 *db; int rc = sqlite3_open("my_database.db", &db); if (rc != SQLITE_OK) { cerr << "Error opening database: " << sqlite3_errmsg(db) << endl; sqlite3_close(db); return 1; } // Read binary data from a file ifstream file("my_binary_data.bin", ios::binary | ios::ate); if (!file.is_open()) { cerr << "Error opening file." << endl; sqlite3_close(db); return 1; } streamsize size = file.tellg(); file.seekg(0, ios::beg); char *buffer = new char[size]; if (!file.read(buffer, size)) { cerr << "Error reading file." << endl; delete[] buffer; sqlite3_close(db); return 1; } file.close(); // Update binary data in database sqlite3_stmt *stmt; const char *sql = "UPDATE my_table SET my_blob_column = ? WHERE id = 1;"; rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); if (rc != SQLITE_OK) { cerr << "Error preparing statement: " << sqlite3_errmsg(db) << endl; delete[] buffer; sqlite3_close(db); return 1; } sqlite3_bind_blob(stmt, 1, buffer, size, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); if (rc != SQLITE_DONE) { cerr << "Error updating data: " << sqlite3_errmsg(db) << endl; delete[] buffer; sqlite3_finalize(stmt); sqlite3_close(db); return 1; } // Cleanup delete[] buffer; sqlite3_finalize(stmt); sqlite3_close(db); return 0; } ``` 在这个示例中,我们首先打开了一个名为"my_database.db"的SQLite3数据库。然后,我们从一个名为"my_binary_data.bin"的文件中读取二进制数据,并将其存储在一个名为"buffer"的字符数组中。接下来,我们准备了一个SQL语句,使用"sqlite3_prepare_v2"函数编译该语句,并使用"sqlite3_bind_blob"函数绑定二进制数据到该语句中。最后,我们使用"sqlite3_step"函数执行该语句并更新数据库中的二进制数据。最后,我们清理并关闭数据库。 请注意,在这个示例中,我们使用了一个名为"SQLITE_TRANSIENT"的特殊标记来告诉SQLite3库,我们正在使用动态分配的内存来存储二进制数据。这意味着SQLite3库将复制这些数据,而不是仅仅在内部保存指向该数据的指针。这可以确保在更新期间,我们的内存不会被释放或修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值