IOS

关注公众号 jb51net

关闭
首页 > 软件编程 > IOS > iOS Sqlite FMDB 使用

iOS中Sqlite和FMDB使用详解

作者:云层之上

这篇文章主要为大家介绍了iOS中Sqlite和FMDB使用方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

正文

本文重点说下常用的本地数据库操作,Sqlite和封装的FMDB的使用,以及Model的存与取。

效果图

什么是数据库

iOS中的数据存储方式

什么是SQLite

调试软件使用的是Navicat,支持大部分主流数据库(包括SQLite) 

SQL语句的种类

数据定义语句(DDL:Data Definition Language)

数据操作语句(DML:Data Manipulation Language)

数据查询语句(DQL:Data Query Language)

字段类型

SQLite将数据划分为以下几种存储类型:

Sqlite使用:

一、创建表

示例:create table t_student (id integer, name text, age inetger, score real) ; 

良好的数据库编程规范应该要保证每条记录的唯一性,为此,增加了主键约束,也就是说,每张表都必须有一个主键,用来标识记录的唯一性,在创表的时候用primary key声明一个主键:

示例:create table t_student (id integer primary key, name text, age integer) ;

主键的设计原则:

integer类型的id作为t_student表的主键。

如果想要让主键自动增长(必须是integer类型),应该增加autoincrement,

示例:create table t_student (id integer primary key autoincrement, name text, age integer) ; 

二、删表

注意:这个删是将整个表删除

示例:drop table if exists t_student ;

三、增(插入数据insert)

示例:insert into t_student (name, age) values (‘小虎牙’, 10) ; 数据库中的字符串内容应该用单引号 ‘ ’ 括住

四、删(删除数据delete)

示例:delete from t_student ;

注意:上面的示例会将t_student表中所有记录都删掉

五、改(更新数据update)

示例:

update t_student set name = ‘rc', age = 18 ; 

注意:上面的示例会将t_student表中所有记录的name都改为rc,age都改为18

六、查(查询数据select)

示例 :  select name, age from t_student ;select * from t_student ;

七、条件语句

如果只想更新或者删除某些固定的记录,那就必须在DML语句后加上一些条件 条件语句的常见格式

示例:

八、起别名

格式:

示例:

九、计算记录的数量

十、排序

按照某个字段的值,进行排序搜索 select * from t_student order by 字段 ; 示例:select * from t_student order by age ;

默认是按照升序排序(由小到大),也可以变为降序(由大到小) 降序 :select * from t_student order by age desc ; 升序(默认):select * from t_student order by age asc ;

用多个字段进行排序 先按照年龄排序(升序),年龄相等就按照身高排序(降序) 示例:select * from t_student order by age asc, height desc ;

十一、limit

使用limit可以精确地控制查询结果的数量,比如每次只查询10条数据

limit常用来做分页查询,比如每页固定显示5条数据,那么应该这样取数据

第1页:limit 0, 5 第2页:limit 5, 5 第3页:limit 10, 5 ... 第n页:limit 5*(n-1), 5

十二、简单约束

建表时可以给特定的字段设置一些约束条件,常见的约束有

建议:尽量给字段设定严格的约束,以保证数据的规范性

name字段不能为null,并且唯一 age字段不能为null,并且默认为1

示例:create table t_student (id integer, name text not null unique, age integer not null default 1) ; 

FMDB使用:

优点:

FMDB

FMDB有三个主要的类

打开数据库

通过指定SQLite数据库文件路径来创建FMDatabase对象

FMDatabase *db = [FMDatabase databaseWithPath:path];
if (![db open]) {
    NSLog(@"数据库打开失败!");
}

文件路径有三种情况

更新数据库

在FMDB中,除查询以外的所有操作,都称为“更新”,create、drop、insert、update、delete等 使用executeUpdate:方法执行更新

- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments

示例 :[db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @18, @"rc"]

执行查询

- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
// 查询数据
FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_student"];
// 遍历结果集
while ([rs next]) {
    NSString *name = [rs stringForColumn:@"name"];
    int age = [rs intForColumn:@"age"];
    double score = [rs doubleForColumn:@"score"];
}

FMDatabaseQueue

FMDatabase这个类是线程不安全的,如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题 为了保证线程安全,FMDB提供方便快捷的FMDatabaseQueue类

// FMDatabaseQueue的创建
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
...
[queue inDatabase:^(FMDatabase *db) {
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"rc"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
    FMResultSet *rs = [db executeQuery:@"select * from t_student"];
    while ([rs next]) {
        // …
    }
}];

