增加和删除改查

日期:2019-09-14编辑作者:编程应用

一.文章概要

当前使用工具是XCode7.

这篇文章主要是写了对于基本数据类型的"增删改查"的操作,至于特殊类型比如UIImage,NSArray,NDDictionary我们在之后的几篇文章中讲到.

简单说明

coreData是苹果提供的数据本地化的解决方案中的一种,实质还是提供数据库去存储数据,只是在coreData中不需要程序员手动创建数据库文件,由coreData自动创建。
coreData的核心文件和类
1.模型文件:后缀是.xcdatamodeld的文件,相当于数据库文件(数据库文件代言人)
2.实体:相当于表
3.实体类:存储对象对应的类
4.上下文:关联模型文件和实际生成的数据库文件(对上下文的操作就相当于对数据库进行操作)
使用步骤
1.创建模型文件(已经自动创建完成,但是要在创建工程的时候勾选coreData的选项)

图片 1

勾选core data选项

2.创建实体(手动的在模型文件中添加一个实体,并且添加对应的属性)

图片 2

模型文件中添加实体

3.创建实体类(使用的时候直接使用类文件去操作属性,不用导入类别文件(7.0以上才有))

图片 3

创建实体类

4.生成一个上下文(上下文已经自动生成,只需要直接拿过来使用)
5.数据的增删改查(都是基于数据的查询)

二.前话

从平常的使用来说,FMDB和coreData是项目中最经常使用的两个数据库持久化方式.CoreData 首先它是一个 ORM框架。 SQLite 与 FMDB 其实都脱离不了关系型(relational)数据 而我们平时使用的编程语言却是面向对象的,都是对象型的。在 iOS 中,coreData 和 FMDB 关于这两者的区别,我认为主要是如下几个方面

coreData

优点映射之后 直接操作对象就能进行增删改查 更贴近 程序员的生活。我们不需要再写SQL语句,避免了 SQL语句的繁琐,比如说 SQL语句经常要写一大堆的包含 values 还要有对应个数的?与前边的()中的属性个数对应缺点 coreData没有 java 中的 ORM 框架快速.尤其是多表操作的时候,效率并不是很高. java中的框架(Hibernate:特点:可以把 SQL语句和面型对象的查询语句混合使用.举个例子:它可以把复杂的多表操作语句用一行查询语句来执行完毕)。而 coreData操作多表的时候,需要创建多个对应数量的Requset,description,谓词,筛选器。就单纯的批量插入数据的速度 相比较FMDB 稍微慢一点点

1.获取上下文

既然上面说到上下文是关联模型文件和实际生成的数据库文件,对上下文的操作就相当于是对数据库进行操作,那么这里的上下文就相当于是一个数据库操作者,那么我们就要获取到这个上下文。
首先先声明一个上下文

    // 声明上下文
    NSManagedObjectContext *_context;

    // 1.获取上下文(相当于打开数据库)
    // 拿到应用程序的delegate
    AppDelegate *delegate = [UIApplication sharedApplication].delegate;
    _context = delegate.managedObjectContext;

coreData还是对数据的操作,那就一定离不开数据的增删改查。

三.FMDB

如果你之前用过sqlSever数据库的话,继续往下看,当然你也可以直接跳过这一小节

首先,想必软件工程专业或者相关专业的同学在学校学的就是sqlSever,对应的数据库课程也是免不了的.那么sql语句是我们必须要学习的."简单查询""多表查询"等等这些都是我们要学习的.恭喜你,你已经有了一定学习FMDB的基础了.

再进一步说,你之前从事java或者安卓开发工作的时候,用到了sqlSever,那么再次恭喜你,你基本已经可以FMDB的那些基本功能了.

由此,我们可见,FMDB的使用方式和之前在其他语言中使用sqlSever数据库的时候,是非常相似的.之前我们在使用sqlSever提到的那些关键点在FMDB中,也有体现,比如"打开数据库,关闭数据库""手敲sql语句,执行sql语句"......所以,FMDB,对于之前上手还是很熟悉,很简单的.

