CoreData 从入门到精通(二) 数据的增删改查
在上篇博客中,讲了数据模型和 CoreData 栈的创建,那下一步就是对数据的操作了。和数据库一样,CoreData 里的操作也无非是增删改查。下面我们将逐步讲解在 CoreData 中进行增删改查的方式。
基本的增删改查
插入条目
先来看一下插入条目的方式,在插入之前,我们需要先创建要插入的数据, 使用 NSEntityDesctiption
类的 + (__kindof NSManagedObject *)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context;
方法来创建一个新的 NSManagedObject
对象,入参分别是 entityName
和 managedObjectContext
,entityName
也就是实体类的名字,例如,我要插入一条新的 Student
字段,entityName
就是 @"Student";
context
是 NSManagedObjectContext
对象,新增的实体类对象会添加到对应的 context
上下文对象中。这个方法返回的是一个 NSManagedObject
实例,可以根据具体情况转换成相应的子类:
Student *student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.context];
student.studentName = @"小明";
student.studentId = 1;
student.studentAge = 20;
NSError *error;
[self.context save:&error];
调用 save
方法时,可以传入一个 NSError
的指针,如果数据保存出错的话,错误信息会保存到 error
里,这也是 Objective-C
里通常处理错误的方式。
查询条目
从数据库中查询数据,会用到三个类:NSFetchRequest
,NSPredicate
,NSSortDescriptor
,分别说一下这三个类的作用:
- NSFetchRequest — fetchRequest 代表了一条查询请求,相当于 SQL 中的 SELECT 语句
- NSPredicate — predicate 翻译过来是谓词的意思,它可以指定一些查询条件,相当于 SQL 中的 WHERE 子句,有关 NSPredicate 的用法,可以看我之前写过的一篇文章:使用 NSPredicate 进行数据库查询
- NSSortDescriptor — sortDescriptor 是用来指定排序规则的,相当于 SQL 中的 ORDER BY 子句
在NSFetchRequest
中有两个属性 predicate
、sortDescriptors
,就是用来指定查询的限制条件的。其中 sortDescriptors
是一个 NSSortDescriptor
的数组,也就是可以给一个查询指定多个排序规则,这些排序规则的优先级就是它们在数组中的位置,数组前面的优先级会比后面的高。除此之外,NSFetchRequest
还有下面这些属性
- fetchLimit — 指定结果集中数据的最大条目数,相当于 SQL 中的 LIMIT 子句
- fetchOffset — 指定查询的偏移量,默认为 0
- fetchBatchSize — 指定批处理查询的大小,设置了这个属性后,查询的结果集会分批返回
- entityName/entity — 指定查询的数据表,相当于 SQL 中的 FROM 语句
- propertiesToGroupBy — 指定分组规则,相当于 SQL 中的 GROUP BY 子句
- propertiesToFetch — 指定要查询的字段,默认会查询全部字段
配置好 NSFetchRequest
对象后,需要调用 NSManagedObjectContext
的 - (NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error;
来执行查询,返回的数组就是查询出的结果集。
Xcode 中预置了用来创建 fetchRequest 的code snippet,键入fetch
,就会出现相应的提示:
创建出来的代码是这样子的:
基本上常用到的代码都在这里了。当然我们也可以自己来手写出来,下面就是一个最简单的查询语句:
NSFetchRequest *fetchRequest = [Student fetchRequest]; // 自动创建的 NSManagedObject 子类里会生成相应的 fetchRequest 方法
// 也可以使用这种方式:[NSFetchRequest fetchRequestWithEntityName:@"Student"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"studentAge > %@", @(20)];
NSArray<NSSortDescriptor *> *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"studentName" ascending:YES]];
fetchRequest.sortDescriptors = sortDescriptors;
//
NSArray<Student *> *students = [self.context executeFetchRequest:fetchRequest error:nil];
删除条目
简单的删除条目还是比较简单的,在上一步里查询出来后,只需调用 NSManagedObjectContext
的 - (void)deleteObject:(NSManagedObject *)object;
方法来删除一个条目。例如,将上面查询出来的 students
全部删除,可以这么写:
for (Student *student in students) {
[self.context deleteObject:student];
}
[self.context save:nil]; // 最后不要忘了调用 save 使操作生效。
更新条目
还是在查询出的 students
数组的基础上,如果要更新里面的字段,可以遍历这个数组,依次修改数组里元素的字段:
for (Student *student in students) {
student.studentName = @"newName";
}
[self.context save:nil];
增删改查进阶
批量插入
简单的批量插入和插入单条数据一样,只是在所有的数据都插入只有才调用 [context save];
来保存。下面是简单的示例代码:
for (NSUInteger i = 0; i < 1000; i++) {
Student *newStudent = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.context];
int16_t stuId = arc4random_uniform(9999);
newStudent.studentName = [NSString stringWithFormat:@"student-%d", stuId];
newStudent.studentId = stuId;
newStudent.studentAge = arc4random_uniform(10) + 10;
}
[self.context save:nil];
批量更新
这里讲的批量更新方式,用到的是集合类型中的 KVC 特性。这是什么呢?就是在 NSArray
这样的集合类型里,可以调用它的 [setValue: forKeyPath:]
方法来更新这个数组中所有元素所对应的 keypath。例如想要将上面查询出来的 students
数组里所有元素的 studentName
属性都修改成 @"anotherName"
,就可以这么来写:
[students setValue:@"anotherName" forKeyPath:@"studentName"];
除了这种批量更新的方式,还有下面将要讲的 NSBatchUpdateRequest
也可以进行批量更新,不妨接着往下看。
NSBatchUpdateRequest 批量更新
NSBatchUpdateRequest
是在 iOS 8, macOS 10.10 之后新添加的 API,它是专门用来进行批量更新的。因为用上面那种方式批量更新的话,会存在一个问题,就是更新前需要将要更新的数据,查询出来,加载到内存中;这在数据量非常大的时候,假如说要更新十万条数据,就比较麻烦了,因为对于手机这种内存比较小的设备,直接加载这么多数据到内存里显然是不可能的。解决办法就是每次只查询出读取一小部分数据到内存中,然后对其进行更新,更新完之后,再更新下一批,就这样分批来处理。但这显然不是高效的解决方案。
于是就有了 NSBatchUpdateRequest
这个 API。它的工作原理是不加载到内存里,而是直接对本地数据库中数据进行更新。这就避免了内存不足的问题;但同时,由于是直接更新数据库,所以内存中的 NSManagedObjectContext
不会知道数据库的变化,解决办法是调用 NSManagedObjectContext
的 + (void)mergeChangesFromRemoteContextSave:(NSDictionary*)changeNotificationData intoContexts:(NSArray<NSManagedObjectContext*> *)contexts;
方法来告诉 context,有哪些数据更新了。
下面来看一下 NSBatchUpdateRequest
的用法。
创建
// 根据 entity 创建
NSBatchUpdateRequest *updateRequest = [[NSBatchRequest alloc] initWithEntity:[Student entity]];
// 根据 entityName 创建
NSBatchUpdateRequest *updateRequest = [[NSBatchUpdateRequest alloc] initWithEntityName:@"Student"];predicate
NSBatchUpdateRequest
的predicate
用来指定更新条件,例如这里指定更新 studentAge 等于 20 的学生updateRequest.predicate = [NSPredicate predicateWithFormat:@"studentAge == %@", @(20)];
propertiesToUpdate
propertiesToUpdate
属性是一个字典,用它来指定需要更新的字段,字典里的 key 就是要更新的字段名,value 就是要设置的新值。因为 Objective-C 字典里只能存储对象类型,所以如果字段基本数据类型的的话,需要转换成NSNumber
对象。updateRequest.propertiesToUpdate = @{@"studentName" : @"anotherName"};
resultType
resultType
属性是NSBatchUpdateRequestResultType
类型的枚举,用来指定返回的数据类型。这个枚举有三个成员:NSStatusOnlyResultType
— 返回 BOOL 结果,表示更新是否执行成功NSUpdatedObjectIDsResultType
— 返回更新成功的对象的 ID,是 NSArray\<NSManagedObjectID *\> * 类型。NSUpdatedObjectsCountResultType
— 返回更新成功数据的总数,是数字类型
一般我们将其指定为
NSUpdatedObjectIDsResultType
updateRequest.resultType = NSUpdatedObjectIDsResultType;
executeRequest
配置完
NSBatchUpdateRequest
对象后,就可以通过 context 的- (nullable __kindof NSPersistentStoreResult *)executeRequest:(NSPersistentStoreRequest*)request error:(NSError **)error;
方法来执行批量更新了:NSBatchUpdateResult *updateResult = [self.context executeRequest:updateRequest error:&error];
NSArray<NSManagedObjectID *> *updatedObjectIDs = updateResult.result;executeRequest
方法返回的是NSBatchUpdateResult
对象,里面有一个id result
属性,它的具体类型就是前面通过枚举成员指定的类型。mergeChanges
底层数据更新之后,现在要通知内存中的 context 了,调用 context 的
mergeChanges
方法,第一个参数是个字典,指定要合并的数据类型(是更新还是删除、插入等);第二个参数就是 context 数组,指定要合并到哪些 context 中去。NSDictionary *updatedDict = @{NSUpdatedObjectsKey : updatedObjectIDs};
[NSManagedObjectContext mergeChangesFromRemoteContextSave:updatedDict intoContexts:@[self.context]];
到这里,批量更新的操作就讲完了,下面来看 NSBatchDeleteRequest
批量删除。
NSBatchDeleteRequest 批量删除
NSBatchDeleteRequest
的用法和 NSBatchUpdateRequest
很相似,不同的是 NSBatchDeleteRequest
需要指定 fetchRequest
属性来进行删除;而且它是 iOS 9 才添加进来的,和 NSBatchUpdateRequest
的适用范围不一样,下面看一下示例代码:
NSFetchRequest *deleteFetch = [Student fetchRequest];
deleteFetch.predicate = [NSPredicate predicateWithFormat:@"studentAge == %@", @(20)];
NSBatchDeleteRequest *deleteRequest = [[NSBatchDeleteRequest alloc] initWithFetchRequest:deleteFetch];
deleteRequest.resultType = NSBatchDeleteResultTypeObjectIDs;
NSBatchDeleteResult *deleteResult = [self.context executeRequest:deleteRequest error:nil];
NSArray<NSManagedObjectID *> *deletedObjectIDs = deleteResult.result;
NSDictionary *deletedDict = @{NSDeletedObjectsKey : deletedObjectIDs};
[NSManagedObjectContext mergeChangesFromRemoteContextSave:deletedDict intoContexts:@[self.context]];
好了,CoreData 中的增删改查就讲完了,下篇文章将会介绍 CoreData Model 中的 relationships 实现多表关联的用法。
CoreData 从入门到精通(二) 数据的增删改查的更多相关文章
- SQLAlchemy(二):SQLAlchemy对数据的增删改查操作、属性常用数据类型详解
SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 目录 SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 1.用se ...
- Mybatis学习总结(二)—使用接口实现数据的增删改查
在这一篇中,让我们使用接口来实现一个用户数据的增删改查. 完成后的项目结构如下图所示: 在这里,person代表了一个用户的实体类.在该类中,描述了相关的信息,包括id.name.age.id_num ...
- 基于renren-fast的快速入门项目实战(实现报表增删改查)
基于renren-fast的快速入门项目实战(实现报表增删改查) 说明:renren-fast是一个开源的基于springboot的前后端分离手脚架,当前版本是3.0 官方开发文档需付费,对于新手而言 ...
- Hibernate3回顾-5-简单介绍Hibernate session对数据的增删改查
5. Hibernate对数据的增删改查 5.1Hibernate加载数据 两种:get().load() 一. Session.get(Class arg0, Serializable arg1)方 ...
- SQL Server -- 回忆笔记(二):增删改查,修改表结构,约束,关键字使用,函数,多表联合查询
SQL Server知识点回忆篇(二):增删改查,修改表结构,约束,关键字使用,函数,多表联合查询 1. insert 如果sql server设置的排序规则不是简体中文,必须在简体中文字符串前加N, ...
- mysql学习笔记一 —— 数据的增删改查
1.连接mysql mysql 直接回车(是以root身份,密码空,登陆的是本机localhost) [root@www mysql]# mysql -uroot -p123 -S /var/lib/ ...
- Ecmall二次开发-增删改查操作
Ecmall二次开发-增删改查操作 Model目录includes/models 自己添加需要的model class OrdercomplainModel extends BaseModel //类 ...
- Django 06 Django模型基础1(ORM简介、数据库连接配置、模型的创建与映射、数据的增删改查)
Django 06 Django模型基础1(ORM简介.数据库连接配置.模型的创建与映射.数据的增删改查) 一.ORM系统 #django模型映射关系 #模型类-----数据表 #类属性-----表字 ...
- Django框架之第二篇--app注册、静态文件配置、form表单提交、pycharm连接数据库、django使用mysql数据库、表字段的增删改查、表数据的增删改查
本节知识点大致为:静态文件配置.form表单提交数据后端如何获取.request方法.pycharm连接数据库,django使用mysql数据库.表字段的增删改查.表数据的增删改查 一.创建app,创 ...
- SQLAlchemy02 /SQLAlchemy对数据的增删改查操作、属性常用数据类型详解
SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 目录 SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 1.用se ...
随机推荐
- linux + nginx 的配置优化
linux 关于TCP/IP 的优化配置 配置文件/etc/sysctl.conf 修改完文件生效的命令 /sbin/sysctl -p 如下是总结的配置内容及说明 net.ipv4.con ...
- Redis常用的命令
常规命令查询地址: http://redisdoc.com/ 如下图:
- 几种AutoLayout自动布局所经常使用的布局约束类型
width表示约束ui控件的固定宽度 height表示约束ui控件的固定高度 Leading Space to Superview 与父视图的左边界线保持固定距离 Trailing Space to ...
- 请求测试——Fiddler2工具(可以测试POST和Get)
使用参考:http://jingyan.baidu.com/article/dca1fa6fa07000f1a44052f6.html 发送POST请求的时候,需要填写发送类型: 发送JSON格式填写 ...
- 微信小程序实现图片双滑缩放大小
在做小程序开发的过程中,后端传来一张图片地图,需要实现双手指滑动,使图片缩放,最终得出了一下代码: js : Page({ data: { touch: { distance: , scale: , ...
- Java并发--线程安全策略
1 不可变对象 用不可变对象保证线程安全,是相当于不让线程并发,逃避了并发. 不可变对象就是指一个类的实例化对象不可变.比如String类的实例 主要方法有: 将类声明为final 将所有成员声明为 ...
- CentOS 7编译安装Python3.7.X
在CentO S上通过编译安装Python3.7,添加python3软链,并使用python3. 环境 安装环境如下: Windows 10 Pro VMWare Workstation 15 Pro ...
- 2、Attentive Group Recommendation----注意力集中的群组推荐
1.摘要: 采用attention和NCF结合解决群组偏好融合的问题. 贡献: 第一个使用神经网络学习融合策略的组推荐. 进一步整合用户-项目交互改进组推荐,减轻冷启动问题. 2.方法: 模型AGRE ...
- 相对URL:协议名跨域的一种处理方式
问题现象 当页面地址协议与页面内请求地址协议不一致(不都是https或不都是http)时,往往请求会被拦截.控制台提示: 原因 浏览器对于JavaScript的同源策略的限制,简言之就是我们常说的跨域 ...
- [luogu2151 SDOI2009] HH去散步 (矩阵快速幂)
传送门 题目描述 HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又因为HH ...