ios - 关于数据持久化不看我看谁(二)

📚.png

所谓的数据库 无非就是进行增删查找的一些操作,在网上看了很多对数据库的封装,但是人家封装的不一定适合自己的项目,还有一个缺点就是,如果使用人家封装的,用的时候觉得还不错,挺好。但是如果有一天忽然发现有bug了,改的动人家代码还好,改不动就GG了,最好的还是自己的写一套适合自己项目需求的。

目录
一、Navicat Premium 的使用
二、SQLite3(待补充)
三、FMDB
3.1 什么是FMDB?
3.2 优点
3.3 核心类
3.4 FMDB基本语法
3.5 FMDB基本使用(创建表、增删改查)
3.6 FMDatabaseQueue基本使用
四、CoreData
4.1 什么是CoreData?
4.2 优点
4.3 核心类
4.4 CoreData基本操作
4.5 CoreData基本使用(创建表、增删改查)

写这篇文章时,光想了2天该如何动手,怎么样写才更加清新明了,让新手一看即懂,让码手也能巩固知识。在网上找了各种大牛封装的,其实也许人家封装得好,但是有些时候也许并不适合自己的项目需求,人家封装的,自己改动也麻烦,何不如自己动手来一趟。最后决定采取(理论知识+示例demo+图片gif)相结合,这样使读者看起来也不至于那么枯燥乏味。笔者在写这篇文章又花了笔者断断续续2天的时间,才整理完毕!


引言
对数据的操作条件是先创建,其次操作(增删查改),首先先说说Sqlite,得看懂Sqlite语句所表达的意思。其次再看FMDB,逐步进入深化。

其次

不要对Sqlite产品一种抵抗心理,试着慢慢看下去的心理其实并不难,其实我们要掌握的东西其实不多,无非就创建表,其次便是增删查改。加一起也就5句语句。多敲几遍就会了。其实Very easy

首先我们先一个软件 叫 Navicat Premium的软件

Navicat Premium.png

百度一下下载一个即可。相信有一定开发经验,以及写过java的一定知道这是一款什么软件。简单来说就是一款的数据库管理工具。

先大致介绍一下Navicat Premium的使用,怕有些新手看着一脸懵逼的感觉。

一、Navicat Premium 的使用

在创建表之前 首页我们要了解一个概念
主键(Primary key)用来唯一地标识某一条记录。粗俗一点来说相当一个人的身份证号码,名字可以有相同的,但是身份证号码是唯一性的。
主键可以是一个字段或者多个字段
主键的设计原则:
主键对用户是没有意义的,主键不包含动态变化的数据,是由计算机自动生成的

创建表

//create  table if exists:如果不存在则创建表
//id integer PRIMARY key AUTOINCREMENT:将id作为主键 自动增长
//not null :不能为空
CREATE TABLE IF NOT EXISTS User2(id integer PRIMARY key AUTOINCREMENT,name text not null,age real default 1,sex text not null) 

创建表之后直接选中Tables然后按快捷键command+r刷新即可

创建表.gif

插入数据

--插入数据如果是字符串加上单引号
insert into User2(name,age,sex) VALUES ('阳阳', 19,'钕') 

图片.png

更新全部值更新

//将表中所有的age设为为100
update User2  set age = 100
/**
其他操作:
将id大于20的 年龄设置为50
*/
//update User2 set age = 55 where id > 20

多插入几条,再 command+ r 走起

111.gif

删除

--删除age = 5
-- delete from User2 where age = 5

--删除sex为男 且年龄小于20的
-- delete from User2 where sex = '男' and age <20 
--名称不是XXX或年龄不是55
-- delete from User2 where name is not "阳阳" or age != 55

全部删除

delete from User2
112.gif

二、SQLite3

SQlite:存储一些大批量的数据。
优点:
①占用资源低
②处理速度快
不比比了。。。开始撸串串

三、FMDB

3.1 什么是FMDB?

FMDB是以OC的方式封装了Sqlite.使其操作性更加面向对象。

3.2 优点

1.使用起来更加面向对象,省去很多C语言代码
2.提供了多线程安全的数据库操作方法,有效地防止数据混乱

3.3 核心类
  • FMDatabase:可以理解成一个数据库。一个FMDatabase对象就代表一个单独的SQLite数据库,用来执行SQL语句
  • FMResultSet:使用FMDatabase执行查询后的结果集
  • FMDatabaseQueue:用于在多线程中执行多个查询或更新,它是线程安全的