你到现在还没有使用过FMDB. 那也不用太烦恼和害怕,数据库的基本格式都是固定的.既然是固定的,拷贝一段改改就可以了.

问:"什么时候使用coredata 什么时候使用FMDatabases?"

答: a. CoreData 在公司使用的比较少,用户的比较多的是FMDatabases。

b.数据存储的结构比较简单的时候,使用CoreData

c.CoreData 开发效率会高点,为什么?因为他是面向对象的,而且不用写sql语句. FMDatabases 数据结果比较复杂的时候,表与表之间的关联比较多的时候使用.

coreData 其实底层也是要写sql 语句的,coreData 帮我们把sql语句封装。

2.增加数据

1.插入单条数据

    // 1.创建需要插入的对象(可以像alloc出来的对象一样的使用,只是多了一些功能)
    /**
     *  @param NSString 实体名(相当于表名)
     *  @return 上下文(相当于数据库)
     */
    Student *stu = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:_context];

    // 2.给对象的属性进行赋值
    stu.name = @"wj";
    stu.age = @25;
    stu.sex = @"未知";

    // 3.保存数据库
    // 参数:错误信息
    // 返回值:执行结果
    BOOL ret = [_context save:nil];
    if (ret) {
        NSLog(@"保存成功");
    }
    else {
        NSLog(@"保存失败");
    }

这样就把一条数据插入到数据库中去了,打印下沙盒路径也可以去查找到数据的存在。
2.插入多条数据
插入多条数据可以使用一个for循环进行插入,简单粗暴。

    for (int i = 0; i < 10; ++i) {
        // 1.创建对象
        Student *stu2 = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:_context];

        // 2.给属性赋值
        stu2.name = [NSString stringWithFormat:@"name%d",i];
        stu2.age = [NSNumber numberWithInteger:(arc4random() % 15 + 15)];
        stu2.sex = arc4random() % 2 == 0 ? @"男" : @"女";
        // 放到循环里的,执行的时候就一条一条的存入到数据库中
        //[_context save:nil];
    }
    // 3.保存数据库
    // 同时插入多条的数据时时运用的多线程,同时执行
    [_context save:nil];

从上面可以知道,数据的插入的会用到的方法为+ (__kindof NSManagedObject *)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context;
所以对数据插入后调用的方法为:- (BOOL)save:(NSError **)error;,表示对数据库进行保存操作。

四.你要的干货,利用coreData来持久化数据

图片 4描述图片 5描述

创建实体对象

图片 6描述 图片 7描述

创建实体对象的属性(属性名,属性类型和关联实体)

图片 8描述

问:什么是关联实体???

答:其实就是我们创建另外的一个实体.这里添加关联实体的意思就是,举个例子我们人类有个宠物狗,那么反过来宠物狗也有个主人是人类. 这里说白了就是两个对象之间的相互关联

温馨提示

图片 9描述图片 10描述图片 11描述图片 12描述图片 13描述

以下的代码是系统默认生成的,这里贴出是仅供对比查验使用,在文篇文章中没有其它的意义.

创建出的子类代码-Book+CoreDataProperties.h:

#import "Book.h"NS_ASSUME_NONNULL_BEGIN@interface Book (CoreDataProperties)@property (nullable, nonatomic, retain) NSNumber *bookID;@property (nullable, nonatomic, retain) NSString *bookName;@property (nullable, nonatomic, retain) Student *student;@endNS_ASSUME_NONNULL_END

创建出的子类代码-Book+CoreDataProperties.m:

#import "Book+CoreDataProperties.h"@implementation Book (CoreDataProperties)@dynamic bookID;@dynamic bookName;@dynamic student;@end

创建出的子类代码-Book.h:

#import <Foundation/Foundation.h>#import <CoreData/CoreData.h>@class Student;NS_ASSUME_NONNULL_BEGIN@interface Book : NSManagedObject// Insert code here to declare functionality of your managed object subclass@endNS_ASSUME_NONNULL_END#import "Book+CoreDataProperties.h"

创建出的子类代码-Book.m:

