Использование Нескольких Постоянных Хранилищ В Основных Данных

Всем разработчикам iOS (и MAC OS X) известен такой системный фреймворк, как Core Data. Эта штука представляет собой довольно мощный ORM (по крайней мере, для мобильной платформы).

Изначально наше приложение использовало одну базу данных для всей информации, которую необходимо было хранить в приложении.

Но по мере расширения функционала стало понятно, что некоторые сущности логичнее разместить в разных базах данных, а то и в разных типах хранилищ (persistent store).

Не буду вдаваться в подробности, главное, что изначально монолитный NSSQLiteStore пришлось разделить на несколько.

В документации этот вопрос освещен очень плохо.

Сказано лишь, что можно использовать несколько NSPersistentStore в рамках одного NSPersistentStoreCoordinator, а тем более использовать их напрямую из одного NSManagedObjectContext. Выглядит фантастически.

Отчаявшись разобраться в проблеме, используя только документацию Apple, мне пришлось переключиться на Google. Побродив по сети, в основном в области Stack Overflow, я собрал много информации и после нескольких часов борьбы с XCode добился более-менее рабочего решения.

В ходе изучения вопроса выяснилось следующее: Действительно, один NSManagedObjectContext может собирать информацию из нескольких NSPersistentStores. Об этом есть подробная статья, в которой описан механизм подключения плагинов к приложению с Core Data. Вы можете найти это здесь: www.cimgf.com/2009/05/03/core-data-and-plug-ins Один NSManagedObjectContext может соответствовать только одной NSManagedObjectModel. Как указать, какие объекты и в каком хранилище следует хранить? Это делается с помощью имени конфигурации.

Вы можете создать набор конфигураций в одной NSManagedObjectModel, а можете взять несколько NSManagedObjectModel и объединить их в одну (при создании каждой все равно потребуется создать именованную конфигурацию).

  
  
  
   

-(NSManagedObjectModel*)managedObjectModel; { if(managedObjectModel)return managedObjectModel; NSBundle*myBundle =[NSBundle bundleForClass:[self class]]; NSArray*bundles =[NSArray arrayWithObject:myBundle]; managedObjectModel =[[NSManagedObjectModel mergedModelFromBundles:bundles] retain]; return managedObjectModel; }

Когда у нас есть подготовленная (объединенная или предварительно созданная) NSManagedObjectModel, мы можем добавить магазины в координатор с помощью функции

- (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType configuration:(NSString *)configuration URL:(NSURL *)storeURLoptions:(NSDictionary *)options error:(NSError **)error

используя имя конфигурации для каждого типа хранилища.

Как это:

NSPersistentStore * store = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:CONFIG_NAME URL:databaseURL options:options error:&error];

Файл хранения будет создан автоматически.

После того, как цель была достигнута, мне очень захотелось своими глазами увидеть хранилища, которые там создали Core Data. В частности, под лупой был рассмотрен NSSQLiteStore. Интересная вещь оказалась — какое бы имя конфигурации у файла ни было, в нем все равно создаются таблицы для всех сущностей из объектной модели.

Однако в файл записываются именно те сущности, которые указаны в конфигурации.

В остальном все волшебным образом работало; из одной NSManagedObjectModel сущности распихивали фактически в разные базы данных! Отдельно стоит сказать о подключении хранилищ.

Вы не можете создавать связи между сущностями из разных репозиториев.

Но вы можете использовать полученные свойства.

Обычно это более или менее четко описано в документе Apple по адресу Developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html#//apple_ref/doc/uid/TP40001857-SW5 Но это еще не все.

Поскольку старая версия приложения хранила кучу полезных данных в своей единственной базе данных, нам захотелось как-то перенести эту информацию в наши новые хранилища.

Для этого я набросал примерно такой метод в категории NSPersistentStoreCoordinator

- (BOOL)migratePersistentStore:(NSURL *)sourceStoreURL withType:(NSString *)sourceType to:(NSURL *)destinationStoreURL type:(NSString *)destinationType andAddWithConfiguration:(NSString *)configuration { NSError *error = nil; NSDictionary *sourceMetadata = [self.class metadataForPersistentStoreOfType:sourceType URL:sourceStoreURL error:&error]; if(sourceMetadata==nil) return NO; NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:sourceMetadata]; NSMigrationManager *migrationManager = [[[NSMigrationManager alloc] initWithSourceModel:sourceModel destinationModel:self.managedObjectModel] autorelease]; NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:nil forSourceModel:sourceModel destinationModel:self.managedObjectModel]; if(mappingModel==nil) return NO; BOOL result = [migrationManager migrateStoreFromURL:sourceStoreURL type:sourceType options:nil withMappingModel:mappingModel toDestinationURL:destinationStoreURL destinationType:destinationType destinationOptions:nilerror:&error]; if(result==NO) return NO; NSLog(@"Successful DB migration"); NSDictionary *options = [self.class migrationOptionsWithAutoMigration:NO]; NSPersistentStore * addedStore = [self addPersistentStoreWithType:destinationType configuration:configuration URL:destinationStoreURL options:options error:&error]; return addedStore!= nil; }

К сожалению, разложить одну базу данных на несколько таким способом не удалось.

Первый вызов метода работает на ура.

Однако при втором вызове с другим именем конфигурации Core Data трагически сообщает мне: «Невозможно добавить одно и то же хранилище дважды».

В итоге я решил мигрировать только ту конфигурацию, где хранятся важные данные, и создать базы для всяких логов и кешей, которые можно будет пересобрать с нуля.

Теги: #objective-c #cocoa touch #основные данные #программирование #Разработка для iOS #objective-c

Вместе с данным постом часто просматривают: