今天来聊一聊sqlite数据库的一些基本操作, 之前一直用的是FMDB来操作数据库的, 但是也想对sqlite本身也做一个了解, 那我们就来一起学习一下.
sqlite数据库是一种轻量级数据库,被集成到用户程序中, 占用资源非常的低,在嵌入式设备中,占用内存很低而查询效率很高,因此广泛应用在各种移动客户端中;
简单,轻巧的足够小(C语言API ,4.43M)
快(比大部分的普通数据库都快)
支持的数据库大小至2TB,所以注定了它是一个跨平台利器,在Android与iOS下均能使用,而且完全可以写出通用的代码,方便我们移植。最后一个重要的原因就是原生的使用也是相当简单
Demo的操作环境: Xcode 8.3.3
一图流:
忽略我的警告… :P
然后在你需要使用sqlite3的地方导入头文件
#import
喏, 然后就可以愉快的开始使用了~
下面就是sqlite3定义的打开数据库的方法:
int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
BTW: 我能说第一次我看到这个方法的时候以为是要手动写的, 自己定义了一套么.. :(
上面第一个参数就是数据库所存放的路径,如果有就直接用, 如果路径下没有数据库文件则系统会在此路径下创建一个数据库。至于第二个参数则是数据库的句柄引用,但此函数调用成功后此句柄将会保存打开数据库的句柄,此句柄在往后的数据库操作中需要用到。因此,可如下方式调用:
NSString *path = [NSString stringWithFormat:@"%@/Documents/demo.db",NSHomeDirectory()];
if (sqlite3_open([path UTF8String], &_dbHandle)==SQLITE_OK) {
NSLog(@"打开数据库成功!");
}
来解释一下句柄:
句柄在很多地方我们见到过,最常见的就是文件的句柄,我们要操纵一个文件,我们就要取得一个文件的句柄。句柄是个什么东西?其实很简单,句柄是一个东西的描述,他被定义为一个结构体,这个结构体可能会包含要描述的东西的具体信息,比如位置、大小、类型等等。我们有了这个描述信息我们就能去找到这个东西,然后操纵它。一句话:句柄是物体的描述结构体。
咱还可以理解为“把手” : 虽然你握住的只是把手,却能拉动整扇门,而且你根本不用在意那门长什么样子, 一扇门如果有多个把手,被不同的人(进程)握住,门往哪儿走就不好说了…
这是我定义的一个句柄:
static sqlite3 *dbHandle;
既然有打开数据库,那么就肯定有关闭数据库的操作了,我们要养成良好的习惯,当需要使用数据库的时候就执行sqlite3_open来打开数据库,等使用完数据库后就调用sqlite3_close函数来对数据库进行关闭。关于sqlite3_close函数声明如下:
int sqlite3_close(sqlite3 *);
关闭的时候把我们定义的句柄传入数据库即可对数据库进行关闭。调用方式如下:
if (sqlite3_close(_dbHandle)==SQLITE_OK) {
NSLog(@"关闭数据库成功!");
}else{
NSLog(@"error %s",sqlite3_errmsg(_dbHandle));
}
常用的有两种方式可以操作数据库中的数据和调整结构, 第一种就是sqlite3_exec函数,这种方法一般用在增、删、改。这个函数不适合来查询数据库, 后面我们再介绍查询数据库的方法. 先来看看此方法的声明:
int sqlite3_exec(
sqlite3*, /* An open database 这是我们打开数据库的句柄*/
const char *sql, /* SQL to be evaluated 待执行的SQL语句*/
int (*callback)(void*,int,char**,char**), /* Callback function 回调函数,有固定的格式,你可以自己实现*/
void *, /* 1st argument to callback 回调函数的第一个参数*/
char **errmsg /* Error msg written here 如果方法执行后存在异常,那么这个参数保存的就是错误的描述信息*/
);
要想让数据库能够存储数据,那就必须得创建一个数据表才能进行数据操作。而数据表是可以包含不同的数据字段,这些字段可以指定不同的数据类型,存储不同的数据。我们建表时可以根据需要进行创建。下面的代码创建了一个叫做persons的数据表,其中包含了3个字段id、name和age. (如果对SQL语句不太熟悉的朋友可以参考相关方面资料), 写个实例来演示上面的sqlite3_exec函数:
-(void)createTable
{
//因为sqlite3是C写的,所以传入的时候,sqlite语句要用C的形式传入,如果用NSString *createString = ***; 这种形式会报错
const char *createString = "create table if not exists persons (id integer primary key autoincrement, name varchar(256), age integer)";
char *errorMsg;
BOOL result = sqlite3_exec(_dbHandle, createString, nil, nil, &errorMsg);
if (result == SQLITE_OK) {
NSLog(@"建表成功!");
}else{
NSLog(@"建表失败!Error:%s",sqlite3_errmsg(_dbHandle)));
}
sqlite3_free(errorMsg);
}
面要注意的一点是,如果你有传入errorMsg参数,那么你必须在执行完sqlite3_exec后,执行sqlite3_free函数来释放errorMsg。否则就会造成内存泄露
2017-06-22 17:44:53.764 SQLiteDemo[68053:2703014] 打开数据库成功!
2017-06-22 17:44:53.764 SQLiteDemo[68053:2703014] 关闭数据库成功!
2017-06-22 17:44:53.764 SQLiteDemo[68053:2703014] 建表失败!Error:(null)
2017-06-22 17:44:53.764 SQLiteDemo[68053:2703014] 插入数据失败! Error:library routine called out of sequence
不明所以? 一脸蒙蔽? 恩…当时的我就是这个样子的! 为什么是null? 如果出了错都不知道原因是什么… :o
所以我用了第二种sqlite3_errmsg(_dbHandle)来打印错误信息,这是一个报错的函数,所以结果就很清楚了: 库程序调用顺序问题
-(void)insertInfo
{
const char *insert = "insert into persons (name,age) values ('阿童木',5)";
char *errorMsg;
BOOL result = sqlite3_exec(_dbHandle, insert, nil, nil, &errorMsg);
if (result == SQLITE_OK) {
NSLog(@"插入数据成功!");
}else{
NSLog(@"插入数据失败! Error:%s",sqlite3_errmsg(_dbHandle));
}
}
打印输出:
2017-06-22 17:39:56.379 SQLiteDemo[67920:2698580] 插入数据成功!
delete from persons; //会直接删除所有的数据,但是不会清空自动增长列的标识。
delete from persons where id = 8; //根据id删除
-(void)deleteInfo
{
const char *delete = "delete from persons where id = 1";
BOOL result = sqlite3_exec(_dbHandle, delete, nil, nil, &errorMsg);
if (result == SQLITE_OK) {
NSLog(@"删除数据成功!");
}else{
NSLog(@"删除数据失败!Error:%s",sqlite3_errmsg(_dbHandle));
}
}
const char *change = "update persons set name = '铁甲小宝' age = '40' where id = 2";
我们查询数据库就使用sqlite3_prepare_v2和sqlite3_step两个函数搭配进行操作。其中sqlite3_prepare_v2是一个将SQL语句编译为sqlite内部一个结构体(sqlite3_stmt).该结构体中包含了将要执行的的SQL语句的信息。而sqlite3_step则是让转化后的SQL进行下一步的操作。因此通过这两个函数可以很方便的获取到数据库中的数据。本人建议使用此方式取得记录集。下面是这两个函数的声明:
int sqlite3_prepare_v2(
sqlite3 *db, /* Database handle 句柄*/
const char *zSql, /* SQL statement, UTF-8 encoded SQL语句 */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
第三个参数是用于指定SQL语句最大的长度,如果此参数为负数,则根据第二个参数中的第一个终结符为准作为一条完整的语句。如果为非负数,则以第二个参数的第一个终结符,或者指定的数字为准作为一条完整语句。第四个参数则是调用函数后返回的一个结构体,此结构体包含了相关语句的信息。关于第五个参数是用于指向前一条语句结束位置,一旦指定此参数,则参数指向位置的左边语句将不进行编译解析。
sqlite3_step(sqlite3_stmt *);
传入参数即为准备语句中的结构体对象。
例子:
-(void)searchInfo
{
//创建数组 存数据
NSMutableArray *array = [[NSMutableArray alloc]init];
const char *select = "select * from persons where name = '阿童木'";
//sqlite3_prepare_v2的参数第一个是数据库连接,第二个是sql语句,第三个是这个语句的长度传入-1表示到第一个null终止符为止,第四个是返回一个语句对象,第五个是返回一个指向该sql语句的第一个字节的指针
int result = sqlite3_prepare_v2(_dbHandle, select, -1, &stmt, nil);
if (result == SQLITE_OK) {
// 开始遍历 每调一次sqlite3_step函数,stmt就会指向下一条记录
while (sqlite3_step(stmt) == SQLITE_ROW) {
int ID = sqlite3_column_int(stmt, 0); // 取出第0列字段的值(int类型的值)
const unsigned char *name = sqlite3_column_text(stmt, 1); // 取出第1列字段的值(text类型的值)
int age = sqlite3_column_int(stmt, 2); // 取出第2列字段的值(int类型的值)
//将C字符串转换为OC字符串
NSString *name1 = [[NSString alloc]initWithUTF8String:(const char *)name];
//将model实例化 并把取出来的数据存入model
ZTPerson *p = [[ZTPerson alloc]init];
p.name = name1;
p.age = age;
p.ID = ID;
[array addObject:p];
}
}else{
NSLog(@"查询语句有问题,查询失败!");
}
NSLog(@"%@",array);
}
这就是今天的关于sqlite数据库的内容, 感觉还差点什么, 明天想起来再加进去.
2017年06月22日22:01:38 Reborn_Tai