一,sqlite 简介

前面写了一篇博文讲如何在 C# 中使用 ADO 访问各种数据库,在移动开发和嵌入式领域也有一个轻量级的开源关系型数据库-sqlite。它的特点是零配置(无需服务器),单磁盘文件存储数据(就像fopen一样),平台无关性,使用起来简单又高效。这些特点让其非常适合移动开发和嵌入式领域。当然,sqlite 也因其力求简单高效,也就限制了它对并发,海量数据的处理。下面,我就再接再厉,来讲讲如何在 iOS 中使用 sqlite 库和第三方封装库 FMDB,以及介绍一个 MAC 下开源的可视化 sqlite 浏览器。

本文源码:https://github.com/kesalin/iOSSnippet/tree/master/SQLiteDemo

二,在 iOS 中的使用

在 iOS 中 sqlite3 库是一套纯 C 的接口,因此很方便地就可以在 obj-c 源码中无痕使用它,而且其使用方式与用 ADO 方式操作数据库大同小异-少了创建数据库链接一环而已(因为 sqlite 没有服务器的概念也就无需链接了)。

  • 首先,需要引入 libsqlite3.0.dylib:

然后包含头文件:

#import "/usr/include/sqlite3.h"

  • 打开或创建数据库
SQLITE_API int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);

使用示例:(dbPath 为 NSString *)

    // open database
//
int state = sqlite3_open([dbPath UTF8String], &database);
if (state == SQLITE_OK) {
DLOG(@" >> Succeed to open database. %@", dbPath);
}
else {
DLOG(@" >> Failed to open database. %@", dbPath);
}
  • 关闭数据库
SQLITE_API int sqlite3_close(sqlite3 *);

上面这个接口将关闭数据库,如果当前还有事务没有提交,会先执行 rollback 操作,然后再关闭数据库。

  • 执行 sql 语句
SQLITE_API int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);

这个接口是最常用到的,几乎除了查询之外的 sql 命令都可以用它来操作,比如创建表,插入/更新/删除记录,创建/提交/回滚事务等。注意:如果 errmsg 不为 null,那么当错误发生时, sqlite 就会为错误消息分配内存,返回给调用者,调用者有责任调用 sqlite3_free 来释放这部分内存。为了方便使用,我封装了一个简单的 obj-c 方法:

- (BOOL)excuteSQLWithCString:(const char *)sqlCmd
{
char * errorMsg;
int state = sqlite3_exec(database, sqlCmd, NULL, NULL, &errorMsg);
if (state == SQLITE_OK) {
DLOG(@" >> Succeed to %@",
[NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding]);
}
else {
DLOG(@" >> Failed to %@. Error: %@",
[NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding],
[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]); sqlite3_free(errorMsg);
} return (state == SQLITE_OK);
}

下面是创建表以及事务操作的使用示例:

- (void)createTable
{
if (database == NULL) {
DLOG(@" >> Database does not open yet.");
return;
} const char * sqlCmd = "create table if not exists customer (id integer primary key autoincrement, name text not null, address text, age integer)"; [self excuteSQLWithCString:sqlCmd];
} - (BOOL)beginTransaction
{
return [self excuteSQLWithCString:"BEGIN EXCLUSIVE TRANSACTION;"];
} - (BOOL)commit
{
return [self excuteSQLWithCString:"COMMIT TRANSACTION;"];
} - (BOOL)rollback
{
return [self excuteSQLWithCString:"ROLLBACK TRANSACTION;"];
}

很简单,不是么?至于插入,更新,删除示例,请参考如下 sqlCmd:

// insert
NSString * sqlCmd = [NSString stringWithFormat:@"insert into customer (name, address, age) values ('%@', '%@', %d)",
customer.name, customer.address, customer.age]; // update
NSString * sqlCmd = [NSString stringWithFormat:@"update customer set address='%@',age=%d where name='%@'",
newValue.address, newValue.age, oldValue.name]; // delete
NSString * sqlCmd = [NSString stringWithFormat:@"delete from customer where name='%@'",
customer.name];
  • 查询操作

查询操作稍微负责一点,需要创建查询描述(sqlite3_stmt),然后调用如下接口编译成字节程序:

SQLITE_API int sqlite3_prepare_v2(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);

注意:这里使用的是 v2 - version 2,这是 sqlite 推荐使用的版本,version 1 仅仅是为了向后兼容而保留着。

