ページ

2010年5月17日月曜日

CoreData - リレーションシップ(1) シンプルなモデル

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

CoreData でリレーションシップ(関連)を扱う必要が出てきたのでこれから調査していく。

※今回 MacOSX10.5で検証。

まずはこんなモデルを作ってみた。

Xcodeでクラスを自動生成すると次のようなコードができる。なるほど対多関連は NSSet として実装されるのか。また tagsのアクセッサも自動的に用意されるようだ。これは便利。

BlogEntry.h

@interface BlogEntry :  NSManagedObject  
{
}

@property (retain) NSDate * created;
@property (retain) NSString * title;
@property (retain) NSString * content;
@property (retain) NSSet* tags;

@end

@interface BlogEntry (CoreDataGeneratedAccessors)
- (void)addTagsObject:(NSManagedObject *)value;
- (void)removeTagsObject:(NSManagedObject *)value;
- (void)addTags:(NSSet *)value;
- (void)removeTags:(NSSet *)value;

@end


Tag.h

@interface Tag :  NSManagedObject  
{
}

@property (retain) NSString * name;

@end


テストデータはプログラムで詰め込む。

- (void)addTestData
{
if ([self getCount]) {
return;
}

NSManagedObjectContext* moc = [self managedObjectContext];

Tag* tag1 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag"
  inManagedObjectContext:moc];
tag1.name = @"Mac";
Tag* tag2 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag"
  inManagedObjectContext:moc];
tag2.name = @"iPhone";
Tag* tag3 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag"
  inManagedObjectContext:moc];
tag3.name = @"iPad";
BlogEntry* blog1 = [NSEntityDescription insertNewObjectForEntityForName:@"BlogEntry"
inManagedObjectContext:moc];
blog1.title = @"CoreData のリレーションしっぷについて";
blog1.content = @"かくかくしかじか";
blog1.created = [NSDate date];
blog1.tags = [NSSet setWithObjects:tag1, tag2, tag3, nil];

BlogEntry* blog2 = [NSEntityDescription insertNewObjectForEntityForName:@"BlogEntry"
inManagedObjectContext:moc];
blog2.title = @"iPad 5/28発売";
blog2.content = @"かくかくしかじか";
blog2.created = [NSDate date];
blog2.tags = [NSSet setWithObjects:tag3, nil];

NSError* error = nil;
[moc save:&error];
if (error) {
NSLog(@"INSERT ERROR: %@", error);
} else {
NSLog(@"INSERTED");
}
}



これをフェッチしてみる。ログを追いやすい様に仕切りを入れてある。

- (void)fetchData
{
NSManagedObjectContext* moc = [self managedObjectContext];
NSFetchRequest* request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"BlogEntry"
  inManagedObjectContext:managedObjectContext]];

NSError* error = nil;
NSLog(@"----- executeFetchRequest ------------------------------------------");
NSArray* list = [moc executeFetchRequest:request error:&error];
NSLog(@"----- listup tag ---------------------------------------------------");
for (BlogEntry* entry in list) {
for (Tag* tag in entry.tags) {
NSLog(@"*tag*: %@", tag.name);
}
}

[request release];
}


さて実行してみる。設定で実行SQLもログに出るようにしてある。


[13883:10b] CoreData: sql: pragma cache_size=1000
[13883:10b] CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA
[13883:10b] CoreData: sql: SELECT COUNT(*) FROM ZBLOGENTRY t0
[13883:10b] CoreData: annotation: total count request execution time: 0.0022s for count of 2.
[13883:10b] ----- executeFetchRequest ------------------------------------------
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZTITLE, t0.ZCONTENT, t0.ZCREATED FROM ZBLOGENTRY t0
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0013s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0033s for 2 rows.
[13883:10b] ----- fetch tags ---------------------------------------------------
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK FROM ZTAG t0 WHERE  t0.Z1TAGS = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0017s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0034s for 2 rows.
[13883:10b] CoreData: annotation: to-many relationship fault "tags" for objectID 0x17bfe0 fulfilled from database.  Got 2 rows
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.Z1TAGS FROM ZTAG t0 WHERE  t0.Z_PK = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0015s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0037s for 1 rows.
[13883:10b] CoreData: annotation: fault fulfilled from database for : 0x17dd40
[13883:10b] ** iPhone
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.Z1TAGS FROM ZTAG t0 WHERE  t0.Z_PK = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0013s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0023s for 1 rows.
[13883:10b] CoreData: annotation: fault fulfilled from database for : 0x17c5b0
[13883:10b] ** Mac
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK FROM ZTAG t0 WHERE  t0.Z1TAGS = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0010s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0020s for 1 rows.
[13883:10b] CoreData: annotation: to-many relationship fault "tags" for objectID 0x17bff0 fulfilled from database.  Got 1 rows
[13883:10b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.Z1TAGS FROM ZTAG t0 WHERE  t0.Z_PK = ?
[13883:10b] CoreData: annotation: sql connection fetch time: 0.0012s
[13883:10b] CoreData: annotation: total fetch execution time: 0.0116s for 1 rows.
[13883:10b] CoreData: annotation: fault fulfilled from database for : 0x17d000
[13883:10b] ** iPad


わかりずらいが、まとめると次のようになる。
  • BlogEntry を取得した段階では Tagは取得されない(fault状態)
  • Tag への参照が発生した段階で、SELECTが発行される
なるほど。データのフェッチは必要最低限になるようだ。これはいい。



ソースコード:
CoreDataRelationship at 2010-05-17 from xcatsan's SampleCode - GitHub


(続く)