#import "Book.h"#import "Student.h"@implementation Book// Insert code here to add functionality to your managed object subclass@end

创建出的子类代码-Student+CoreDataProperties.h:

#import "Student.h"NS_ASSUME_NONNULL_BEGIN@interface Student (CoreDataProperties)@property (nullable, nonatomic, retain) NSNumber *id;@property (nullable, nonatomic, retain) NSString *name;@property (nullable, nonatomic, retain) Book *book;@endNS_ASSUME_NONNULL_END

创建出的子类代码-Student+CoreDataProperties.m:

#import "Student+CoreDataProperties.h"@implementation Student (CoreDataProperties)@dynamic id;@dynamic name;@dynamic book;@end

创建出的子类代码-Student.h:

#import <Foundation/Foundation.h>#import <CoreData/CoreData.h>@class Book;NS_ASSUME_NONNULL_BEGIN@interface Student : NSManagedObject// Insert code here to declare functionality of your managed object subclass@endNS_ASSUME_NONNULL_END#import "Student+CoreDataProperties.h"

创建出的子类代码-Student.m:

#import "Student.h"#import "Book.h"@implementation Student// Insert code here to add functionality to your managed object subclass@end

我们暂时不做封装,直接在UIViewController演示.

这里的上下文在OC中,类似我们coreAnimation中用到的图形上下文. OC中哪些有上下文?

在正式看下边的'增删改查'代码之前,我们需要也别注意的是:

在我们每次执行四个操作的时候,都首先要保证上下文存在

并且代码执行在'异步主队列中'

'增'部分:创建保存对象部分,不是直接new或者alloc-init

'查'部分:NSPredicate过滤条件

'改'部分:'查'部分:NSPredicate过滤条件

'删'部分:'查'部分:NSPredicate过滤条件

上边提到我们的演示文件类型是UIViewController,所以是在'viewDidLoad'方法中创建上下文,并且同时创建一个全局变量,保证上下文在我们执行'增删改查'的操作过程当中不会被销毁. 代码如下:

- viewDidLoad { [super viewDidLoad]; //创建上下文 [self createCoreDataContent];}

创建上下文的方法:

- createCoreDataContent{ //创建数据库文件的路径 // NSString *path = [NSHomeDirectory() stringByAppendingString:@"Doucments/ImortData"]; // NSFileManager *manager = [NSFileManager defaultManager]; // if (![manager fileExistsAtPath:path]) { // [manager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; // } // //documet目录下 NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [doc stringByAppendingPathComponent:@"/student.db"];//注意不是:stringByAppendingString NSURL *url = [NSURL fileURLWithPath:path]; NSLog(@"-----------------------------------"); NSLog(@"data : %@",path); //创建文件,并且打开数据库文件 NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil]; NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; //给存储器指定存储的类型 NSError *error; NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]; if (store == nil) { [NSException raise:@"添加数据库错误" format:@"%@",[error localizedDescription]]; } //创建图形上下文 NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; context.persistentStoreCoordinator = psc; self.managedContext = context;}

//插入数据- insertData{ NSLog; //创建模型数据模型 Student *student = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.managedContext]; student.name = @"张三2"; student.id = @; Book *book = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:self.managedContext]; book.bookID = @; book.bookName = @"<老人与海2>"; student.book = book; Student *student2 = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:self.managedContext]; student2.name = @"李四2"; student2.id = @; Book *book2 = [NSEntityDescription insertNewObjectForEntityForName:@"Book" inManagedObjectContext:self.managedContext]; book2.bookID = @; book2.bookName = @"<飞鸟集2>"; student2.book = book2; //保存,用 save 方法 NSError *error = nil; BOOL success = [self.managedContext save:&error]; if  { [NSException raise:@"访问数据库错误" format:@"%@",[error localizedDescription]]; }}

//读取数据库文件- readData{ NSLog; dispatch_async(dispatch_get_main_queue(), ^{ // 初始化一个查询请求 // NSFetchRequest *request = [[NSFetchRequest alloc] init]; // 设置要查询的实体 // request.entity = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:self.managedContext]; //以上代码简写成下边 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"]; // 设置排序 NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"id" ascending:NO]; request.sortDescriptors = [NSArray arrayWithObject:sort]; // 设置条件过滤(搜索name中包含字符串"zhang"的记录,注意:设置条件过滤时,数据库SQL语句中的%要用*来代替,所以%Itcast-1%应该写成*zhang*) // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*zhang*"]; // request.predicate = predicate; // 执行请求 NSError *error = nil; NSArray *objs = [self.managedContext executeFetchRequest:request error:&error]; if  { [NSException raise:@"查询错误" format:@"%@", [error localizedDescription]]; } NSLog(@"-----------------------------------"); // 遍历数据 int index = 0; for (NSManagedObject *obj in objs) { NSLog(@"%d---name=%@", index++,[obj valueForKey:@"name"]); } for (Student *stu in objs) { Book *book = stu.book; NSLog(@"%@---name=%@", stu.name,book.bookName); } });}

//更新数据- modifyData{ // 如果是想做更新操作:只要在更改了实体对象的属性后调用[context save:&error],就能将更改的数据同步到数据库 //先从数据库中取出所有的数据,然后从其中选出要修改的那个,进行修改,然后保存 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"]; //设置过滤条件 NSPredicate *pre = [NSPredicate predicateWithFormat:@"name = %@",@"李四2"]; request.predicate = pre; NSError *error = nil; NSArray *objs = [self.managedContext executeFetchRequest:request error:&error]; if  { [NSException raise:@"查询错误" format:@"%@", [error localizedDescription]]; } // 2.更新身高 for (Student *stu in objs) { stu.name = @"被修改的新名字"; } //保存,用 save 方法 BOOL success = [self.managedContext save:&error]; if  { [NSException raise:@"访问数据库错误" format:@"%@",[error localizedDescription]]; }}

//删除- removeData:sender{ NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Student"]; //查到到你要删除的数据库中的对象 NSPredicate *predic = [NSPredicate predicateWithFormat:@"name = %@",@"张三2"]; request.predicate = predic; //请求数据 NSArray *objs = [self.managedContext executeFetchRequest:request error:nil]; for (Student *stu in objs) { [self.managedContext deleteObject:stu]; } [self.managedContext save:nil];}

3.查询数据

1.创建查询对象

    // 1.创建查询对象
    // 参数:实体(相当于表名)
    NSFetchRequest *fetch_t = [NSFetchRequest fetchRequestWithEntityName:@"Student"];

这个name要和创建实体的名字一致。
2.直接查询

    // 1.直接查询(将表中所有的数据全部获取到)
    // 返回值:查询结果
    NSArray *array = [_context executeFetchRequest:fetch_t error:nil];
    // 遍历获取所有的对象
    for (Student *stu in array) {
        [stu show];
    }

3.按条件进行查询
按条件进行查询可以使用谓词进行查询,谓词相当于就是一个查询的条件而已。

    // 1.创建条件对象
    // 参数:查询条件
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age < 18"];
    NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"age = 18 and sex = '男'"];

    // 条件:age的值不固定
    NSPredicate *predicate3 = [NSPredicate predicateWithFormat:@"age < %d",25];

    // 条件:值和属性都不固定(属性不固定,用K占位)
    NSPredicate *predicate4 = [NSPredicate predicateWithFormat:@"%K < %d", @"age", 30];

    // 2.给查询对象添加条件
    [fetch_t setPredicate:predicate4];

    // 3.查询结果
    NSArray *result = [_context executeFetchRequest:fetch_t error:nil];

    // 遍历获取的所有对象
    for (Student *stu2 in result) {
        [stu2 show];
    }

4.查询结果排序

    // 1.创建一个排序对象
    // 参数1:排序的条件(按属性进行排序)
    // 参数2:是否升序
    NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES];

    //2.设置查询对象的排序属性
    fetch_t.sortDescriptors = @[sort];

    // 3.查询结果
    NSArray *sortAscend = [_context executeFetchRequest:fetch_t error:nil];

    for (Student *stu4 in sortAscend) {
        [stu4 show];
    }