然后使用如下接口来评估的查询结果:

SQLITE_API int sqlite3_step(sqlite3_stmt*);

如果该接口返回 SQLITE_ROW,表面查询到了一行记录,我们就可以以下接口从查询描述中获取我们想要的值:

SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol);
SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol);
SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol);
SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);

最后,需要通过如下接口释放先前创建的查询描述。通常,为了提高查询效率,可以把常用的查询描述缓存起来。

SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);

下面就来看一个具体的使用示例:

- (NSArray *)queryAllCustomers
{
NSMutableArray * array = [[NSMutableArray alloc] init]; const char * sqlCmd = "select name, address, age from customer";
sqlite3_stmt * statement;
int state = sqlite3_prepare_v2(database, sqlCmd, -1, &statement, nil);
if (state == SQLITE_OK) {
DLOG(@" >> Succeed to prepare statement. %@",
[NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding]);
} NSInteger index = 0;
while (sqlite3_step(statement) == SQLITE_ROW) {
// get raw data from statement
//
char * cstrName = (char *)sqlite3_column_text(statement, 0);
char * cstrAddress = (char *)sqlite3_column_text(statement, 1);
int age = sqlite3_column_int(statement, 2); NSString * name = [NSString stringWithCString:cstrName encoding:NSUTF8StringEncoding];
NSString * address = [NSString stringWithCString:cstrAddress encoding:NSUTF8StringEncoding];
KSCustomer * customer = [[KSCustomer alloc]
initWith:name
address:address
age:age];
[array addObject:customer]; DLOG(@" >> Record %d : %@ %@ %d", index++, name, address, age);
} sqlite3_finalize(statement); DLOG(@" >> Query %d records.", [array count]);
return array;
}

三,MAC 下查看 sqlite db 文件的工具

MAC 下有一款不错的开源可视化 sqlite db 浏览器:SQLite Database Browser,你可以从以下链接获取:

http://sourceforge.net/projects/sqlitebrowser/

该软件运行界面如下:

四,封装 sqlite 的第三方库 FMDB