事务、回滚 : 操作数据库时,会出现这种情况:更新10条记录,当更新到第5条时,服务器宕机了,后面的麻烦就来了,我们要每次判断哪条记录更新了,哪条记录没更新!这时候就用到了事务,将更新10条记录放到一个事务中,成功完成所有更新操作时再提交,只要其中一条记录更新失败就回滚,回到初始状态,简单的说就是要么全部成功,要么全部不成功!

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"rc"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
    FMResultSet *rs = [db executeQuery:@"select * from t_student"];
    while ([rs next]) {
        // …
    }
}];
//事务回滚
*rollback = YES;

对模型进行存取

以头条新闻为例,一条新闻为一条记录:

1.先在本地数据库查找是否存在缓存,存在则显示缓存的新闻,

2.不存在则去头条服务器请求数据显示,同时缓存

思路很简单: 要做到请求了今日头条的新闻缓存到本地数据库后,下次打开直接在数据库取出数据后显示。

两种情况:

1、直接缓存后天返回的json数据 ,好处是简单,避免了模型转字典的过程,数据原始,但是如果对数据有更新,比如:新闻是否已读等。

2、将服务器返回的json转成model(模型)再缓存,缺点就是可能在模型中有为了方便开发而新增的字段,而这些字段是不需要进行缓存的。

第1种比较简单,就以第2种为例: 写了一个新闻缓存的工具类

NewsCacheTool.h
/**缓存新闻数据到本地数据库*/
+ (void)saveNewsToDatabase:(NSArray *)newsArray;
/**读取新闻(userID对应用户的id,同个应用可能存在多个账号登录情况)*/
+ (NSArray *)selectNewsToDatabase:(NSString *)userID;
/**清除缓存*/
+ (void)clearNewsCache:(void(^)(BOOL success))flag;
NewsCacheTool.m
static FMDatabase *_db;
// 第一次使用就开始创建表
+ (void)initialize{
    NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
    NSString *filePath = [cachePath stringByAppendingPathComponent:@"News.sqlite"];
    _db = [FMDatabase databaseWithPath:filePath];
    if ([_db open]) {
        NSLog(@"打开数据库成功");
        // 自增主键、userID、二进制数据流
        NSString *sql = @"create table if not exists t_news (id integer primary key autoincrement,userID text,dict blob);";
        BOOL success = [_db executeUpdate:sql];
        if (success) {
            NSLog(@"创建表成功");
        }else{
            NSLog(@"创建表失败");
        }
    }else{
        NSLog(@"打开数据库失败");
    }
}
// 缓存数据,这里为了安全起见应该使用事务
+ (void)saveNewsToDatabase:(NSArray *)newsArray{
    // 遍历模型数组
    for (NewsModel *nesw in newsArray){
        // 用户的id应该从自己的服务器取得
        NSString *userID = @"001";
        // 这是模型转字典的,自己用runtime简单实现了,有很多优秀的第三方库可以使用,自选
        NSDictionary * newsDic = [nesw getDictionayFromModel];
        NSError *error;
        NSData *data;
        if (@available(iOS 11.0, *)){
            data = [NSKeyedArchiver archivedDataWithRootObject:newsDic requiringSecureCoding:YES error:&error];
        }else{
            data = [NSKeyedArchiver archivedDataWithRootObject:newsDic];
        }
        if (data == nil || error) {
            NSLog(@"缓存失败:%@", error);
            return;
        }
        BOOL success = [_db executeUpdate:@"insert into t_news (userID,dict) values(?,?)",userID,data];
        if (success) {
            NSLog(@"插入成功");
        }else{
            NSLog(@"插入失败");
        }
    }
}
// 在数据库中读取数据
+ (NSArray *)selectNewsToDatabase:(NSString *)userID{
    NSString *sql = [NSString stringWithFormat:@"select * from t_news where userID = '%@';",userID];
    FMResultSet *set = [_db executeQuery:sql];
    NSMutableArray *array = [NSMutableArray array];
    while ([set next]) {
        NSData *data = [set dataForColumn:@"dict"];
        NSError *error;
        NSDictionary *dic;
        if (@available(iOS 11.0, *)) {
            dic = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSObject class] fromData:data error:&error];
        } else {
            dic = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        }
        if(dic){
            NewsModel *news = [[NewsModel alloc]initWithDictionary:dic];
            [array addObject:news];
        }
    }
    return array;
}
// 清除新闻缓存
+ (void)clearNewsCache:(void (^)(BOOL success))flag{
    BOOL success = [_db executeUpdate:@"delete from t_news;"];
    if(flag){
        flag(success);
    }
}

模型转字典的过程中,在NewsModel类中,用runtime简单实现了,实际开发中可能会多层嵌套字典或数据,市面上有很多成熟优秀的轮子,可自行选择。

SqliteDemo

以上就是iOS中Sqlite和FMDB使用详解的详细内容,更多关于iOS Sqlite FMDB 使用的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文