000-数据存储
一、iOS常见的几种数据存储
1.plist列表(writeToFile:)
2.偏好设置(NSUserDefaults)
3.归档(NSCodeing、NSKeyedArchiver)
4.SQLite3(数据库、一般借助第三方库FMDB)
5.Core Data(对象型的数据库)
二、应用沙盒
1.简介
每一个应用都有自己的应用沙盒,也就是系统文件目录。每一个应用都必须呆在自己的应用沙盒中,不可以访问别的应用沙盒(iOS8已经允许访问其他应用沙盒)
2.结构

1)Documents:保存应⽤运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录
2)Library/Caches:保存应用运行时⽣成的需要持久化的数据,iTunes同步设备时不会备份该目录(一般存储体积大、不需要备份的非重要数据)
3)Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置) 应⽤会在该⺫录中查找应⽤的设置信息。iTunes同步设备时会备份该目录
4)tmp:保存应⽤运行时所需的临时数据,使⽤完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时 不会备份该目录
3.应用沙盒目录的获取方法
1 // 1.获取沙盒根目录 2 NSString *homePath = NSHomeDirectory(); 3 4 // 2.获取Documents目录 5 // 第一种方法:根目录直接拼接(不建议采用,原因是如果苹果公司将文件夹名称改了,那就会很麻烦) 6 NSString *docPath1 = [homePath stringByAppendingPathComponent:@"Documents"]; 7 // 第二种方法:方法获取(推荐使用) 8 // 参数1:NSDocumentDirectory 查找Documents文件夹 9 // 参数2:NSUserDomainMask 在用户目录下查找 10 // 参数3:YES 是否展开用户目录(NO就是~,YES就是展开) 11 NSString *docPath2 = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject; 12 13 // 3.拼接文件路径 14 // 假设Documents文件夹下面存储一张图片image.png 15 NSString *filePath = [docPath2 stringByAppendingPathComponent:@"image.png"];
三、plist列表
1.简介
1)plist列表只能存储一些系统自定义的数据类型,例如:NSString、NSArray、NSNumber、NSDictionary、NSData等等
2)plist列表不能存储一些我们自定义的对象类型,例如:自定义的Person对象
3)我们一般将plist文件保存在沙河目录Documents文件夹下,文件后缀名为.plist
2.实际应用
1 #pragma mark - 数据保存 2 - (void)saveData 3 { 4 // 1.获取Documents目录 5 NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject; 6 7 // 2.拼接文件路径 8 // 数组形式保存文件名:user1.plist 9 // 字典形式保存文件名:user2.plist 10 NSString *filePath1 = [docPath stringByAppendingPathComponent:@"user1.plist"]; 11 NSString *filePath2 = [docPath stringByAppendingPathComponent:@"user2.plist"]; 12 13 // 3.保存数据 14 // 第一种情况:数组形式保存 15 NSArray *userArr = @[@"Frank", @25, @"M", @"湖北黄冈"]; 16 [userArr writeToFile:filePath1 atomically:YES]; 17 // 第二种情况:字典形式保存 18 NSDictionary *userDic = @{@"name": @"Frank", @"age": @25, @"sex": @"M", @"home": @"湖北黄冈"}; 19 [userDic writeToFile:filePath2 atomically:YES]; 20 } 21 22 #pragma mark - 读取数据 23 - (void)readData 24 { 25 // 1.获取Documents目录 26 NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject; 27 28 // 2.拼接文件路径 29 // 数组形式保存文件名:user1.plist 30 // 字典形式保存文件名:user2.plist 31 NSString *filePath1 = [docPath stringByAppendingPathComponent:@"user1.plist"]; 32 NSString *filePath2 = [docPath stringByAppendingPathComponent:@"user2.plist"]; 33 34 // 3.读取数据 35 // 第一种情况:数组形式 36 NSArray *userArr = [NSArray arrayWithContentsOfFile:filePath1]; 37 NSLog(@"userArr = %@", userArr); 38 // 第二种情况:字典形式 39 NSDictionary *userDic = [NSDictionary dictionaryWithContentsOfFile:filePath2]; 40 NSLog(@"userDic = %@", userDic); 41 }
四、偏好设置
1.简介
1)很多应用都支持偏好设置,iOS提供了一套标准的解决方案来为应用加入偏好设置。
2)每一个应用都有一个实例NSUserDefault,iOS就是通过它来存储偏好设置
3)文件存储在沙河目录Library下文件夹Preferences,文件后缀名为.plist
4)注意我们的偏好设置是将所有的东西都保存在同一个文件中,且主要用来保存应用的一些设置信息
5)偏好设置同plist列表一样只能保存iOS系统自定义的一些数据类型
2.实际应用
1 #pragma mark - 数据保存 2 - (void)saveData 3 { 4 // 1.获取NSUserDefaults对象 5 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 6 7 // 2.保存数据 8 [defaults setObject:@"Frank" forKey:@"name"]; 9 [defaults setInteger:25 forKey:@"age"]; 10 [defaults setDouble:1.69 forKey:@"height"]; 11 [defaults setFloat:148.2f forKey:@"weight"]; 12 [defaults setObject:@"M" forKey:@"sex"]; 13 14 // 3.同步数据(强制数据立即保存) -- 有点类似我们刷新表 15 // 若没有同步数据,系统会在将来某一时间点自动将数据保存到Preferences文件夹下 16 [defaults synchronize]; 17 } 18 19 #pragma mark - 读取数据 20 - (void)readData 21 { 22 // 1.获取NSUserDefaults对象 23 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 24 25 // 2.读取保存的数据 26 NSString *name = [defaults objectForKey:@"name"]; 27 NSInteger age = [defaults integerForKey:@"age"]; 28 double height = [defaults doubleForKey:@"height"]; 29 CGFloat weight = [defaults floatForKey:@"weight"]; 30 NSString *sex = [defaults objectForKey:@"sex"]; 31 32 // 3.打印输出数据 33 NSLog(@"name=%@, age=%ld, height=%.2f, weight=%.2f, sex=%@", name, age, height, weight, sex); 34 }
五、NSKeydeArchiver归档
1.简介
1)与前面两中数据保存方法均不能针对自定义对象类型,归档是专门针对自定义对象类型的数据保存
2)我们一般将归档文件保存在沙盒目录下的Documents文件夹下,其文件后缀名可以是任意的
3)通过plist存储的文件在文件中打开是直接显示的,而归档存储的文件在文件打开时是乱码的,更加安全
2.实际应用
1)自定义对象:UserModel类
1 // UserModel.h文件 2 #import <Foundation/Foundation.h> 3 4 #pragma mark - 归档对象必须遵循<NSCoding>协议 5 @interface UserModel : NSObject<NSCoding> 6 7 @property (nonatomic, copy) NSString *userId; // 用户ID 8 @property (nonatomic, copy) NSString *userName; // 用户名 9 @property (nonatomic, copy) NSString *password; // 用户密码 10 @property (nonatomic, assign) NSInteger age; // 用户的年龄 11 @property (nonatomic, assign) double weight; // 用户的体重 12 @property (nonatomic, assign) float height; // 用户的身高 13 @property (nonatomic, copy) NSString *userTel; // 用户的电话号码 14 @property (nonatomic, copy) NSString *userEmail; // 用户的邮箱 15 16 @end 17 18 19 // UserModel.m文件 20 #import "UserModel.h" 21 22 @implementation UserModel 23 24 #pragma mark - 必须实现的协议方法<NSCoding> 25 // 1.归档 26 - (void)encodeWithCoder:(NSCoder *)aCoder 27 { 28 [aCoder encodeObject:_userId forKey:@"userId"]; // 用户ID 29 [aCoder encodeObject:_userName forKey:@"userName"]; // 用户名 30 [aCoder encodeObject:_password forKey:@"password"]; // 用户密码 31 [aCoder encodeInteger:_age forKey:@"age"]; // 用户的年龄 32 [aCoder encodeDouble:_weight forKey:@"weight"]; // 用户的体重 33 [aCoder encodeFloat:_height forKey:@"height"]; // 用户的身高 34 [aCoder encodeObject:_userTel forKey:@"userTel"]; // 用户的电话号码 35 [aCoder encodeObject:_userEmail forKey:@"userEmail"]; // 用户的邮箱 36 } 37 // 2.解档 38 - (id)initWithCoder:(NSCoder *)aDecoder 39 { 40 if (self = [super init]) { 41 _userId = [aDecoder decodeObjectForKey:@"userId"]; // 用户ID 42 _userName = [aDecoder decodeObjectForKey:@"userName"]; // 用户名 43 _password = [aDecoder decodeObjectForKey:@"password"]; // 用户密码 44 _age = [aDecoder decodeIntegerForKey:@"age"]; // 用户的年龄 45 _weight = [aDecoder decodeDoubleForKey:@"weight"]; // 用户的体重 46 _height = [aDecoder decodeFloatForKey:@"height"]; // 用户的身高 47 _userTel = [aDecoder decodeObjectForKey:@"userTel"]; // 用户的电话号码 48 _userEmail = [aDecoder decodeObjectForKey:@"userEmail"]; // 用户的邮箱 49 } 50 return self; 51 } 52 53 @end
2)保存数据、读取数据工具类:UserDataSaveTool类
1 // UserDataSaveTool.h文件 2 #import <Foundation/Foundation.h> 3 #import "UserModel.h" 4 5 @interface UserDataSaveTool : NSObject 6 7 #pragma mark - 保存用户数据 8 + (void)saveUserModel:(UserModel *)userModel; 9 10 #pragma mark - 获取用户数据 11 + (UserModel *)getUserModel; 12 13 @end 14 15 16 // UserDataSaveTool.m文件 17 #import "UserDataSaveTool.h" 18 19 @implementation UserDataSaveTool 20 21 #pragma mark - 用户信息保存的沙盒路径 22 + (NSString *)getAccoutPath 23 { 24 // 归档文件名为:account.archive 25 NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"account.archive"]; 26 return filePath; 27 } 28 29 #pragma mark - 保存用户数据 30 + (void)saveUserModel:(UserModel *)userModel 31 { 32 // 用户信息(用户信息model:accout)归档 -- 执行这个就会自动跳转执行用户model里的归档协议 33 [NSKeyedArchiver archiveRootObject:userModel toFile:[self getAccoutPath]]; 34 } 35 36 #pragma mark - 获取用户数据 37 + (UserModel *)getUserModel 38 { 39 // 取出账号(账号存的就是一个model) -- 执行这个就会自动跳转执行用户model里的解档协议 40 UserModel *userModel = [NSKeyedUnarchiver unarchiveObjectWithFile:[self getAccoutPath]]; 41 return userModel; 42 } 43 44 @end
3)实际调用
1 #pragma mark - 数据保存 2 - (void)saveData 3 { 4 // 1.初始化自定义对象模型 5 UserModel *userModel = [[UserModel alloc] init]; 6 userModel.userId = @"20099830215"; 7 userModel.userName = @"Frank"; 8 userModel.password = @"9830215"; 9 userModel.age = 25; 10 userModel.weight = 110.0; 11 userModel.height = 1.69; 12 userModel.userTel = @"15112256959"; 13 userModel.userEmail = @"1058842360@qq.com"; 14 15 // 2.借助UserDataSaveTool工具保存对象模型 16 [UserDataSaveTool saveUserModel:userModel]; 17 } 18 19 #pragma mark - 读取数据 20 - (void)readData 21 { 22 // 1.借助UserDataSaveTool工具获取对象模型 23 UserModel *userModel = [UserDataSaveTool getUserModel]; 24 25 // 2.打印输出 26 NSLog(@"userModel = %@", userModel); 27 }
3.注意
1)对象必须遵循NSCoding协议,并且重写协议的两个方法
2)如果是继承,在子类中也是要重写NSCoding协议的两个方法,而且协议方法中必须先调用父类的方法
例如:给上面的UserModel添加一个属性"sex"
1 // PersonModel.h文件 2 #import "UserModel.h" 3 4 @interface PersonModel : UserModel 5 6 #pragma mark - 给子类的属性 7 @property (nonatomic, copy) NSString *sex; // 性别 8 9 @end 10 11 12 // PersonModel.m文件 13 #import "PersonModel.h" 14 15 @implementation PersonModel 16 17 #pragma mark - 重写协议方法<NSCoding> 18 // 1.归档 19 - (void)encodeWithCoder:(NSCoder *)aCoder 20 { 21 // 先调用父类的归档协议方法 22 [super encodeWithCoder:aCoder]; 23 [aCoder encodeObject:@"M" forKey:@"sex"]; 24 } 25 // 2.解档 26 - (instancetype)initWithCoder:(NSCoder *)aDecoder 27 { 28 // 先初始化父类 29 if (self = [super initWithCoder:aDecoder]) { 30 [aDecoder decodeObjectForKey:@"sex"]; 31 } 32 return self; 33 } 34 35 @end
六、SQLite3
1.前言
在我们的实际开发中都需要做一些离线数据的处理,即离线缓存。上面三个方法都可以实现离线缓存,但是它们都有一个致命的弱点:无法存储大批量的数据,存在一些性能的问题。除了这个关键性的问题外,它们还存在各种各样不同的约束问题。例如:对于归档,它只支持一次性写入,一次性读取,这对于大数据的存储有着很大的问题。针对这些问题,数据库SQLite3可以解决。
2.SQLite3的简介
1)3是SQLite的版本号
2)SQLite3是一种轻量级的嵌入式数据库,目前安卓和iOS使用的都是SQLite数据库
3)SQLite的占用资源非常低、处理速度也较MySql、PostgreSQL这两款著名的数据库快
4)SQLite中可以有多张表,表里的元素都是以字段存储和读取
3.SQL语句
1)SQL语句中字段的类型
integer:整型
real:浮点类型
text:文本字符串
blob:二进制数据(比如文件)
2)SQL语句的基本的操作
a.创表
create table 表名(字段名1 字段类型, 字段名2 字段类型, 字段名3 字段类型, ...);
create table if not exists 表名(字段名1 字段类型, 字段名2 字段类型, 字段名3 字段类型, ...);
// 实际上SQLite是无类型的,就算声明为integer类型,还是能存储字符串文本(主键除外),创表时可以不用去声明字段类型
b.删表
drop table 表明;
drop table if exists 表名;
c.插入数据
insert into 表名(字段1, 字段2, ...) values (字段1的值, 字段2的值, ...);
例:insert into t_student(name, age, sex) values ('Frank', 25, 'M');
// 注意:SQL中字符串的值必须使用单引号''引上
d.更新数据
update 表名 set 字段1 = 字段1的值, 字段2 = 字段2的值, ...;
例:update t_student set name = 'Frank', age = 25;
// 上面语句是将表t_student中所有记录的字段name的值改为"Frank",所有记录的字段age的值改为25
e.删除数据
delete from 表名;
例:delete from t_student;
// 上面语句是将表t_student中的所有记录删除
d.条件语句
where 指定字段 = 指定值;
where 指定字段 is 指定值;
where 指定字段 != 指定值;
where 指定字段 is not 指定值;
where 指定字段 > 指定值;
where 指定字段1 = 指定值1 and 指定字段2 > 指定值2;
where 指定字段1 = 指定值1 or 指定字段2 = 指定值2;
e.查找语句
select 字段1, 字段2, ... from 表名; // 查询指定字段
select * from 表名; // 查询所有字段
f.排序
select * from 表名 order by 字段; // 查询结果按照指定字段升序排列(默认)
select * from 表名 order by 字段 desc; // 查询结果按照指定字段降序排列
select * from 表名 order by 字段 asc; // 查询结果按照指定字段升序排列
select * from 表名 order by 字段1 desc, 字段2 asc; // 查询结果按照字段1降序排列,按照字段2升序排列
g.使用limit精准控制查询结果的数量
select * from 表名 limit 数值; // 指定获取查询结果的前几条数据
select * from 表名 limit 数值1, 数值2; // 指定跳过前面几条(数值1)数据,然后取指定条(数值2)数据
h.普通字段约束
创表时,可以给一些特定的字段设置一些约束条件,常见的有:
not null:规定字段值不能为null
unique:规定字段的值必须唯一
default:指定字段的默认值
例:create table t_student (id integer, name text not null unique, age integer not null default 1);
// name字段:不能为null,并且唯一
// age字段:不能为null,默认值为1
i.主键约束
主键用来唯一标识某一条记录。主键可以是一个字段或多个字段
只要声明为primary key ,就说明是一个主键字段
主键字段默认就包含了not null和unique两个约束
主键自动增长,应该增加autoincrement
例:create table t_student (id integer primary key autoincrement, name text, age integer);
// 自动增长的主键
j.外键约束
外键约束可以用来建立表与表之间的联系
例:create table t_student (id integer primary key autoincrement, name text, class_id integer, constraint fk_student_class foreign key (class_id) references t_class (id));
// t_student表中的外键:fk_t_student_class_id_t_class_id
// 作用:用t_student表中的class_id字段引用t_class表的id字段
4.FMDB
1)什么是FMDB
a.FMDB是iOS平台的SQLite数据库框架
b.FMDB以OC的方式封装了SQLite的C语言API
2)FMDB的优点
a.使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码
b.对比苹果自带的CoreData框架,更加轻量级和灵活
c.提供了多线程安全的数据库操作方法,有效地防止数据混乱
3)FMDB的核心类
a.FMDatabase:一个FMDatabase对象就代表一个单独的SQLite数据库。用来执行SQL语句
b.FMResultSet:使用FMDatabase执行查询后的结果集
c.FMDdatabaseQueue:用于多线程中执行多个查询或更新,它是线程安全的
4)打开数据库(根据文件路径)
FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
if (![db open]) {
NSLog(@"数据库打开失败!");
}
// 文件路径有三种情况:
a.文件路径具体:如果不存在该数据库文件会自动创建
b.文件路径为空字符串@"":会在临时目录下创建一个空的数据库;当FMDatabase连接关闭时,数据库文件也被删除
c.文件路径为nil:会创建一个内存中临时数据库;当FMDatabase连接关闭时,数据库会被销毁
5)FMDB的用法
a.在项目中导入系统框架libsqlite3.dylib
b.将第三方库FMDB拖入到项目中
6)FMDB管理工具类
1 // FMDBManager.h文件 2 #import <Foundation/Foundation.h> 3 #import "FMDatabaseQueue.h" 4 #import "FMDatabaseAdditions.h" 5 #import <objc/runtime.h> 6 7 @interface FMDBManager : NSObject 8 9 10 // 工具类特点: 11 // 1.根据实体对象创表,每张表的主键为实体类的首个属性名,主键为自增NSInteger类型 12 // 2.删除和插入可以灵活地传入实体对应的任何一个属性作为依据字段,根据该字段来判断删除的记录是否存在和判断是插入还是更新操作 13 // 3.查找方法可以将一个字符串作为参数传递,该字符串为条件查找的条件语句;方法内部会根据该字符串是否为nil,来判断是条件查询还是全部查询 14 15 // 工具类的要求: 16 // 1.实体对应的首字段取名最好为“idNum” 17 // 2.实体所有属性最好都是NSString类型 18 19 #pragma mark - 数据库管理类对象 20 + (FMDBManager *)sharedInstace; 21 22 #pragma mark - 创建表 23 - (void)creatTable:(id)model; 24 25 #pragma mark - 数据库更新或插入数据 26 - (void)insertAndUpdateModelToDatabase:(id)model selectColumn:(NSString *)selectColumn; 27 28 #pragma mark - 删除元素 29 - (void)deleteModelInDatabase:(id)model selectColumn:(NSString *)selectColumn; 30 31 #pragma mark - 全部查找 32 - (NSArray *)selectModelArrayInDatabase:(NSString *)className factorString:(NSString *)factorString; 33 34 @end 35 36 37 // FMDBManager.m文件 38 #import "FMDBManager.h" 39 40 // 通过实体获取类名 41 #define KCLASS_NAME(model) NSStringFromClass([model class]) 42 // 通过实体获取属性数组 43 #define KMODEL_PROPERTYS [self getAllProperties:model] 44 // 通过实体获取属性数组的数目 45 #define KMODEL_PROPERTYS_COUNT [[self getAllProperties:model] count] 46 47 @implementation FMDBManager 48 { 49 FMDatabaseQueue *_dbQueue; // 数据库操作队列 50 } 51 52 #pragma mark - 数据库管理类对象 53 + (FMDBManager *)sharedInstace 54 { 55 static dispatch_once_t onceToken; 56 static FMDBManager *manager = nil; 57 dispatch_once(&onceToken, ^{ 58 manager = [[FMDBManager alloc] init]; 59 }); 60 return manager; 61 } 62 63 #pragma mark - 获取沙盒路径 64 - (NSString *)getDBFilePath 65 { 66 NSString *dbFilePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject stringByAppendingPathComponent:@"db.sqlite"]; 67 NSLog(@"%@", dbFilePath); 68 return dbFilePath; 69 } 70 71 #pragma mark - 创建数据库操作队列 72 // 一个数据库文件对应一个数据库操作队列 73 - (void)creatDBQueue 74 { 75 // 创建数据库操作队列的同时还会判断数据库是否存在,如果不存在,系统自动创建数据库 76 _dbQueue = [[FMDatabaseQueue alloc] initWithPath:[self getDBFilePath]]; 77 } 78 79 #pragma mark - 获取实体对象的属性数组 80 // 参数:model 实体对象 81 // 返回值:实体对象属性数组 82 - (NSArray *)getAllProperties:(id)model 83 { 84 u_int count; 85 objc_property_t *properties = class_copyPropertyList([model class], &count); 86 // 定义一个可变的属性数组 87 NSMutableArray *propertiesArray = [NSMutableArray array]; 88 for (int i = 0; i < count ; i++) 89 { 90 const char* propertyName = property_getName(properties[i]); 91 [propertiesArray addObject: [NSString stringWithUTF8String: propertyName]]; 92 } 93 free(properties); 94 return propertiesArray; 95 } 96 97 #pragma mark - 创表 98 // 参数1:model 实体对象 99 - (void)creatTable:(id)model 100 { 101 // 判断数据库操作队列是否存在 102 if (!_dbQueue) { 103 // 数据库操作队列不存,就得创建 104 [self creatDBQueue]; 105 } 106 107 // 使用数据库操作队列操作数据库 108 [_dbQueue inDatabase:^(FMDatabase *db) { 109 // 打开数据库 110 if (![db open]) { 111 // 数据库打开失败 112 NSLog(@"数据库打开失败"); 113 return; 114 } 115 116 // 为数据设置缓存,提高查询效率 117 [db setShouldCacheStatements:YES]; 118 119 // 判断数据库中是否已经存在该实体对应的表 120 // 不存在该实体对应的表,才创建表 121 if (![db tableExists:KCLASS_NAME(model)]) { 122 // 创表语句的第一部分 123 // %@ integer primary key autoincrement(设置主键为实体model的首字段,integer类型,自增) 124 // 主键 125 NSString *primaryKey = KMODEL_PROPERTYS[0]; 126 NSString *creatTableOneStr = [NSString stringWithFormat:@"create table %@(%@ integer primary key autoincrement", KCLASS_NAME(model), primaryKey]; 127 128 // 创表语句的第二部分 129 NSMutableString *createTableTwoStr = [NSMutableString string]; 130 // 遍历实体对象的属性数组 131 // 直接跳过实体的首字段 132 for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) { 133 // 实体对应的字段都是以文本字符串类型存储到数据库表中 134 NSString *popertyName = KMODEL_PROPERTYS[i]; 135 [createTableTwoStr appendFormat:@", %@ text", popertyName]; 136 } 137 138 // 创建表总语句 139 NSString *createTableStr = [NSString stringWithFormat:@"%@%@)", creatTableOneStr, createTableTwoStr]; 140 141 // 创表方法 142 if ([db executeUpdate:createTableStr]) { 143 NSLog(@"创表成功"); 144 } 145 } 146 147 // 关闭数据库 148 [db close]; 149 }]; 150 } 151 152 #pragma mark - 数据库更新或插入数据 153 // 参数1:model 实体对象 154 // 参数2:selectColumn 依据字段 155 - (void)insertAndUpdateModelToDatabase:(id)model selectColumn:(NSString *)selectColumn 156 { 157 // 判断数据库操作队列是否存在 158 if (!_dbQueue) { 159 // 数据库操作队列不存,就得创建 160 [self creatDBQueue]; 161 } 162 163 // 使用数据库操作队列操作数据库 164 [_dbQueue inDatabase:^(FMDatabase *db) { 165 // 打开数据库 166 if (![db open]) { 167 // 数据库打开失败 168 NSLog(@"数据库打开失败"); 169 return; 170 } 171 172 // 为数据设置缓存,提高查询效率 173 [db setShouldCacheStatements:YES]; 174 175 // 判断是否存在该实体对象所对应的表 176 if (![db tableExists:KCLASS_NAME(model)]) { 177 // 不存在该实体对象所对应的表就去创表 178 [self creatTable:model]; 179 } 180 181 // 按照传进来的查找字段查找该条记录是否存在 182 NSString *selectStr = [NSString stringWithFormat:@"select * from %@ where %@ = ?", KCLASS_NAME(model), selectColumn]; 183 // 获取实体对象的首个属性对应的值 184 NSString *selectColumnValue = [model valueForKey:selectColumn]; 185 // 查找 186 FMResultSet *resultSet = [db executeQuery:selectStr, selectColumnValue]; 187 // 判断查找结果是否为空 188 // 如果有查找结果,对应实体对象就是更新操作 189 if ([resultSet next]) { 190 // 更新语句第一部分 191 NSString *updateOneStr = [NSString stringWithFormat:@"update %@ set ",KCLASS_NAME(model)]; 192 193 // 更新语句第二部分 194 NSMutableString *updateTwoStr = [NSMutableString string]; 195 // 遍历实体对象的属性数组 196 // 跳过实体对应的首字段 197 for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) { 198 // 获取实体属性字段 199 NSString *propertyName = KMODEL_PROPERTYS[i]; 200 if ([propertyName isEqualToString:selectColumn]) { 201 continue; 202 } 203 [updateTwoStr appendFormat:@"%@ = ?", propertyName]; 204 // 判断是否遍历到属性数组的最后一个 205 if (i != KMODEL_PROPERTYS_COUNT - 1) { 206 [updateTwoStr appendFormat:@", "]; 207 } 208 } 209 210 // 更新语句第三部分 211 NSString *updateThreeStr = [NSString stringWithFormat:@" where %@ = %@",selectColumn, selectColumnValue]; 212 213 // 更新总语句 214 NSString *updateStr = [NSString stringWithFormat:@"%@%@%@", updateOneStr, updateTwoStr, updateThreeStr]; 215 216 // 属性值数组 217 NSMutableArray *propertyValueArr = [NSMutableArray array]; 218 // 遍历属性数组 219 // 跳过实体对应的首字段 220 for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) { 221 NSString *propertyName = KMODEL_PROPERTYS[i]; 222 if ([propertyName isEqualToString:selectColumn]) { 223 continue; 224 } 225 NSString *propertyValue = [model valueForKey:propertyName]; 226 if (propertyValue == nil) { 227 propertyValue = @""; 228 } 229 [propertyValueArr addObject:propertyValue]; 230 } 231 232 // 调用更新方法 233 if([db executeUpdate:updateStr withArgumentsInArray:propertyValueArr]) 234 { 235 NSLog(@"数据更新成功"); 236 } 237 } else { 238 // 插入操作 239 240 // 插入语句第一部分 241 NSString *insertOneStr = [NSString stringWithFormat:@"insert into %@ (",KCLASS_NAME(model)]; 242 243 // 插入语句第二部分 244 NSMutableString *insertTwoStr =[NSMutableString string]; 245 // 跳过实体对应的首字段 246 for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) { 247 // 获取实体属性字段 248 NSString *propertyName = KMODEL_PROPERTYS[i]; 249 [insertTwoStr appendFormat:@"%@", propertyName]; 250 if (i != KMODEL_PROPERTYS_COUNT - 1) { 251 [insertTwoStr appendFormat:@", "]; 252 } 253 } 254 255 // 插入语句第三部分 256 NSString *insertThreeStr =[NSString stringWithFormat:@") values ("]; 257 258 // 插入语句第四部分 259 NSMutableString *insertFourStr =[NSMutableString string]; 260 // 跳过实体对应的首字段 261 for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) { 262 [insertFourStr appendFormat:@"?"]; 263 if (i != KMODEL_PROPERTYS_COUNT - 1) { 264 [insertFourStr appendFormat:@", "]; 265 } 266 } 267 268 // 插入整个语句 269 NSString *insertStr = [NSString stringWithFormat:@"%@%@%@%@)", insertOneStr, insertTwoStr, insertThreeStr, insertFourStr]; 270 271 // 属性值数组 272 NSMutableArray *propertyValueArr = [NSMutableArray array]; 273 // 遍历属性数组 274 // 跳过实体对应的首字段 275 for (int i = 1; i < KMODEL_PROPERTYS_COUNT; i++) { 276 // 获取实体属性字段 277 NSString *propertyName = KMODEL_PROPERTYS[i]; 278 // 通过实体的字段获取对应的值 279 NSString *propertyValue = [model valueForKey:propertyName]; 280 // 判断这个值是否为nil 281 if (propertyValue == nil) { 282 propertyValue = @""; 283 } 284 [propertyValueArr addObject:propertyValue]; 285 } 286 287 // 调用插入方法 288 if([db executeUpdate:insertStr withArgumentsInArray:propertyValueArr]) 289 { 290 NSLog(@"插入成功"); 291 } 292 293 // 关闭数据库 294 [db close]; 295 } 296 }]; 297 } 298 299 #pragma mark - 删除元素 300 // 参数1:model 实体对象 301 // 参数2:selectColumn 依据字段 302 - (void)deleteModelInDatabase:(id)model selectColumn:(NSString *)selectColumn 303 { 304 // 判断数据库操作队列是否存在 305 if (!_dbQueue) { 306 // 数据库操作队列不存,就得创建 307 [self creatDBQueue]; 308 } 309 310 // 使用数据库操作队列操作数据库 311 [_dbQueue inDatabase:^(FMDatabase *db) { 312 // 打开数据库 313 if (![db open]) { 314 // 数据库打开失败 315 NSLog(@"数据库打开失败"); 316 return; 317 } 318 319 // 为数据设置缓存,提高查询效率 320 [db setShouldCacheStatements:YES]; 321 322 // 判断是否存在该实体对象所对应的表 323 if (![db tableExists:KCLASS_NAME(model)]) { 324 // 不存在该实体对象所对应的表就去创表 325 [self creatTable:model]; 326 } 327 328 // 删除语句(根据实体首字段的首属性字段去删) 329 NSString *deleteStr = [NSString stringWithFormat:@"delete from %@ where %@ = ?", KCLASS_NAME(model), selectColumn]; 330 331 // 执行删除方法 332 // 实体字段对应的值 333 NSString *selectColumnValue = [model valueForKey:selectColumn]; 334 if([db executeUpdate:deleteStr, selectColumnValue]) 335 { 336 NSLog(@"删除成功"); 337 } 338 339 // 关闭数据库 340 [db close]; 341 342 }]; 343 } 344 345 #pragma mark - 查找 346 // 参数1:tableName 表名 347 // 参数2:factorString 条件查找语句 348 // 返回值:直接返回实体数组 349 - (NSArray *)selectModelArrayInDatabase:(NSString *)tableName factorString:(NSString *)factorString 350 { 351 // 判断数据库操作队列是否存在 352 if (!_dbQueue) { 353 // 数据库操作队列不存,就得创建 354 [self creatDBQueue]; 355 } 356 357 // 需要返回的实体数组(block里面需要改变其值,所以必须使用__block修饰) 358 __block NSMutableArray *modelArray = [NSMutableArray array]; 359 360 // 使用数据库操作队列操作数据库 361 [_dbQueue inDatabase:^(FMDatabase *db) { 362 // 打开数据库 363 if (![db open]) { 364 // 数据库打开失败 365 NSLog(@"数据库打开失败"); 366 return; 367 } 368 369 // 为数据设置缓存,提高查询效率 370 [db setShouldCacheStatements:YES]; 371 372 // 条件查询 373 NSString * selectStr = nil; 374 if (factorString != nil) { 375 selectStr = [NSString stringWithFormat:@"SELECT * FROM %@ %@",tableName, factorString]; 376 }else { 377 // 全局查询 378 selectStr = [NSString stringWithFormat:@"select * from %@",tableName]; 379 } 380 381 FMResultSet *resultSet = [db executeQuery:selectStr]; 382 while([resultSet next]) { 383 // 使用表名作为类名创建对应的类的对象 384 id model = [[NSClassFromString(tableName) alloc] init]; 385 for (int i = 0; i < KMODEL_PROPERTYS_COUNT; i++) { 386 // 值是从我们的数据表的Column字段取出来 387 // 获取表中字段 388 NSString *propertyName = KMODEL_PROPERTYS[i]; 389 NSString *propertyValue = [resultSet stringForColumn:propertyName]; 390 // 设置model对应属性的对应值 391 [model setValue:propertyValue forKey:propertyName]; 392 } 393 [modelArray addObject:model]; 394 } 395 }]; 396 return modelArray; 397 } 398 399 @end
7)事务
例:银行转账系统,张三和李四账户上都有1000元钱,张三现在给李四转账500元
update t_account set money = 500 where name = @"张三";
update t_account set money = 1500 where name = @"李四";
张三给李四转账500元,这一操作就会执行上面两条SQL语句,但是考虑到安全性,我们必须要求两条语句同生同死。这个时候我们可以将他放在同一个事务中
事务:把多条语句放到同一个事务中,就会同生共死(即使前面的SQL语句执行成功,而后面的SQL执行失败,系统也会回滚,使它们全部失败)
1 [_dbQueue inDatabase:^(FMDatabase *db) { 2 // 创建事务 3 [db beginTransaction]; 4 [db executeUpdate:@"insert into t_person (name, age) values (?, ?);", @"Frank", @22]; 5 [db executeUpdate:@"insert into t_person (name, age) values (?, ?);", @"Kin", @15]; 6 // 提交事务 7 [db commit]; 8 }];

浙公网安备 33010602011771号