在 iOS 中直接使用 sqlite 原生 C 接口还是不那么方便,因此催生了第三方的 iOS 版封装库,其中使用比较广泛又轻量级的就是 FMDB(https://github.com/ccgus/fmdb),目前该库只有六个文件,不超过2000行代码。

使用也是非常简单,在工程中包含这六个文件:

然后包含头文件:

#import "FMDatabase.h"
#import "FMResultSet.h"
#import "FMDatabaseAdditions.h"

就可以使用该库了:

    // Create database
//
NSString * path = [UIHUtilities configPathFor:kDatabaseFile];
FMDatabase db = [[FMDatabase databaseWithPath:path] retain]; if (![db open])
{
DLog(@" >> Error: Failed to open database at %@", path);
} #if DEBUG
db.traceExecution = TRUE;
#endif // Create tables
//
[db executeUpdate:@"CREATE TABLE Image (studyUid text, patientId text, seriesUid text, SOPUid text, contentDate text, modality text, patientPosition text, filepath text, thumbnailPath text)"]; // insert
//
BOOL retValue = [db executeUpdate:@"INSERT INTO Image (studyUid, patientId, seriesUid, SOPUid, contentDate, patientPosition, modality, filepath, thumbnailPath) VALUES (?,?,?,?,?,?,?,?,?)", image.studyUid, image.patientId, image.seriesUid, image.SOPUid, image.contentDate, image.patientPosition, image.modality, image.filepath, image.thumbnailPath]; if (!retValue)
DLog(@" >> Error: Database failed to insert image %@", image); // query
//
FMResultSet *rs = [db executeQuery:@"SELECT * FROM Image WHERE SOPUid = ?", SOPUid];
if ([rs next])
{
....
} // query count
//
NSInteger count = 0;
FMResultSet *rs = [db executeQuery:@"SELECT COUNT(*) FROM Image WHERE seriesUid = ?", seriesUid];
if ([rs next]) {
count = [rs intForColumnIndex:0];
} // delete
//
retValue = [db executeUpdate:@"DELETE FROM Image WHERE seriesUid = ?", seriesUid];
if (!retValue)
DLog(@" >> Error: Database failed to delete image by seriesUid %@", seriesUid); // release database
//
[db release];

sqllite相关总结的更多相关文章

  1. ELMAH入门

    简介 ELMAH(Error Logging Modules and Handlers)错误日志记录模块和处理程序,是一种应用广泛的错误日志工具是完全可插拔.它可以动态添加到一个正在运行的ASP.NE ...

  2. sqlite3 解决并发读写冲突的问题

    #include "stdafx.h" #include "sqlite3.h" #include <iostream> #include < ...

  3. 嵌入式单片机STM32应用技术(课本)

    目录SAIU R20 1 6 第1页第1 章. 初识STM32..................................................................... ...

  4. 使用C#访问SQLLite

    1.SQLLite如何集成在C#中 2.相关C#与SQLLite资源,及说明 3.简单示例

  5. 站点发布到 IIS 后,System.Data.SqlLite.dll 末找到

    近来在部署一个站点到客户的服务器 IIS 上时,打开后却出现一个错误的页面,系统提示System.Data.SqlLite.dll 末找到,在站点部署到客户的服务器之前时,在本地测试,却没有发现什么异 ...

  6. QT 读写sqllite数据库

    QT 读写sqllite数据库 分类: 技术资料2014-04-10 10:39 84人阅读 评论(0) 收藏 举报 #include <QtGui/QApplication> #incl ...

  7. SQLLite 简介

    [1] SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内 ...

  8. SQL Server数据库读取数据的DateReader类及其相关类

    之前学了几天的SQL Server,现在用C#代码连接数据库了. 需要使用C#代码连接数据库,读取数据. 涉及的类有: ConfigurationManage SqlConnection SqlCom ...

  9. MySql的相关资操作

    01-MySql的前戏   MySql的前戏 在学习Mysql之前,我们先来想一下一开始做的登录注册案例,当时我们把用户的信息保存到一个文件中: #用户名 |密码root|123321 alex|12 ...

随机推荐

  1. ubuntu搭建开发环境踩坑实录

    谨以此文,记录和ubuntu系统不死不休的搏斗过程,后续待补. 1.双系统安装,windows采用uefi模式安装(优启通可制作uefi的win10安装盘),ubuntu不要划分boot区,而应该划分 ...

  2. PHP中foreach有关引用的问题

    软件开发的过程中,细节处理非常重要,说得大一点就是细节决定成败,别人不懂的地方,你懂,别人没注意到的细节,你注意到了,这就是你胜出对方的地方,这样就体现出了你的价值. 下面是几个foreach循环中引 ...

  3. web项目tomcat启动url自定义(去掉项目名)

    通常,使用maven构建web项目,启动时默认的访问路径: http://ip:port/项目名 很多时候我们不喜欢这样 访问,我们希望下面的访问方式: http://ip:port 如果是本地的to ...

  4. SpringMVC Model,ModelMap ModelAndView

    SpringMVC 调用方法之前会创一个隐含的模型对象(即Model,ModelMap ModelAndView) //@ModelAttribute 先于login方法执行 @ModelAttrib ...

  5. 洛谷 P3690 【模板】Link Cut Tree (动态树) || bzoj 3282: Tree

    https://blog.csdn.net/saramanda/article/details/55253627 https://blog.csdn.net/CHHNZ/article/details ...

  6. Tree Recovery POJ - 2255

    Tree Recovery POJ - 2255 根据树的前序遍历和中序遍历还原后序遍历. (偷懒用了stl的find) #include<iostream> #include<st ...

  7. Android Dialogs(1)Dialog简介及Dialog分类

    Dialogs A dialog is a small window that prompts the user to make a decision or enter additional info ...

  8. sdut2355Binary Search Heap Construction

    链接 捣鼓了一下午..按堆建树 写完交 返回TLE..数据不大 感觉不会超了 无奈拿了数据来看什么奇葩数据会超 发现数据跟我输出不一样 看了好久才明白理解错题意了 给出的字符串有两个标签 按前一个来建 ...

  9. React.js 简介

    React.js 是一个帮助你构建页面 UI 的库.如果你熟悉 MVC 概念的话,那么 React 的组件就相当于 MVC 里面的 View.如果你不熟悉也没关系,你可以简单地理解为,React.js ...

  10. 手写一套迷你版HTTP服务器

    本文主要介绍如何通过netty来手写一套简单版的HTTP服务器,同时将关于netty的许多细小知识点进行了串联,用于巩固和提升对于netty框架的掌握程度. 服务器运行效果 服务器支持对静态文件css ...