山涧一点绿,疑似,非是
可惜,可悲,可叹
(保护环境)
函数说明参照文章:
http://blog.csdn.net/zhuzhihai1988/article/details/7878093
简介
SQLite是一款轻量级数据库,开源,它采用C语言编写,具有可移植性强、可靠性高、小而容易使用的特点。现在使用的是SQLite3。数据库文件后缀一般定义为db或sqlite3,皆可,因为Linux有无后缀名没有本质区别。
SQLite是无数据类型的数据库,就是字段不用指定类型,不过实际操作中我们一般都是指定类型。
SQLite的字段数据类型和MYSQL等不太一样。最常用的数据类型是:
- INTEGER:有符号的整数类型。
- REAL:浮点类型。
- TEXT:字符串类型,采用UTF-8和UTF-16字符编码。
- BLOB:二进制大对象类型,能够存放任何二进制数据。
在SQLite中没有Boolean类型,可以采用整数0和1替代。也没有日期和时间类型,它们存储在TEXT、REAL和INTEGER类型中。其他数据库的类型可以进行转换:
- VARCHAR、CHAR和CLOB转换成为TEXT类型。
- FLOAT、DOUBLE转换成为REAL类型。
- NUMERIC转换成为INTEGER或者REAL类型。
SQLite3.0开发库
苹果自带SQLite3.0开发库,使用SQLite的工程需要导入SQLite3.0开发库(libsqlite3.dylib或libsqlite3.0.dylib)。SQLite3.0开发库中自带对SQLite数据库的纯C语言函数,Objective-C可以直接调用C函数,不过需要注意两者数据类型的兼容问题。
- 导入SQLite3.0开发库:
XCode打开需要使用SQLite的工程,单击选择工程,右侧出现工程的设置画面,选择Bulid Phases tab,如下图,打开Link Binary With Libraries,空的吧,点击+来添加IOS自带的库。
点击+后,画面如下,搜索sqlite,会检索出两个sqlite3.0的库,任意选择其中一个库,点击“Add”。(添加第三方库时,点击Add Other)
添加完毕后,列表中会显示库,如下。现在我们就可以使用SQLite的函数了。
- 使用SQLite3.0库:
数据库无非就是创建DB,创建表,条件检索,全检索,增加,删除,修改。那么我们给出各个代码并分析其使用到的SQLite3.0的函数。
这里我创建了一个类UserDao,对应的Entity类为UserInfo(Java的孩纸们注意啦,这里可以没有映射)
UserInfo类:(只给出头文件喽)
@interface UserInfo : NSObject
@property NSInteger userId;
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *age;
@property (nonatomic, strong) NSString *tel;
@end
UserDao类:(UserList数据库管理类)
#import <Foundation/Foundation.h>
#import "sqlite3.h"//引入sqlite3.h。
#import "UserInfo.h"@interface UserDao : NSObject
{
sqlite3 *db;//sqlite库对象
}+(UserDao *)shareDBManager;//单例模式
-(NSString *)documentDirectoryFile;//UserList数据库路径
-(void)createEditableCopyOfDatabaseIfNeeded;//创建表
-(int) insert:(UserInfo *)userInfo;//添加数据
-(int) remove:(UserInfo *)userInfo;//删除数据
-(int) modify:(UserInfo *)userInfo;//修改数据
-(UserInfo *) findById:(UserInfo *)userInfo;//条件检索
-(NSMutableArray *) findAll;//全检索@end
UserDao.m:
#define DB_NAME @"UserList.sqlite3"//数据库文件名
static UserDao* singleton = nil;
+(UserDao *)shareDBManager{
static dispatch_once_t onceToken;//GCD单例,只执行一次。
dispatch_once(&onceToken, ^{
singleton = [[self alloc] init];
});
return singleton;
}-(NSString *)documentDirectoryFile{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];//documents沙盒路径
NSString *dbFilePath = [documentPath stringByAppendingPathComponent:DB_NAME];//拼接DB文件路径
return dbFilePath;
}
创建表:
-(void)createEditableCopyOfDatabaseIfNeeded{
NSString *writableDBPath = [self documentDirectoryFile];//获取db文件路径if (sqlite3_open([writableDBPath UTF8String], &db) != SQLITE_OK) {//打开DB,NSString转为const char
sqlite3_close(db);
}else{
char *err;
NSString *createSQL = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS users(userId INTEGER PRIMARY KEY AUTOINCREMENT, userName TEXT, age TEXT, tel TEXT)"];//创建表SQL
if (sqlite3_exec(db, [createSQL UTF8String], NULL, NULL, &err) != SQLITE_OK) {//执行SQL
sqlite3_close(db);
NSAssert1(NO, @"建表失败:%s", err);
}
sqlite3_close(db);//关闭DB
}
}
创建表我们用到了三个纯C函数,sqlite3_open→sqlite3_exec→sqlite3_close。
int sqlite3_open(const char *filename, sqlite3 **ppDb)//第一个参数时db文件全路径,第二个参数时sqlite3对象地址。
看完代码你可能会奇怪如果db文件不存在怎么办,不会出问题吗。事实上,sqlite3_open函数,如果该路径下文件不存在的话,会自动创建一个。如果打开成功的话会返回0即SQLITE_OK,SQLite3库自带结果集,其中最常用的是三个正常flag。
#define SQLITE_OK 0 /* Successful result */
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
int sqlite3_exec(sqlite3*,const char *sql,int (*callback)(void*,int,char**,char**),void *, char **errmsg)//第一个参数是打开了的db,第二个是要执行的sql文,第三个是回调函数,第四个是回调函数的第一个参数,第五个是错误信息。
int sqlite3_close(sqlite3 *)//释放DB资源,只要执行过sqlite3_open就一定要有对应的sqlite3_close,即使打开失败。
添加数据:
-(int)insert:(UserInfo *)userInfo{
NSString *writableDBPath = [self documentDirectoryFile];//获取db文件路径
if (sqlite3_open([writableDBPath UTF8String], &db)!=SQLITE_OK) {//打开DB
sqlite3_close(db);
NSAssert(NO, @"打开DB失败!");
}else{
sqlite3_stmt *statement;//SQL语句对象
NSString *sql = @"INSERT OR REPLACE INTO users(userName,age,tel) VALUES(?,?,?)";//追加数据的sql文,参数用"?"代替。
if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, NULL)==SQLITE_OK) {//准备sql给sqlite3_stmt对象
sqlite3_bind_text(statement, 1, [userInfo.userName UTF8String], -1, NULL);//绑定sql中的?参数,不同数据类型对应不同的函数。
sqlite3_bind_text(statement, 2, [userInfo.age UTF8String], -1, NULL);
sqlite3_bind_text(statement, 3, [userInfo.tel UTF8String], -1, NULL);
if (sqlite3_step(statement) != SQLITE_DONE) {//执行sqlite3_prepare_v2创建的sqlite3_stmt对象
NSAssert(NO, @"插入数据失败!");
}
}
sqlite3_finalize(statement);//释放sqlite3_stmt对象
sqlite3_close(db);//释放db
}
return 0;
}
插入数据操作,一般会涉及6个函数:(如果没有参数则不用sqlite3_bind_*)sqlite3_open→sqlite3_prepare_v2→sqlite3_bind_*→sqlite3_step→sqlite3_finalize→sqlite3_close。
这里我们使用了一个新的类型sqlite3_stmt,SQL语句对象,我们使用sqlite3_prepare_v2将sql文和sqlite3_stmt对象绑定,然后通过操作sqlite3_stmt来给sql文中参数传参、执行sql、获取结果。最后记得使用sqlite3_finalize释放它。
int sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt,const char **pzTail)//这个函数将sql文本转换成一个准备语句对象sqlite3_stmt,同时返回这个对象的指针,它实际上并不执行这个SQL语句,仅为执行准备这个sql语句。
- db:sqlite3对象指针
- zSql:sql语句,使用UTF-8编码,NSString对象使用UTF8String转换
- nByte:如果nByte小于0,则函数取出zSql中从开始到第一个0终止符的内容;如果nByte不是负的,那么它就是这个函数能从zSql中读取的字节数的最大值。一般为-1
- ppStmt:能够使用sqlite3_step()执行的编译好的准备语句的指针,如果错误发生,它被置为NULL,如假如输入的文本不包括sql语句。调用过程必须负责在编译好的sql语句完成使用后使用sqlite3_finalize()释放它。
- pzTail:上面提到zSql在遇见终止符或者是达到设定的nByte之后结束,假如zSql还有剩余的内容,那么这些剩余的内容被存放到pZTail中,不包括终止符。
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*))//sqlite3_bind_*()给宿主参数(sql文中的?)绑定值,*代表不同类型,如下,与sqlite数据库数据类型匹配。
- 第一个参数:sqlite3_stmt对象
- 第二个参数:sql中第m个?(从1开始)
- 第三个参数:给第m个?赋的值
- 第四个参数:第三个参数字符串长度,一般给-1
- 第五个参数:回调函数
int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
int sqlite3_bind_double(sqlite3_stmt*, int, double);
int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
int sqlite3_bind_null(sqlite3_stmt*, int);
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
int sqlite3_step(sqlite3_stmt*)//执行sqlite3_stmt对象,这个语句执行到结果的第一行可用的位置。继续前进到结果的第二行的话,需要再次调用sqlite3_setp()。继续调用sqlite3_setp()直到这个语句完成,用while循环执行,使用SQLITE3_ROW判断,如果返回结果是SQLITE3_ROW则说明不是还有结果语句。那些不返回结果的语句(如:INSERT,UPDATE,或DELETE),sqlite3_step()只执行一次就返回,用SQLITE3_DONE判断是否成功。所以这里是有SQLITE3_DONE判断。
int sqlite3_finalize(sqlite3_stmt *pStmt)//释放SQL语句对象,必须执行(不论准备结果是成功还是失败)。
修改数据:
-(int)modify:(UserInfo *)userInfo{
NSString *writableDBPath = [self documentDirectoryFile];
if (sqlite3_open([writableDBPath UTF8String], &db) != SQLITE_OK) {
sqlite3_close(db);
NSAssert(NO, @"打开DB失败!");
}else{
sqlite3_stmt *statement;
NSString *sql = @"UPDATE users SET userName = ?, age = ?, tel = ? WHERE userId = ? ";
if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_text(statement, 1, [userInfo.userName UTF8String], -1, NULL);
sqlite3_bind_text(statement, 2, [userInfo.age UTF8String], -1, NULL);
sqlite3_bind_text(statement, 3, [userInfo.tel UTF8String], -1, NULL);
sqlite3_bind_int(statement, 4, (int)userInfo.userId);
if (sqlite3_step(statement) != SQLITE_DONE) {
NSAssert(NO, @"修改数据失败");
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
return 0;
}
修改数据操作,一般会涉及6个函数:(如果没有参数则不用sqlite3_bind_*)sqlite3_open→sqlite3_prepare_v2→sqlite3_bind_*→sqlite3_step→sqlite3_finalize→sqlite3_close。
删除数据:
-(int)remove:(UserInfo *)userInfo{
NSString *writableDBPath = [self documentDirectoryFile];
if (sqlite3_open([writableDBPath UTF8String], &db) != SQLITE_OK) {
sqlite3_close(db);
NSAssert(NO, @"打开DB失败!");
}else{
sqlite3_stmt *statement;
NSString *sql = @"DELETE FROM users WHERE userId = ?";
if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_int(statement, 1, (int)userInfo.userId);
if (sqlite3_step(statement) != SQLITE_DONE) {
NSAssert(NO, @"删除数据失败!");
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
return 0;
}
删除数据操作,一般会涉及6个函数:(如果没有参数则不用sqlite3_bind_*)sqlite3_open→sqlite3_prepare_v2→sqlite3_bind_*→sqlite3_step→sqlite3_finalize→sqlite3_close。
数据的增删改的操作流程是一样的,除了sql文不一样。
条件查找:
-(UserInfo *)findById:(UserInfo *)userInfo{
NSString *writableDBPath = [self documentDirectoryFile];
if (sqlite3_open([writableDBPath UTF8String], &db) != SQLITE_OK) {//打开DB,不存在则自动创建
sqlite3_close(db);
NSAssert(NO, @"打开DB失败!");
}else{
sqlite3_stmt *statement;
NSString *sql = @"SELECT * FROM users WHERE userId = ? ";
if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, NULL) == SQLITE_OK) {//准备SQL语句对象
sqlite3_bind_int(statement, 1, (int)userInfo.userId);//绑定SQL参数
if (sqlite3_step(statement) == SQLITE_ROW) {//执行SQL语句对象,每次取一条结果
UserInfo *result = [[UserInfo alloc] init];
result.userId = userInfo.userId;
char *userName= (char *)sqlite3_column_text(statement, 1);//取结果
result.userName = [[NSString alloc] initWithUTF8String:userName];
char *age = (char *)sqlite3_column_text(statement, 2);
result.age = [[NSString alloc] initWithUTF8String:age];
char *tel = (char *)sqlite3_column_text(statement, 3);
result.tel = [[NSString alloc] initWithUTF8String:tel];
return result;
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
return nil;
}
查询数据操作,一般会涉及7个函数:(如果没有参数则不用sqlite3_bind_*)sqlite3_open→sqlite3_prepare_v2→sqlite3_bind_*→sqlite3_step→sqlite3_column_*→sqlite3_finalize→sqlite3_close。
查找的sqlite3_step返回值用SQLITE_ROW判断是否还有数据。
sqlite3_column_*(sqlite3_stmt*, int iCol)//从执行sqlite3_step()执行一个准备语句得到的结果集的当前行中返回一个列。第一个参数是执行的SQL语句对象,第二个参数是结果集中列的序号。其中函数名中*代表各个数据类型。
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
int sqlite3_column_type(sqlite3_stmt*, int iCol);
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
全检索:
全检索和条件检索类似,只是循环调用sqlite3_step,直到返回结果不是SQLITE_ROW。
-(NSMutableArray *)findAll{
NSString *writableDBPath = [self documentDirectoryFile];
if (sqlite3_open([writableDBPath UTF8String], &db) != SQLITE_OK) {
sqlite3_close(db);
NSAssert(NO, @"打开DB失败!");
}else{
sqlite3_stmt *statement;
NSString *sql = @"SELECT * FROM users";
if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, NULL) == SQLITE_OK) {
NSMutableArray *userList = [[NSMutableArray alloc] init];
while (sqlite3_step(statement) == SQLITE_ROW) {
UserInfo *userInfo = [[UserInfo alloc] init];
NSInteger userId= sqlite3_column_int(statement, 1);
userInfo.userId = userId;
char *userName= (char *)sqlite3_column_text(statement, 1);
userInfo.userName = [[NSString alloc] initWithUTF8String:userName];
char *age = (char *)sqlite3_column_text(statement, 2);
userInfo.age = [[NSString alloc] initWithUTF8String:age];
char *tel = (char *)sqlite3_column_text(statement, 3);
userInfo.tel = [[NSString alloc] initWithUTF8String:tel];
[userList addObject:userInfo];
}
return userList;
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
return nil;
}
全检索操作,一般会涉及6个函数:(如果没有参数则不用sqlite3_bind_*)sqlite3_open→sqlite3_prepare_v2→sqlite3_step→sqlite3_column_*→sqlite3_finalize→sqlite3_close。
注:sqlite3_close对应着sqlite3_open,sqlite3_finalize对应着sqlite3_prepare_v2,因此在出现过sqlite3_open和sqlite3_prepare_v2之后的各分支中确保最后一定会有对应的释放函数执行。
扩展:
现在有很多开源的对SQLite进行封装的第三方库,如果客户没有这方面要求可以使用,譬如FMDB,FMDB只有3个类如下,使用的时候,只需要把这六个文件添加到工程中即可使用。