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

来源:互联网 发布:淘宝今日购物车宝贝 编辑:程序博客网 时间:2024/04/29 21:10
 

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

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

      大约是一年多前开始接触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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  1. NSArray *arr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory/*NSCachesDirectory*/, NSUserDomainMask, YES);  


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

    判断当前数据库中是否存在某个TABLE,如存在则删除,之后创建新表:
[cpp] view plaincopy
  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 plaincopy
  1. int nRows = DBHelper::execNoQuery("insert into emp values (7, 'David Beckham');");  
  2. cout << nRows << " rows inserted" << endl;  




     然后进行UPDATE,DELETE操作并返回操作的记录条数:
[cpp] view plaincopy
  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 plaincopy
  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 plaincopy
  1. cout << DBHelper::execScalar("select count(*) from emp;") << " rows in emp table in ";  



     使用Buffer进行SQL语句构造:
复制代码
 
[cpp] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 平板输不了密码怎么办 华硕笔记本键盘打不开怎么办 电脑打不开rar文件怎么办 苹果手机淘宝卡怎么办 淘宝联盟网址打不开怎么办 淘宝买东西卖家不退货怎么办 手机清理后微信打不开视频怎么办 搜索历史已关闭怎么办 微博重新激活怎么办 淘宝直播反应慢怎么办 微信新设备无法登录怎么办 dnf自动连接失败怎么办 APP注册没有成功怎么办 忘记绑定微信号怎么办 淘宝钻石绣被骗怎么办 safari出现闪退怎么办 12123手机号被占用怎么办 12306换手机了怎么办 51串口打开失败怎么办 打开com串口失败怎么办 xp串口打开失败怎么办 台式电脑没光驱怎么办 相机功能用不了怎么办 支付宝登录失败怎么办 淘宝号限制登陆怎么办 海信电视看不了怎么办 淘宝不记得密码怎么办 淘宝号忘记了怎么办 号码注销支付宝怎么办 旺旺号限制登录怎么办 淘宝单被监控了怎么办 晚上手机网速慢怎么办 卖家淘金币怎么办 淘宝不能下单怎么办 淘宝店铺失效了怎么办 淘宝订单没货怎么办 拼多多预售到期怎么办 直通车出价太高怎么办 htc手机黑屏打不开怎么办 商品被屏蔽该怎么办 遇到恶意差评怎么办