However, I find the Core Data API to be overly complicated and exceedingly verbose. When compared to other modern ORM APIs, it takes a lot of work to do simple entity management tasks.
Example: Fetch a Collection of Books using Core Data
- (void) testCoreDataQuery {
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
fetchRequest.entity = [NSEntityDescription entityForName: @"Book"
inManagedObjectContext: managedObjectContext];
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc]
initWithKey: @"title" ascending: YES] autorelease];
[fetchRequest setSortDescriptors: [NSArray arrayWithObject: sortDescriptor]];
NSFetchedResultsController *fetchResultsController =
[[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:nil] autorelease];
NSError *error = nil;
if(![fetchResultsController performFetch:&error]) {
NSLog(@"FETCH ERROR: %@",[error userInfo]);
}
NSArray *entities = [fetchResultsController fetchedObjects];
NSLog(@"found %d books",entities.count);
}
That is a lot of code to write to perform what should be a really simple operation. This example doesn't even include any sort or selection criteria.
Introducing BEEntityManager
To simplify interaction with Core Data, I created a simple Entity Manager API which is inspired by the Java Persistence API. I'm guessing there are other APIs like this available, but I haven't bothered to look. Let me know if you know of any.
Example: Fetch a Collection Using BEEntityManager
- (void) testBEEntityManagerQuery {
BEEntityManager *entityManager = [[[BEEntityManager alloc] init] autorelease];
NSArray *books = [entityManager query: @"Book" criteria: @"author = 'F. Scott Fitzgerald'" sort: @"title" ascending: YES ];
NSLog(@"found %d books",books.count);
}
This is a much easier and more maintainable way to interact with a data store. To each their own though.. it's not for everyone... some people like pain.
BEEntityManager API Features
The entity manager includes simple methods for your common data store operations.
query
get
create
createOrUpdate
save
delete
It also simplifies accessing and managing instances of NSManagedObjectContext. This is helpful in apps of any significant size.
You can download BEEntityManager from github. To see examples of how to use the API, take a look at the BEEntityManagerTest class. If you find it useful, please let me know.








Looks promising. I haven't review the test class yet, but have a couple thoughts:
ReplyDelete1) query: @"Book" could your example use [Book className]. or possibly change the interface to [Book class] for a little more type safety?
2) can you provide an example of how to do a query w/ a inner/outer join?
Thanks for the comment.. good points.
ReplyDelete1) I did a quick test on this approach and found a problem. It seems that you can't make a static call to "class" or "className" on subclasses of NSManagedObject. It doesn't compile.
// does not compile
if ([book isKindOfClass: [Book class]]) {
}
// does compile
if ([book isKindOfClass: [NSObject class]]) {
}
Generally, for something like this I prefer to rely on unit testing with code coverage over type safety anyway.
2) So far, with Core Data I haven't had to do anything that is specifically a inner/outer join. However, this entity manager is simply a wrapper to simplify use of Core Data. So if Core Data supports that, it could probably be simplified.
Criteria selection with Core Data is some what different from other ORM query languages. I haven't looked at it in detail, but I think the closest thing to a inner/outer join is the use of an "ANY" clause in a NSPredicate. The following would be similar to a join or sub-select.
NSPredicate *pred = [NSPredicate predicateWithFormat:@"ANY events.ID in %@",[NSArray arrayWithObjects:eventId,nil]];
Good job, I think that BEEntityManager could be a Singleton class.
ReplyDeleteThanks for the comment!
ReplyDeleteIn most use cases, it would probably be fine as a singleton. However, I generally avoid creating singletons. Within our field, there is an almost religious crusade against them. So I just follow convention and don't create them.
As far as scope and instances are concerned, what's really important for an entity manager is how it uses instances of NSManagedObjectContext. By default, this entity manager will use a single instance no matter how many instances of BEEntityManager you create. However, it is not statically assigned to the class. This allows the creation of new instances of NSManagedObjectContext when the use case requires.
So it works similar to a singleton, because new instances will reuse the same NSManagedObjectContext instance. Just be sure to allocate and release appropriately and you wont need a singleton or static BEEntityManager.