3.4 FMDB基本语法

查询(executeQuery):除了查询使用executeQuery,其余的使用更新

    FMResultSet *resultSet  = [_db executeQuery:@"select * from User"];

更新(executeUpdate):包括create,update,insert,delete,drop(不区分大小写) 都使用executeUpdate

例如下图:

图片.png
3.5 FMDB基本使用(创建表、增删改查)
3.5.1 创建表

创建表之前先设置数据库路径,然后再创建表

@property(nonatomic,strong) FMDatabase *db;  
@property(nonatomic,assign) NSInteger  count;
- (void)viewDidLoad {

    [super viewDidLoad];
    _userModel = [UserModel new];
    _count = 1;
    //设置数据库名称
    NSString *fileName = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"User.sqlite"];
    NSLog(@"%@",fileName);
    //2.获取数据库
    _db = [FMDatabase databaseWithPath:fileName];
    if ([_db open]) {
        NSLog(@"打开数据库成功");
    }else{
        NSLog(@"打开数据库s失败");
    }    
}
#pragma mark - 创建表
- (IBAction)createOnClick {
    
// CREATE TABLE IF NOT EXIST:表不存在 再创建    AUTOINCREMENT 自动增长   NOT NULL 不能为空
//CREATE TABLE User (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL, sex text NOT NULL)
//id integer PRIMARY KEY AUTOINCREMENT 将id作为主键 自动增长
    BOOL result = [self.db executeUpdate:@"CREATE TABLE IF NOT EXISTS User (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL, sex text NOT NULL);"];
    if (result) {
        NSLog(@"创建表成功");
    }else{
        NSLog(@"创建表失败");
    }
}

根据后台打印, 我们复制路径然后前往该文件夹。然后使用Navicat打开User.sqlite.

/Users/love/Library/Developer/CoreSimulator/Devices/418C03C5-5D16-48A5-9499-DB13892EAB2A/data/Containers/Data/Application/EB85DCE1-479D-4F02-95A6-4BBD9840BC64/Documents/User.sqlite
图片.png
3.5.2 增删改查
#pragma mark - 添加数据
- (IBAction)addDataOnClick {
    NSString *name  = [NSString stringWithFormat:@"%@号大美钕",@(_count)];
    NSInteger age = _count;
    NSString *sex = _count%2 ==0 ? @"女":@"男";
    BOOL result = [self.db executeUpdate:@"INSERT INTO User (name,age,sex) VALUES (?,?,?)",name,@(age),sex];
    _count ++;
    result == YES ? NSLog(@"插入成功"):NSLog(@"插入失败");
}
#pragma mark - 删除数据
- (IBAction)deleteDataOnClick {
    
    BOOL result = [_db executeUpdate:@"delete from User where id = ?",@(5)];

    if (result) {
        NSLog(@"删除成功");
    }else{
        NSLog(@"删除失败");
    }
}
#pragma mark - 查询数据
- (IBAction)queryDataOnClick {
    FMResultSet *resultSet  = [_db executeQuery:@"select * from User"];
//遍历查询
    while ([resultSet next]) {
     //拿到每条数的id
        int idNum = [resultSet intForColumn:@"id"];
        NSString *name = [resultSet objectForColumnName:@"name"];
        int age = [resultSet intForColumn:@"age"];
        NSString *sex = [resultSet objectForColumnName:@"sex"];
        NSLog(@"学号:%@ 姓名:%@ 年龄:%@ 性别:%@",@(idNum),name,@(age),sex);
    }
}
#pragma mark - 修改数据
- (IBAction)changeDataOnClick{
    NSString *newName = @"花花";
    NSString *oldName = @"3号大美钕";
    
    BOOL result = [_db executeUpdate:@"update User set name = ? where name = ?",newName,oldName];
    
    if (result) {
        NSLog(@"修改成功");
    }else{
        NSLog(@"修改失败");   
    }   
}
#pragma mark - 清除数据
- (IBAction)cleanDataOnClick { 
    BOOL result = [_db executeUpdate:@"drop table if exists User"];
    if (result) {
        
        NSLog(@"清除表中的所有数据成功");
    }else{        
        NSLog(@"清除表中的所有数据失败");
    }
}
1234.gif
3.6 FMDatabaseQueue基本使用

FMDatabase是线程不安全的,当FMDB数据存储想要使用多线程的时候,FMDatabaseQueue就派上用场了。执行命令的时候非常方便,直接在一个block中进行操作
例如:

