Bootstrap

SQLite3语句以及用实现FMDB数据存储的学习

FMDB与数据存储的学习

前言

这周开始挑选完项目,我们就要进入项目内容的相关学习,为后面项目的编写做铺垫,本篇文章就是介绍了数据存储的相关知识。

SQLite

SQLite,是一款轻型的数据库,它包含在一个相对小的C库中。

设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。

支持Windows/Linux/Unix等等主流PC操作系统和Android/iOS等主流手机操作系统

SQLite数据类型

SQLite一共只有五种数据类型

  • NULL:表示该值为NULL值。
  • INTEGER:整型值。
  • REAL:浮点值。
  • TEXT: 文本字符串,存储使用的编码方式为UTF-8、UTF-16。
  • BLOB:二进制大对象(BLOB)是任意类型的数据。

SQLite常用函数

  • sqlite3_open——打开数据库,如果目录下没有就创建一个
  • sqlite3_close——关闭数据库
  • sqlite3_exec —— 用于执行 SQL 语句
  • sqlite3_prepare_v2——用于将 SQL 查询语句编译为可执行的 SQLite 语句。
  • sqlite3_step
  • sqlite3_column_text

SQLite相关语句的使用

界面配置

想要在OC之中使用SQLite,需要以下步骤:

在项目的general选项之中,找到Frameworks, Libraries, and Embedded Content的选项

请添加图片描述

点击加号,然后在弹出的搜索栏之中寻找sqlite

请添加图片描述

这样子就可以将框架导入到我们的工程文件了

学习SQLite之前还需要了解部分关于NSBundle的相关内容,有兴趣的读者可以进行了解

SQLite的OC使用

创建Model

我们创建一个person来进行文件操作

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *phone;
@property (strong, nonatomic) NSString *address;

-(instancetype)initWithName:(NSString *)name andPhone:(NSString *)phone andAddress:(NSString *)address;

@end

#import "Person.h"

@implementation Person
-(instancetype)initWithName:(NSString *)name andPhone:(NSString *)phone andAddress:(NSString *)address {
    if(self = [super init]) {
        self.name = name;
        self.phone = phone;
        self.address = address;
    }
    return self;
}
- (NSString *)description
{
    return [NSString stringWithFormat:@"Name:%@,Phone:%@,Address:%@", _name,_phone,_address];
}
@end

创建库

-(void)createDB: (NSString *)dbName{
    //1.查找沙盒
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    //2.拼接数据库
    NSString *dbPath = [docPath stringByAppendingPathComponent:dbName];
    //3.常见数据库
    int result = sqlite3_open([dbPath UTF8String], &sqlite);
    if (result == SQLITE_OK) {
        NSLog(@"数据库创建成功");
    }
    else {
        NSLog(@"数据库创建失败");
    }
}

创建表格

-(void) createTab: (NSString *)tabName{
    NSString *sql = [NSString stringWithFormat:@"create table %@(id integer primary key autoincrement, name text, phone text, address text)", tabName];
    char *error;
    int result = sqlite3_exec(sqlite, [sql UTF8String], NULL, NULL, &error);
    if (result == SQLITE_OK) {
        NSLog(@"表格创建成功");
    } else {
        NSLog(@"表格创建失败");
    }
}

其中sql之中的内容如下

id integer primary key autoincrement

  • id:列名,表示每一行的唯一标识符。
  • integer:数据类型,表示 id 列的数据类型是整数。
  • primary key:指定 id 列是主键,主键是每行数据的唯一标识,不允许重复。
  • autoincrement:表示 id 值会自动递增,每次插入新数据时,id 会自动加 1,从而确保每一行有一个唯一的 id 值。

剩下的nameaddress等内容其实就是对应的每列内容所存储的方式

int sqlite3_exec(
    sqlite3 *db,              /* 数据库连接对象 */
    const char *sql,          /* 要执行的 SQL 语句 */
    int (*callback)(void*,int,char**,char**),  /* 回调函数 */
    void *data,               /* 回调函数的用户数据 */
    char **errmsg             /* 错误信息 */
);

值得注意的是,在创建表格之前我们需要先打开数据库才可以成功创建表格

添加数据

