iOS应用数据持久化 SQLite

5,973 阅读8分钟

iOS应用数据持久化 SQLite

一、前言

虽然平时开发不会直接使用SQLite,但是作为一个轻量级数据库,理解和掌握其基本原理和SQL语句基本使用还是有必要的。FMDB就是基于SQLite的封装,并且微信团队在自研自己的数据库前也是使用SQLite

点击项目名称 -> TAGETS -> Build Phases -> Link Binary With Libraries 点击添加libsqlite3.tbd

Pasted Graphic.png

二、代码准备

@interface ViewController (){
    /**数据库*/
    sqlite3 *database;
    /**准备语句*/
    sqlite3_stmt *stmt;
}
@property(nonatomic, strong) NSString *databasePath;
/**id文本框*/
@property (weak, nonatomic) IBOutlet UITextField *idTF;
/**名字文本框*/
@property (weak, nonatomic) IBOutlet UITextField *nameTF;
/**年龄文本框*/
@property (weak, nonatomic) IBOutlet UITextField *ageTF;

模拟器先拉好UI:

Pasted Graphic 1.png

1. 生成路径

- (NSString *)databasePath{
    if (!_databasePath) {
        /**获取Document路径*/
        NSArray *Paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *DocumentPath = [Paths firstObject];
        /**拼接数据库位置*/
        _databasePath = [DocumentPath stringByAppendingPathComponent:@“person_info.sqlite"];
        NSLog(@"%@",_databasePath);
    }
    return _databasePath;
}

2. 打开数据库

/**打开数据库*/
- (void)openDatabase{
    /**创建或打开数据库*/
    int openFlag = sqlite3_open_v2(self.databasePath.UTF8String, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
    /**判断是否成功*/
    if (openFlag != SQLITE_OK) {
        /**失败则关闭数据库*/
        sqlite3_finalize(stmt);
        sqlite3_close(self->database);
        NSLog(@"创建或打开数据库失败 --- %d",openFlag);
    }else{
        NSLog(@"创建或打开数据库成功");
        [self createForm];
    }
}

3. 创建表

/** 创建表*/
- (void)createForm{
    char *errMsg = NULL;
    /** 创建表
1、建表格式: create table if not exists 表名 (列名 类型,....)
2、如需生成默认增加的id: id integer primary key autoincrement
3、数据库名、表名、字段名使用小写,关键字、函数名称使用大写
4、每个sql语句最后都要加上”;“
*/
    NSString *sqlStr = [NSString stringWithFormat:
                        @"CREATE TABLE IF NOT EXISTS 'person'("
                        "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
                        "name TEXT NOT NULL,"
                        "age INTEGER NOT NULL);"];
/**
     第1个参数:数据库对象
     第2个参数:sql语句
     第3个参数:查询时候用到的一个结果集闭包
     第4个参数:用不到
     第5个参数:错误信息
     */
    int execRst = sqlite3_exec(database, sqlStr.UTF8String, NULL, NULL, &errMsg);
    if (execRst == SQLITE_OK) {
        NSLog(@"创建表成功");
    }else{
        NSLog(@"创建表失败:%s",errMsg);
    }
}

运行打印:

Sqlite3Demo[46764:3296271] /Users/xxxx/Library/Developer/CoreSimulator/Devices/…/data/Containers/Data/Application/…/Documents/Person.sqlite
Sqlite3Demo[46764:3296271] 创建或打开数据库成功
Sqlite3Demo[46764:3296271] 创建表成功

打开生成路径,可以看到一个person_info.sqlite的文件:

Pasted Graphic 4.png

Navicat打开person_info.sqliteperson的表已经创建好了:

Pasted Graphic 3.png

4. 插入多条测试数据

/**插入多条数据*/
- (IBAction)insertMultipleData:(UIButton *)sender{
    [self openDatabase];
    for (int i = 0; i < 10; i++) {
        NSString *name = [NSString stringWithFormat:@"张%d",i+1];
        int age = arc4random_uniform(20) + 10;
        // 拼接 sql 语句
        NSString *sql = [NSString stringWithFormat:@"INSERT INTO person (name,age) VALUES ('%@',%d);",name,age];
        // 执行 sql 语句
        char *errMsg = NULL;
        int result = sqlite3_exec(database, sql.UTF8String, NULL, NULL, &errMsg);
        if (result == SQLITE_OK) {
            NSLog(@"插入数据成功 - %@",name);
        } else {
            NSLog(@"插入数据失败 - %s",errMsg);
        }
    }
}

点击模拟器增多条:

Sqlite3Demo[47220:3310420] 插入数据成功 - 张1
Sqlite3Demo[47220:3310420] 插入数据成功 - 张2
Sqlite3Demo[47220:3310420] 插入数据成功 - 张3
Sqlite3Demo[47220:3310420] 插入数据成功 - 张4
Sqlite3Demo[47220:3310420] 插入数据成功 - 张5
Sqlite3Demo[47220:3310420] 插入数据成功 - 张6
Sqlite3Demo[47220:3310420] 插入数据成功 - 张7
Sqlite3Demo[47220:3310420] 插入数据成功 - 张8
Sqlite3Demo[47220:3310420] 插入数据成功 - 张9
Sqlite3Demo[47220:3310420] 插入数据成功 - 张10

5. 添加查询

/**查询所有记录*/
- (IBAction)retrieveAllData:(UIButton *)sender {
    [self openDatabase];
    NSString *searchSqlStr = @"SELECT * FROM person";
    [self operationData:searchSqlStr.UTF8String];
}
/**操作数据*/
- (void)operationData:(const char *)sql{
    int result = sqlite3_prepare_v2(database, sql, -1, &stmt, nil);
    if (result != SQLITE_OK) {
        NSLog(@"操作失败,%d",result);
    } else {
        /**打印操作后的数据,每调用一次sqlite3_step,stmt就会指向下一条记录*/
        printf("------------------------------\n");
        /**找到一条记录*/
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            /**取出第0列字段的值*/
            int ID = sqlite3_column_int(stmt, 0);
            /**取出第1列字段的值*/
            const unsigned char *name = sqlite3_column_text(stmt, 1);
            /*取出第2列字段的值*/
            int age = sqlite3_column_int(stmt, 2);
            printf("查到的数据 id:%d name:%s 年龄:%d\n",ID,name,age);
            
        }
        /*关闭连接*/
        [self clearTextField];
        sqlite3_finalize(stmt);
        sqlite3_close(self->database);
    }
}

点击查全部:

------------------------------
查到的数据 id:1 name:张1 年龄:29
查到的数据 id:2 name:张2 年龄:16
查到的数据 id:3 name:张3 年龄:26
查到的数据 id:4 name:张4 年龄:29
查到的数据 id:5 name:张5 年龄:18
查到的数据 id:6 name:张6 年龄:26
查到的数据 id:7 name:张7 年龄:20
查到的数据 id:8 name:张8 年龄:17
查到的数据 id:9 name:张9 年龄:14
查到的数据 id:10 name:张10 年龄:26

Navicat里看一下,已经有10条记录:

1__#$!@%!#__Pasted Graphic 3.png

还有一个sqlite_sequenceseq10

1__#$!@%!#__Pasted Graphic 4.png

注:如果没有看到数据,点击左边下面的按钮刷新,右键刷新无效。

1__#$!@%!#__Pasted Graphic 1.png

三、CRUD增删改查操作

1. 增加一条数据

/**插入一条记录*/
- (IBAction)insertData:(UIButton *)sender {
    [self openDatabase];
    NSString *insertSplStr = [NSString stringWithFormat:@"INSERT INTO person (name,age) VALUES ('%@',%@);",_nameTF.text,_ageTF.text];
    [self updateDataWithSql:insertSplStr.UTF8String success:^{
        [self clearTextField];
        NSLog(@"插入数据成功");
    }];
}

name填入夏洛特,age24,点击增:

Pasted Graphic 5.png

Sqlite3Demo[47564:3319611] 插入数据成功

点击查全部,发现已经插入了一条数据:

------------------------------
查到的数据 id:1 name:张1 年龄:29
查到的数据 id:2 name:张2 年龄:16
查到的数据 id:3 name:张3 年龄:26
查到的数据 id:4 name:张4 年龄:29
查到的数据 id:5 name:张5 年龄:18
查到的数据 id:6 name:张6 年龄:26
查到的数据 id:7 name:张7 年龄:20
查到的数据 id:8 name:张8 年龄:17
查到的数据 id:9 name:张9 年龄:14
查到的数据 id:10 name:张10 年龄:26
查到的数据 id:11 name:夏洛特 年龄:24

回到Navicatperson表点击刷新,可以看到新增的一条记录:

Pasted Graphic 6.png

2. 删除一条数据

/**删除一条记录*/
- (IBAction)deleteData:(UIButton *)sender {
    [self openDatabase];
    NSString *deleteSplStr = [NSString stringWithFormat:@"DELETE FROM person where id = %@;",self.idTF.text];
    [self updateDataWithSql:deleteSplStr.UTF8String success:^{
        [self clearTextField];
        NSLog(@"删除数据成功");
    }];
}

id = 5 的数据删除掉:

Pasted Graphic 7.png 点击删除:

Sqlite3Demo[47564:3319611] 删除数据成功

查看全部数据,发现id5的数据已经没有了:

------------------------------
查到的数据 id:1 name:张1 年龄:29
查到的数据 id:2 name:张2 年龄:16
查到的数据 id:3 name:张3 年龄:26
查到的数据 id:4 name:张4 年龄:29
查到的数据 id:6 name:张6 年龄:26
查到的数据 id:7 name:张7 年龄:20
查到的数据 id:8 name:张8 年龄:17
查到的数据 id:9 name:张9 年龄:14
查到的数据 id:10 name:张10 年龄:26
查到的数据 id:11 name:夏洛特 年龄:24

Navicate里查看:

Pasted Graphic 8.png

3. 修改一条数据

/**更新一条记录*/
- (IBAction)updateData:(UIButton *)sender {
    [self openDatabase];
    NSString *changeSqlStr = [NSString stringWithFormat:@"UPDATE person SET name = '%@' WHERE id = '%@';",self.nameTF.text,self.idTF.text];
    [self updateDataWithSql:changeSqlStr.UTF8String success:^{
        [self clearTextField];
        NSLog(@"修改成功");
    }];
}
/**更新记录操作*/
- (void)updateDataWithSql:(const char *)sql success:(void(^)(void))successBlock {
    /**
     第1个参数:一个已经打开的数据库对象
     第2个参数:sql语句
     第3个参数:参数2中取出多少字节的长度,-1 自动计算,\0停止取出
     第4个参数:准备语句
     第5个参数:通过参数3,取出参数2的长度字节之后,剩下的字符串
     */
    int result = sqlite3_prepare_v2(database, sql, -1, &stmt, nil);
    if (result != SQLITE_OK) {
        NSLog(@"操作失败,%d",result);
        sqlite3_finalize(stmt);
        sqlite3_close(self->database);
    } else {
        sqlite3_step(stmt);
        successBlock();
    }
}

id = 10的名字改为赵云:

Pasted Graphic 9.png

查看全部:

------------------------------
查到的数据 id:1 name:张1 年龄:29
查到的数据 id:2 name:张2 年龄:16
查到的数据 id:3 name:张3 年龄:26
查到的数据 id:4 name:张4 年龄:29
查到的数据 id:6 name:张6 年龄:26
查到的数据 id:7 name:张7 年龄:20
查到的数据 id:8 name:张8 年龄:17
查到的数据 id:9 name:张9 年龄:14
查到的数据 id:10 name:赵云 年龄:26
查到的数据 id:11 name:夏洛特 年龄:24

4. 查找检索数据

/**查询记录*/
- (IBAction)retrieveData:(UIButton *)sender {
    [self openDatabase];
    /**
     查询age < 25也可使用sql= "select * from person where age < 25"
     */
    NSString *sqlStr;
    if(_idTF.text.length > 0){
        sqlStr = [NSString stringWithFormat:@"SELECT id, name, age FROM person WHERE id = '%@';",_idTF.text];
    }
    else if(_nameTF.text.length > 0){
        sqlStr = [NSString stringWithFormat:@"SELECT id, name, age FROM person WHERE name = '%@';",_nameTF.text];
    }
    else if(_ageTF.text.length > 0){
        sqlStr = [NSString stringWithFormat:@"SELECT id,name,age FROM person WHERE age %@;",_ageTF.text];
        NSLog(@"sqlStr == %@",sqlStr);
    }
    [self operationData:sqlStr.UTF8String];
}

id = 4的记录:

Pasted Graphic 10.png

------------------------------
查到的数据 id:4 name:张4 年龄:29

查年龄小于25的记录:

Pasted Graphic 11.png

点击查按钮:

------------------------------
查到的数据 id:2 name:张2 年龄:16
查到的数据 id:7 name:张7 年龄:20
查到的数据 id:8 name:张8 年龄:17
查到的数据 id:9 name:张9 年龄:14
查到的数据 id:11 name:夏洛特 年龄:24

查找名字赵云:

------------------------------
查到的数据 id:10 name:赵云 年龄:26

点击增加多条数据测试,再查看全部数据:

------------------------------
查到的数据 id:1 name:张1 年龄:29
查到的数据 id:2 name:张2 年龄:16
查到的数据 id:3 name:张3 年龄:26
查到的数据 id:4 name:张4 年龄:29
查到的数据 id:6 name:张6 年龄:26
查到的数据 id:7 name:张7 年龄:20
查到的数据 id:8 name:张8 年龄:17
查到的数据 id:9 name:张9 年龄:14
查到的数据 id:10 name:赵云 年龄:26
查到的数据 id:11 name:夏洛特 年龄:24
查到的数据 id:12 name:张1 年龄:21
查到的数据 id:13 name:张2 年龄:11
查到的数据 id:14 name:张3 年龄:20
查到的数据 id:15 name:张4 年龄:28
查到的数据 id:16 name:张5 年龄:25
查到的数据 id:17 name:张6 年龄:24
查到的数据 id:18 name:张7 年龄:22
查到的数据 id:19 name:张8 年龄:29
查到的数据 id:20 name:张9 年龄:14
查到的数据 id:21 name:张10 年龄:26

查看Navicatperson

Pasted Graphic 12.png

查看squence,点击刷新,id21完全一致:

Pasted Graphic 13.png

点击清空:

/**清空表中数据和自增排序*/
- (IBAction)removeAllData:(UIButton *)sender {
    [self openDatabase];
    const char *deleteSpl = "DELETE FROM person";
    [self updateDataWithSql:deleteSpl success:^{
        NSLog(@"清空表数据成功");
    }];
}

表数据已经被清空:

Pasted Graphic 14.png

这个时候sqlite_sequence还是21,没有归0

Pasted Graphic 15.png

此时点击插入10条数据,发现id21开始:

Pasted Graphic 16.png

并没有真正的清空,需要给数据添加seq0操作:

sqlite3 没有truncate关键字:

/**清空表中数据和自增排序*/
- (IBAction)removeAllData:(UIButton *)sender {
    [self openDatabase];
    const char *deleteSpl = "DELETE FROM person";
    [self updateDataWithSql:deleteSpl success:^{
        NSLog(@"清空表数据成功");
        const char *setSeqSpl = "UPDATE sqlite_sequence SET seq = 0 WHERE name = 'person'";
        [self updateDataWithSql:setSeqSpl success:^{
            NSLog(@"自增主键归零");
        }];
    }];
}

再点击清空:

Pasted Graphic 17.png

点击插入多条,id又从1开始:

Pasted Graphic 18.png

四、总结

SQLite接口都是C语言,并不面向对象,所有操作需要自己手写SQL语句。

参考