建议对数据库的操作,最好写一个工具类对数据进行增删查改,这里只是写了一个简单滴创建表的示例,其他的增删改查的方法仿之写法即可。
/**********控制器直接调用**************/

 [[DBTool shareInstance] createTable];

/**********DBTool类**************/
static DBTool *instance = nil;
+ (instancetype)shareInstance{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc]init];
    });
    return instance;
}
- (FMDatabaseQueue *)dbQueue{

    if(!_dbQueue){
        _dbQueue = [FMDatabaseQueue databaseQueueWithPath:[self dbPath]];
    }
    return _dbQueue;
}
- (NSString *)dbPath{
    NSString *dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:@"Demo1.sqlite"];
    NSLog(@"数据库路径---%@",dbPath);
    return dbPath;
}

- (void)createTable{
    [self.dbQueue inDatabase:^(FMDatabase *db) {
        BOOL result = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_stu (id integer PRIMARY KEY AUTOINCREMENT, name text NOT NULL, age integer NOT NULL, sex text NOT NULL);"];
        if (result) {
            NSLog(@"创建表成功");   
        }else{
            NSLog(@"创建表失败");
        }
    }];
}

四、CoreData

4.1 什么是CoreData?

CoreData:对SQLite3的一层面向对象的包装,本质上还是转换成对应的SQL语句去执行。可以管理实体以及实体之间的关联关系的持久化。

4.2 优点
  • 1.不用写 SQL 语句
  • 2.代码清晰,如果有语法错误会即使提示,而不是等到运行时才知道错误.
  • 3.可视化的结构,让对于字段的增删清晰明朗
  • 4.用于做数据持久化,适合做大量的存储和查询.
4.3 核心类
  • NSManagedObiectModel(托管对象模型):

代表CoreData的模型文件

  • NSPeristentStoreCoordinator(持久化存储协调器):

负责管理底层的存储文件,例如SQLite数据库等。

  • NSManagedObjectContext(托管对象上下文):

负责应用和数据库之间的交互。例如:应用对实体所做的任何增、删、查、改操作都必须通过该对象来完成

  • NSEntityDescription(实体描述):

对象相当于实体的抽象。实体描述定义了该实体的名字、实体的实现类,并用一个集合定义了该实体包含的所有属性

  • NSFetchRequest(抓取请求):

该对象封装了查询实体的请求,包括程序需要查询哪些实体、查询条件、排序规则等。抓取请求定义了本次查询的实体的名字、抓取请求的查询条件,通过NSPredicate来表示,并用一个NSArray集合定义了所有的排序规则

4.4 CoreData基本操作

创建有两种方式

方式一:创建工程时勾选UserCoreData

图片.png

勾选之后进入工程你会发现系统帮我们创建了一个后缀名为” .xcdatamodeld”的文件

图片.png

方式二:新建一个DataModel文件。名字自己定,创建一个xcdatamodeld文件

图片.png

有了XX.xcdatamodeld文件后 我们打开,进行如下操作:

图片.png
图片.png
图片.png

最后会生成如下4个文件,创建模型也算是大功告成了

图片.png

紧接着直接在控制器中导入即可

#import "UserInfo+CoreDataClass.h"
4.5 CoreData基本使用(创建表、增删改查)

4.5.1 创建表

//注意:需要导入#import <CoreData/CoreData.h>头文件
@property(nonatomic,strong)NSManagedObjectContext *context;

- (IBAction)c_createTable{
    //注意创建时候的后缀用momd
    NSURL *pathurl = [[NSBundle mainBundle]URLForResource:@"DB" withExtension:@"momd"];
    
    NSManagedObjectModel *model  = [[NSManagedObjectModel alloc]initWithContentsOfURL:pathurl];
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:model];
    
    NSString *dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)lastObject]stringByAppendingPathComponent:@"UserInfo.sqlite"];
    
    NSLog(@"____%@",dbPath);
    NSError *error = nil;
    
    NSURL *url = [NSURL fileURLWithPath:dbPath];
   [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil
                             error:&error];
    if (error == nil) {   
        NSLog(@"数据库添加成功");
    }else{
        NSLog(@"数据库添加失败");
    }
    self.context  = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.context.persistentStoreCoordinator = psc;
}

此时根据后台打印路径前往文件夹打开

图片.png

查看UserInfo中的定义的属性

图片.png