-(void)insertPerson: (Person *)person{
    NSString *sql = [NSString stringWithFormat:@"insert into stus(name, phone, address) values('%@', '%@', '%@')", person.name, person.phone, person.address];
    char *error;
    int result = sqlite3_exec(sqlite, [sql UTF8String], NULL, NULL, &error);
    if (result == SQLITE_OK) {
        NSLog(@"数据插入成功");
    } else {
        NSLog(@"数据插入失败:%s",error);
    }
}

用终端查看表中数据

对于插入表中的数据,我们可以使用终端来进行查看,首先是找到Document目录下的路径,我们可以打印出来

NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSLog(@"%@",docPath);

拿到Document的路径之后,就在终端使用cd进入目录,然后直接使用以下命令进入DB文件

sqlite3 your_database_file.db//这里换成你自己的库名
查看所有表

在 SQLite 命令行模式中,输入以下命令列出所有表:

.tables
查看表结构

如果想查看某个表的结构(例如 table_name 表),可以使用以下命令:

.schema table_name
查看表内容

要查看表中的数据,可以使用 SELECT 语句,例如:

SELECT * FROM table_name;
退出 SQLite

完成后,输入 .exit 或按 Ctrl + D 退出 SQLite 命令行模式:

.exit

删除数据

-(void)deletePerson: (NSString *)personName {
    NSString *sql = [NSString stringWithFormat:@"delete from stus where name = '%@'", personName];

    char *error;
    int result = sqlite3_exec(sqlite, [sql UTF8String], NULL, NULL, &error);
    if (result == SQLITE_OK) {
        NSLog(@"删除数据成功");
    }
    else {
        NSLog(@"删除数据失败:%s",error);
    }
}

这个删除功能只是在库之中无法查询到删除之后的数据,但是在库中仍然存在,所以这是一个相对不太安全的操作,需要我们在实际运用的时候进行注意

更新数据

-(void)updatePerson: (Person *)person{
    NSString *sql = [NSString stringWithFormat :@"update stus set phone ='%@', address = '%@' where name = '%@'", person.phone, person.address, person.name];
    char *error;
    int result = sqlite3_exec(sqlite, [sql UTF8String], NULL, NULL, &error);
    if (result == SQLITE_OK) {
        NSLog(@"更新数据成功");
    }
    else {
        NSLog(@"更新数据失败:%s",error);
    }
    sqlite3_close(sqlite);
}

这个程序是根据学生名字来进行更新相关信息

查询数据

(NSArray<Person *> *)queryPersonByName: (NSString *)name{
    NSString *sql = [NSString stringWithFormat:@"SELECT phone, address FROM stus WHERE name = '%@'", name];
    sqlite3_stmt *stmt;
    int result = sqlite3_prepare_v2(sqlite, [sql UTF8String], -1, &stmt, NULL);
    NSMutableArray *person = [NSMutableArray array];
    if (result == SQLITE_OK) {
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            const char *Phone = (const char *) sqlite3_column_text(stmt, 0);
            const char *cAddress = (const char *)sqlite3_column_text(stmt, 1);
            Person *p = [[Person alloc] initWithName:name andPhone:[NSString stringWithUTF8String:Phone] andAddress:[NSString stringWithUTF8String:cAddress]];
            [person addObject:p];
        }
    } else {
        NSLog(@"查询失败: %s", sqlite3_errmsg(sqlite));
    }
    
    return person;
}

sqlite3_stmt *stmt; 声明了一个 sqlite3_stmt 结构体指针 stmt,相当于行指针,用于存储编译后的 SQL 语句。

sqlite3_prepare_v2 函数编译 SQL 语句,将编译结果放入 stmt,以供后续执行。

result 会接收 sqlite3_prepare_v2 的返回值,若 result 等于 SQLITE_OK,则表示 SQL 语句准备成功。

sqlite3_step:逐行获取查询结果。如果 sqlite3_step 返回 SQLITE_ROW,说明查询到了数据。

sqlite3_column_text:用于获取当前行的指定列值。sqlite3_column_text(stmt, 0) 获取第一列 (phone) 的值,sqlite3_column_text(stmt, 1) 获取第二列 (address) 的值。

FMDB

了解完相关的SQLite3的原理,我们可以也使用第三方库FMDB来进行来进行数据库存储

FMDB是iOS 和 macOS 平台上一个用于操作 SQLite 数据库的库,它封装了 SQLite 的 C 语言 API,使得在 Objective-C 中处理数据库操作更加简便和安全。