到这里后上下文会引用一个方法就是- (nullable NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error;进行查询。
查询结果的显示是在Student这个类写了一个方法而已,

- (void)show {
    NSLog(@"%@ ~ %@ ~ %@",self.name, self.age, self.sex);
}

小结

为了方便地测试上边的代码,我们可以把各个方法在界面上对应.比如下图这样:

图片 14描述

到这里,对于coreData的基本操作"增删改查"的基本操作刚和代码就完成了.

具体的 Demo 代码可以在我的 GitHub 上找到 Demo地址

4.删除数据

想要删除数据,那么首先要得找到这条数据,所以我们要的使用查询数据库的手段,所以我们就得按照条件进行查询数据。
1.数据的查询

    // 1.按条件将需要删除的数据查询出来
    NSFetchRequest *fetch_t = [NSFetchRequest fetchRequestWithEntityName:@"Student"];

    NSPredicate *pre = [NSPredicate predicateWithFormat:@"age < 25"];
    fetch_t.predicate = pre;
    NSArray *array = [_context executeFetchRequest:fetch_t error:nil];

2.删除数据
按条件找到相应的数据了,就可以进行删除了。

    // 2.遍历查询结果,将结果中的对象删除
    for (Student *stu in array) {
        // 我觉得这个地方应该在判断下array到底有没有值,没有就直接返回,有值才去删除操作
        [_context deleteObject:stu];
    }

上面得到删除数据的方法为:- (void)deleteObject:(NSManagedObject *)object;

交流

图片 15

欢迎大家关注我的微博和我GitHub,我会不时分享和转发一些大牛的技术贴和开源项目.GitHub:

5.更新数据

其实说到底更新数据和删除数据没有多大的差别,在实现上来说,都需要按条件找到相应的数据,只是删除的话是把数据删除了,而更新是要把数据附上一个新的值而已,最后将数据库进行保存。

// 1.按条件将需要删除的数据查询出来
    NSFetchRequest *fetch_t = [NSFetchRequest fetchRequestWithEntityName:@"Student"];
    NSPredicate *pre = [NSPredicate predicateWithFormat:@"age < 25"];
    fetch_t.predicate = pre;
    NSArray *array = [_context executeFetchRequest:fetch_t error:nil];
    // 2.遍历查询结果,将结果中的对象更新
    for (Student *stu in array) {
        stu.age = @20;
    }
    // 3.保存数据库
    [_context save:nil];

coreData和sqlite3的比较

1.sqlite3是通过sql语句直接操作对象的属性;而coreData是直接对对象进行操作的。而两者都是直接或者间接对数据库文件进行操作的。
2.sqlite3使用的时候更加灵活,但是需要sql语句,这不能有一点的写错了,再加上Xcode对sql语句没有语法提示的。coreData不用sql语句编写就可以操作数据库的,但是对数据库的操作还是不够灵活(在runtime是不能使用coreData的,只能使用sqlite3或者是FMDB进行操作的)。
3.如果整个工程的数据结构比较简单的时候,尽量的去使用coreData进行数据本地化的处理。

本文由今晚最快开奖现场直播发布于编程应用,转载请注明出处:增加和删除改查

关键词:

面向公约开采笔记,Swift编制程序最佳体验之Ge

关于Swift代码风格,能让绝大部分人的眼前一亮,WTF?还能这样写?今天就带来一段Swift常见的Generic 比如 UIButton,U...

详细>>

利用Intent来开展App间的主导交互,系统深远学习

Intent是Android中存放一个操作的抽象描述的数据结构,可用于在不同的组件以及不同的app间进行传递,是消息的载体,有显...

详细>>

源码分析,Glide使用简介及流程分析

Glide 那篇小说将会为大家梳理一下Glide3.5版本的大旨流程,为了便利阅读,文章中的源码部分将轻松部分有关至极捕获...

详细>>

Android难题之——自定义View(上)

1、每个Activity包含一个Window对象(Window是abstract的,一般是由PhoneWindow实现)。2、PhoneWindow将DecorView设置为整个应用的...

详细>>