4.5.2 增删改查

#pragma mark - 添加数据
- (IBAction)c_addDataOnClick{
     //运用NSEntityDescription创建NSManagedObject对象
    UserInfo *user = [NSEntityDescription insertNewObjectForEntityForName:@"UserInfo" inManagedObjectContext:self.context];
    
   user.name = [NSString stringWithFormat:@"%@号大美钕",@(_count)];
    user.age = _count;
    user.sex = _count%2 ==0 ? @"女":@"男";
 
    NSError *savaError = nil;
    BOOL result = [self.context save:&savaError];
   _count ++;
    result == YES ? NSLog(@"插入成功"):NSLog(@"插入失败");
}
#pragma mark - 删除数据--->(年龄大于等于5的删除)
- (IBAction)c_deleteDataOnClick{
    
    //NSFetchRequest:一条查询请求,相当于 SQL 中的select语句
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"UserInfo"];
    //NSPredicate:谓词,指定一些查询条件,相当于 SQL 中的where
    NSPredicate *predicate  = [NSPredicate predicateWithFormat:@"age>=%d",5];
    fetchRequest.predicate = predicate;
    NSError *error = nil;
    NSArray *arrResult  = [self.context executeFetchRequest:fetchRequest error:&error];
    
    if (arrResult.count >0) {
        for (UserInfo *user in arrResult) {
            NSLog(@"%zd",user.age);
            [self.context deleteObject:user];
        }
     BOOL result =   [self.context save:nil];
        result == YES? NSLog(@"删除成功"):NSLog(@"删除失败");   
    }
}
#pragma mark - 查询数据 --->(查询数据中的所有数据并打印)
- (IBAction)c_queryDataOnClick{
    //获取这个类
    NSEntityDescription *entity = [NSEntityDescription  entityForName:@"UserInfo" inManagedObjectContext:self.context]; 
    //创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
    //设置查询请求的实体
    [fetchRequest setEntity:entity];
    NSArray *arrResult = [self.context executeFetchRequest:fetchRequest error:nil];
    for (UserInfo *user in arrResult) {
        NSLog(@"名字是:%@ 性别是:%@ 年龄是:%zd",user.name,user.sex,user.age);
    }
}
#pragma mark - 修改数据 --->(年龄等于2的改成等于1000)
- (IBAction)c_changeDataOnClick{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"UserInfo"];
    NSPredicate *predicate  = [NSPredicate predicateWithFormat:@"age=%d",2];
    fetchRequest.predicate = predicate;
    NSError *error = nil;
    NSArray *arrResult  = [self.context executeFetchRequest:fetchRequest error:&error];
    for (UserInfo *user in arrResult) {
        NSLog(@"%zd",user.age);
        user.age = 1000;
    }
   BOOL result  =   [self.context save:&error];
    result == YES? NSLog(@"修改成功"):NSLog(@"修改失败");
}

#pragma mark - 清除数据 --->(清空数据库所有数据)
-(IBAction)c_cleanDataOnClick{
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"UserInfo"];
    
    NSError *error = nil;
    NSArray *arrResult  = [self.context executeFetchRequest:fetchRequest error:&error];
    for (UserInfo *user in arrResult) {
        [self.context deleteObject:user];
    }
   BOOL  result =  [self.context save:&error];
    result == YES? NSLog(@"清空所有数据成功"):NSLog(@"清空所有数据失败");
}
操作演示gif
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • 1.CoreData 1.1 CoreData概述 1)Core data 是数据持久存储的最佳方式 2)Core...
    微春风阅读 3,810评论 0 10
  • 阅读完本书,首先给我的感觉是内容有点对不起它的¥59.80定价,全书主要讲了两块内容,一块是SQLite3,...
    瑞小萌阅读 2,825评论 4 33
  • 在程序开发中,数据层永远是程序的核心结构之一。我们将现实事物进行抽象,使之变成一个个数据。对这些数据的加工处理是代...
    sindri的小巢阅读 16,830评论 13 85
  • 在程序开发中,数据层永远是程序的核心结构之一。我们将现实事物进行抽象,使之变成一个个数据。对这些数据的加工处理是代...
    帅不过oneS阅读 596评论 0 1
  • 忘记了已经连续几天持续这样的阴天。世界变得灰蒙蒙的,人也变得灰突突的,心也变得敏感而悲伤起来。想念阳光的热烈干脆温暖。
    眠月站阅读 583评论 0 50