新建类

我们新建一个Model

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Student : NSObject
@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) int age;
@property (strong, nonatomic) NSString *sex;
-(instancetype)initWithName:(NSString *)name andAge:(int)age andSex:(NSString *)sex;
@end

NS_ASSUME_NONNULL_END

建立FMDB数据库

NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *sqlFilePath = [path stringByAppendingPathComponent:@"stu.db"];
self.db = [FMDatabase databaseWithPath:sqlFilePath];

我们用拼接的方法,首先获取沙盒文件之中Document路径

然后我们就用以下方式用路径来创建数据库

  1. 一个文件的系统路径:如果不存在该路径,会自动创建
  2. 一个空字符串@"":在一个临时目录下创建一个空的数据库,当数据库连接关闭,自动删除数据库文件
  3. NULL:会创建一个内存中临时数据库,当数据库连接关闭的时候,将被自动删除

后面两种不是我们要的因为没有保存数据,那么我们使用第一种方法

FMDatabase *db = [FMDatabase databaseWithPath:sqlFilePath];
self.DataBase = db;

打开数据库

-(void)createDB{
    NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES).firstObject;
    NSString *fileName = [docPath stringByAppendingPathComponent :@"student.db"];
    self.db = [FMDatabase databaseWithPath: fileName];
    BOOL isSuccess = [self.db open];
    if (isSuccess) {
        NSLog(@"打开数据库成功");
    }
    else {
        NSLog(@"打开数据库失败");
    }
}

创建表

-(void)createTab {
    [self createTab];
    NSString *sql = @"create table t_stu(id integer primary key autoincrement, name text, age integer, sex text)";
    BOOL isSuccess = [self.db executeUpdate: sql];
    if (isSuccess) {
        NSLog(@"数据表创建成功");
    } else {
        NSLog(@"数据表创建失败");
    }
    [self.db close];
}

添加数据

-(void)insertStu:(Student *)stu{
    [self createDB];
    NSString *sql = @"insert into t_stu(name, age, sex) values (?, ?, ?)";
    BOOL isSuccess = [self.db executeUpdate: sql, stu. name, @(stu.age), stu.sex];
    if (isSuccess) {
        NSLog(@"数据插入成功");
    }
    else {
        NSLog(@"数据插入失败");
    }
    [self.db close];
}

注意:对于FMDB来说它的操作对象必须是OC之中的对象,而不能是C语言之中的int,double的相关类型,所以需要我们将相关内容的用NSNumber进行分装

删除数据

-(void)deleteStu: (NSString *)stuName{
    [self createDB];
    NSString *sql = @"delete from t_stu where name = ?" ;
    BOOL isSuccess = [self.db executeUpdate: sql, stuName];
    if (isSuccess) {
        NSLog(@"数据删除成功");
    } else {
        NSLog(@"数据删除失败");
    }
    [self.db close];
}

更改数据

-(void)updateStu: (Student *)stu{
    [self createDB];
    NSString *sql = @"update t_stu set age =?, sex =? where name = ?" ;
    BOOL isSuccess = [self.db executeUpdate:sql, @(stu.age), stu.sex, stu.name];
    if (isSuccess) {
        NSLog(@"数据修改成功");
    }
    else {
        NSLog(@"数据修改失败");
    }
    [self.db close];
}

查询数据

-(NSArray<Student *> *)queryStu:(NSString *)stuName{
    [self createDB];
    NSMutableArray *stus = [NSMutableArray array];
    NSString *sql = @"select age, sex from t_stu where name = ?";
    FMResultSet *result = [self.db executeQuery:sql, stuName];
    while (result.next) {
        NSString *sex = [result stringForColumn :@"sex"];
        int age = [result intForColumn:@"age"];
        Student *stu = [[Student alloc]initWithName:stuName andAge:age andSex:sex];
        [stus addObject: stu];
    }
    [self.db close];
    return stus;
}

总结

这还是一个比较粗糙的FMDB的实用说明,浅层的学习也足够知乎日报项目的实用,后续的相关学习就等知乎日报完成之后在。再进行系统规划吧

参考文章

SQLite 入门教程(非常详细)零基础入门到精通,收藏这一篇就够了

合集·iOS开发基础-基于Objective-C语言

;