持久化工具
Core Data框架提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite、二进制文件等数据库文件中,也能够将保存在数据库中的数据还原成OC对象.
CoreData结构模型
模型概念解释
托管对象上下文(managed object context): 应用程序和数据存储之间的一块缓冲区。这块缓冲区包含所有未被写入数据存储的托管对象。你可以查询(使用NSFetchRequest)、创建、删除对象缓冲区内的托管对象。查询、创建、删除对象等操作都会调用上下文的方法且在上下文中进行。在上下文没有保存之前,对数据的任何修改都只记录在暂存区中,不会影响磁盘上的数据。你可以创建多个上下文,但整个程序只能创建一个NSPersstentStoreCoordinator对象。相当于一个打开的记事本,记事本提供了一个编辑环境,可以在记事本中增更删改,但只有打开和保存时才与持久化存储发生联系。参与对数据进行各种操作的整个过程,它持有 Managed Object。我们通过它来监测 Managed Object。数据操作语言 (DML)
托管对象(managed object): 保存到数据存储中的一个对象。这在概念上类似于SQL中的一条记录, 并且通常也包含一些域,这些域对应于你想要保存的对象的属性。也可以理解为在运行时创建的对应实体的具体实例。模型对象的数据被持有在NSManagedObject对象中。每一个NSManagedObject对象都对应一个实体。
持久化存储协调器(persistent store coordinator): 处理到上下文和数据存储的连接,并且包含一些底层信息,像用到数据存储的名字和位置。这个类通常被托管对象上下文用到。负责从磁盘加载数据和将数据写入磁盘(从数据文件(xml, sqlite,二进制文件等)中读取数据生成 Managed Object,或保存 Managed Object 写入数据文件)。协调器可以处理多种格式的数据库文件。在这里,你将设置数据存储的名字和位置,以及数据存储的时机。持久化存储协调器有它自己内部对象值的缓存。如果context需要一个指定的对象,并且持久化存储协调器在缓存中已经有需要的值,那么,对象可以不通过store而被直接加到context。这很重要,因为访问store就意味着执行SQL代码,这比使用内存中存在的值要慢很多。数据库连接(DBC)
托管对象模型(managed object model): 包含每一个你想存储到数据存储中的托管对象(实体)的定义。用来描述程序的实体、其属性、关系的模型图。NSManagedObjectModel and the NSEntityDescription 类完整定义了Core Data模型应该/可以包含的内容。数据定义语言 (DDL)
持久化存储(persistent store): 这个是数据真正存储的地方,CodeData提供了四种存储的选择,PersistentStore本身并不是objc类,仅仅是数据存储。在数据库sqlite文件中,一张表中的一条数据(table row)就相当于代码中的一个NSManagedObject对象,他们之间的映射是通过NSManagedObjectModel对象完成的
实体(Entity): 对实体对象的描述,Entity理解为数据库中的一张表。每个实体可以包含Attributes,用来存储实体的信息。与实体对应的Object-C对象,则会用@property来描述这些属性。
Entity的组成:
attribute | relationship | fetched properties |
---|---|---|
表的属性,相当于表的列 | 描述Entity(表)之间的关系 | 类似于relationship,但是一种“宽松的关系”,依赖于fetch request |
基本术语
Core Data框架中5个基本的类:
NSManagedObjectContext | NSManagedObject | NSPersistentStoreCoordinator | NSManagedObjectModel | NSEntityDescription |
---|---|---|---|---|
内存操作MOs的区域 | 运行时创建的实体的具体实例 | 联接MOC和底层数据库的中间件,依据ObjectModel的描述 | 数据模型,用来描述程序的实体、其属性、关系的模型图 | Entity的描述 |
CoreData基本操作
建立DataModel、Entity和NSManagedObject subclass
建立数据模型:1.CMD+N -> iOS -> Core Data -> Data Model -> 点击Next 2.设置数据模型名称、存储位置等,如步骤5、6所示,点击Create,即可生成数据模型包xxx.xcdatamodeld,包中默认含有一个与数据模型包同名的数据模型xxx.xcdatamodel,还可以添加其他的数据模型。
数据模型包:xxx.xcdatamodeld文件是一个包(package),该包里面包含了我们的数据模型.xcdatamodel(s)
在数据模型中建立Entity:
- 选择xxx.xcdatamodeld,点击Add Entity,如图步骤1、2所示。
- 设置Entity的名字,如:MTWCalcultateResult。
- 设置Entity的属性Attributes,点击Attributes中的“+”,添加属性,并根据需要设置属性类型(Type),和可以设置属性的约束规则。如步骤4、5、6所示。属性类型包含常用的类型,如:Double,String,Date等,如果不设置属性类型,Xcode会报错。
- 如果有多个Entity,如Book,Author等,还可以设置Entity之间的关系:Relationships,Relationships包括to-one、to-many(一对一、一对多,多对多)关系。
建立与Entity对应的OC中的NSManagedObject子类:
- CMD+N -> iOS -> Core Data -> NSManagedObject subclass
- 选择对应的Data Model(数据模型)
- 选择数据模型中与要生成NSManagedObject子类对应的Entity。
- 设置数据模型名称、存储位置等。
创建完毕后,生成的NSManagedObject子类如图所示:注意到由Entity生成的NSManagedObject subclass中的@property标记为@dynamic,表明CoreData在运行时动态生成property的getter和setter方法。CoreData中MO的@property特殊性如下苹果文档所示:
If you do not provide custom instance variables, you retrieve property values from and save values into the internal store using primitive accessor methods.
For example, given an entity with an attribute firstName, Core Data automatically generates firstName, setFirstName:, primitiveFirstName, and setPrimitiveFirstName:. Core Data does this even for entities represented by NSManagedObject. To suppress compiler warnings when you invoke these methods, you should use the Objective-C 2.0 declared properties feature, as described in Declaration.
如果Entity中的内容更改了,一定要重新生成并覆盖原有的NSManagedObject subclass,并且将沙盒中的原有的数据库文件删除,否则会因为数据不一致产生错误。
CoreData在OC中的基本操作
本文中使用单例来对CoreData进行操作,基本操作包括:打开(创建)数据库,增加、删除、查询数据等。
- .h(头)文件中的内容
@interface MTWCoreDataTool : NSObject
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
singleton_h(Instance)
- (void)addCalculateResult:(NSString *)result expression:(NSString *)expression
timeStamp:(NSString *)timeStamp calculatorType:(NSString *)calcluatorType;
@end
.m(实现)文件中的内容
打开数据库
- (void)openDatabase
{
// 1. 实例化数据模型(将所有定义的模型都加载进来)
self.managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
// 2. 实例化持久化存储调度,要建立起桥梁,需要模型
self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
// 3. 添加一个持久化的数据库到存储调度
// 3.1 建立数据库保存在沙盒的URL
NSArray *docs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [docs[0] stringByAppendingPathComponent:CalculateDatabase];
NSLog(@"path: %@",path);
NSURL *url = [NSURL fileURLWithPath:path];
// 3.2 打开或者新建数据库文件
// 如果文件不存在,则新建之后打开
// 否者直接打开数据库
NSError *error = nil;
[self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
if (error) {
NSLog(@"打开数据库出错 - %@", error.localizedDescription);
} else {
NSLog(@"打开数据库成功!");
self.managedObjectContext = [[NSManagedObjectContext alloc] init];
self.managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
}
}
- 向数据库中添加一条记录,这里向数据库中添加计算结果的记录。
- (void)addCalculateResult:(NSString *)result expression:(NSString *)expression
timeStamp:(NSString *)timeStamp calculatorType:(NSString *)calcluatorType
{
// 1. 实例化并让context“准备”将一条计算结果记录增加到数据库
MTWCalculateResult *calculateResult = [NSEntityDescription insertNewObjectForEntityForName:@"MTWCalculateResult" inManagedObjectContext:self.managedObjectContext];
// 2. 设置计算结果信息
calculateResult.result = result;
calculateResult.expression = expression;
calculateResult.timeStamp = timeStamp;
calculateResult.calculatorType = calcluatorType;
// 3. 保存(让context保存当前的修改)
if ([self.managedObjectContext save:nil]) {
NSLog(@"新增成功");
} else {
NSLog(@"新增失败");
}
}
- 从数据库中删除一条记录
- (void)removeCalculateResult:(MTWCalculateResult *)result
{
// 删除一条记录
[self.managedObjectContext deleteObject:result];
// 2. 通知self.managedObjectContext保存数据
if ([self.managedObjectContext save:nil]) {
NSLog(@"删除成功");
} else {
NSLog(@"删除失败");
}
}
- 查询数据库中符合条件的记录,用到了谓词 NSPredicate
- (NSArray *)allCalculateResult
{
// 1. 实例化一个查询(Fetch)请求
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:CalculateDatabase];
// 2. 设置谓词条件
// request.predicate = [NSPredicate predicateWithFormat:@"result CONTAINS 'error'"];
// 3. 让self.managedObjectContext执行查询数据
NSArray *resultsArray = [self.managedObjectContext executeFetchRequest:request error:nil];
return resultsArray;
}