Sqlpo Presentation
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Sqlpo Presentation

on

  • 1,460 views

360}|iDev Presentation

360}|iDev Presentation

Statistics

Views

Total Views
1,460
Views on SlideShare
1,459
Embed Views
1

Actions

Likes
1
Downloads
32
Comments
0

1 Embed 1

http://www.slideshare.net 1

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • <br />
  • And that is spelled with an “A” contrary to the sign out front. <br />
  • A lot of people guard their personal e-mail for fear of spam and other unwelcome e-mail in their inbox. I’ve never been that smart. I’m somewhat virtually promiscuous. I’ve been using this e-mail address since Apple first started that service. I have had, and still have others, but this is the best way to reach me. <br /> <br /> I do try to respond to every e-mail I get, though sometimes I do get a little overwhelmed. If you send me an e-mail, and you don’t get a response in a week, feel free to send another one. The only e-mails I ignore on purpose are spam. <br /> <br /> The link is to my blog, focused on iPhone development from a technical standpoint, primarily. I post code, tutorials, etc., and also link to other good resources. <br />
  • If you know my name, it’s probably from my writing. I do a fair amount of technical writing and there’s no better way to cement your understanding of a technology then to try and explain it to somebody else. It’s also a great way to raise your visibility in the community. Writing is what allowed me to go full-time with Cocoa / Apple technologies. <br />
  • Writing is great, but the bulk of my income comes from doing contract programming work. Prior to last March, I was doing mostly Enterprise Software consulting - travel-based programming for large corporate and government entities. Not exciting, but pays the bills, and because I used to be a developer for PeopleSoft, it was the easiest type of work for me to get. <br /> <br /> The release of the iPhone SDK and the book deal gave me the opportunity to finally jump with both feet into the Mac programming world. <br />
  • This slide is just for grins & giggles. This bad-boy (well, not this particular bad-boy, but one that looked like this) was what I used to write my first line of code... in Applesoft Basic. I include this because a lot of people don’t believe me when I tell them I started coding on an Apple ][ made by Bell & Howell. <br />
  • We’ve come a little way, haven’t we? <br />
  • <br />
  • Fancy way of saying that it takes data stored in objects and stores and retrieves that data in and out of a relational database. Relational databases and OO programs use different, but comparable, paradigms. <br />
  • The SQL strings were “black boxes” as far as the compiler was concerned - you could put the Gettysburg Address into a string literal, and your program would happily pass that onto the SQL server. <br /> <br />
  • All software development builds on earlier stuff, so there may be earlier examples that could be thought of as forms of ORM, but many people recognize EOF as the first fully-fledged ORM tool, and it was the first one I used. WebObjects, though it’s now Java-based, back in 1996 was Objective-C based, and it cost $50,000 a license. They were able to get that because the development time with WO & EOF was so much faster than the tools commonly in use at the time - primarily manual CGI coding. <br /> <br /> The basic idea behind EOF was that instead of writing SQL, you used a GUI tool to create mappings to a remote database. The mappings specified which objects and attributes corresponded to which tables and field. Once you created this mapping document (called an EOModel), you then interacted with the data in the database as if they were objects. <br /> <br /> This represented a HUGE step forward. <br />
  • WebObjects and EOF slowly fell to the side (yes, it still exists, but are not still widely used outside of Apple), while other development platforms took the ideas EOF pioneered, contorted them to fit the design patterns of other languages and, in some cases, extended them <br />
  • ActiveRecord is, in a sense, the logical extension of EOF. Ruby shares a lot of similarities with Objective-C, including a very dynamic nature - both languages allow methods and attributes to be added at runtime. ActiveRecord got rid of the need for an intermediate modeling tool, and leveraged the dynamic nature of the language to automatically create the class attributes based on the database table structure. <br /> <br /> When I first became aware of ActiveRecord, it was like the clouds parted and the angels sang. Seriously. If you’ve ever done integration work, you know just how cool this idea is. One of the first times I experimented with ActiveRecord, I took a 270 line Java integration program and turned it into a 27 line Ruby script thanks to ActiveRecord and Ruby’s terse syntax. <br /> <br /> Now, we’re at a point where we have no SQL and no modeling tool. Your objects just take shape based on the contents of the database. <br /> <br />
  • With the release of Tiger, Apple gave us Core Data, which is not EOF, but is very heavily influenced by it, and almost certainly shares some code. Using Core Data is very similar to using EOF, except there’s no remote database. They took the basic idea and reworked toward the needs of the workstation (laptop/desktop) application developer You design your data model visually, in a tool much like the one used to create the mappings in EOF, and by doing so (assuming you’ve selected SQLite as your storage option), it automatically creates the underlying tables in an embedded SQLite database. It’s great. But... <br />
  • When we were writing the early chapters of the iPhone SDK, things were changing all the time, and it was hard to get a straight answer about what things we were used to having in Cocoa would be available on the iPhone. At first, Interface Builder wasn’t there, but by Beta4, we had a mostly functioning IB. <br /> <br /> At some point, it became clear that Core Data wasn’t going to be ported to the iPhone, at least for the initial release, even though SQLite was included on the iPhone. The iPhone documentation was recommending the use of SQLite for applications that had a lot of structured data, and Apple even posted sample code showing the “recommended way” to access SQLite in your applications - the old fashioned way, embedding static SQL strings in your application. <br /> <br /> They basically wanted us to step back in time fifteen years and do it the way we did it before EOF existed. <br /> <br /> Well, no thank you. <br />
  • <br />
  • <br />
  • Here is a fully functional class declaration. Notice that we have properties that are objects, and native datatypes. That’s all no problem. It can handle most raw datatypes, and most objects. <br /> <br /> It can’t handle non-object pointers, like void * and char *, so you should use NSString instead of C-strings, and any chunk of memory that you have in a pointer, should be put into an NSData or NSMutableData. <br /> <br /> When it comes to objects, they are saved based on whether the implement a formal protocol. We have provided categories for most standard objects that tells SQLPO how to save those objects into the database, translate from the object to a table column, so the data is searchable. A property that is also a subclass of SQLitePersistentObjects will get stored in its own table, with a reference to that object’s row and table being stored in this object. <br /> <br /> If there is no category (and the object is not also a subclass of SQLitePersistentObject, then it will look to see if the object conforms to NSCoding. If it does, it will archive the object into a blob. <br /> You must define properties for anything you want persisted into SQLite. Any non-persistent variables should not have properties. That’s how SQLPO knows what to save and what not to save. Instance Variables without properties will not get saved to the database, nor will read-only properties. <br />
  • Here is a fully functional class declaration. Notice that we have properties that are objects, and native datatypes. That’s all no problem. It can handle most raw datatypes, and most objects. <br /> <br /> It can’t handle non-object pointers, like void * and char *, so you should use NSString instead of C-strings, and any chunk of memory that you have in a pointer, should be put into an NSData or NSMutableData. <br /> <br /> When it comes to objects, they are saved based on whether the implement a formal protocol. We have provided categories for most standard objects that tells SQLPO how to save those objects into the database, translate from the object to a table column, so the data is searchable. A property that is also a subclass of SQLitePersistentObjects will get stored in its own table, with a reference to that object’s row and table being stored in this object. <br /> <br /> If there is no category (and the object is not also a subclass of SQLitePersistentObject, then it will look to see if the object conforms to NSCoding. If it does, it will archive the object into a blob. <br /> You must define properties for anything you want persisted into SQLite. Any non-persistent variables should not have properties. That’s how SQLPO knows what to save and what not to save. Instance Variables without properties will not get saved to the database, nor will read-only properties. <br />
  • Here’s the implementation of the class - nothing special, just typical stuff. You just implement it, and the SuperClass knows how to do pretty much everything. You could, of course (and probably should) create a designated initializer, but for simplicity’s sake, I’ve left it off here. <br />
  • All you do is allocate the object as you would any other object, assign values to its properties, and then call the save method. <br /> <br /> That one line is all it takes to save this object as a row in a table in a SQLite database. You don’t need to create the database, you don’t need to create the table, you just save. <br />
  • <br />
  • <br />
  • <br />
  • NSDictionary, NSArray, NSSet, and their mutable counterparts all follow these rules. <br />
  • <br />
  • In addition to the properties you define, there is one more special one, called pk, which stands for “primary key”. When you know the pk (and you’ll see why you might later), loading an object is simple enough. What if you don’t know the pk value, though? <br />
  • This will create an instance of your class for every row in the database. You might do this sometimes, but usually you don’t want every objects. <br />
  • This method will return an array of all people with a last name that is equal to “Washington”. <br />
  • You can also use SQL pattern matching - using the % or _ wildcards (percent matches any number of characters, underscore matches just one character), this code would return everybody whose last name begins with “Wash”. Note: The bottom two examples will generate a compiler warning because the compiler won’t be able to find the method. That’s logical, since it doesn’t exist - these methods are created dynamically at runtime. You can squelch the warning by creating a category on your own class in your class’ header file <br />
  • There’s our category - add the declaration for any dynamic methods you use to a category like this, right in your class’ header file, and the compiler warnings go away. <br />
  • If you use this method, you are responsible for providing the correct SQL. It must start with “WHERE” and you must use the correct column names. Capitalized letters in camel case become lower-case with an underscore. But, you don’t have to remember that, because there’s a category on NSString that will do it for you. You can even specify an “ORDER BY” clause if you want the results returned in a specific order. <br />
  • If you use this method, you are responsible for providing the correct SQL. It must start with “WHERE” and you must use the correct column names. Capitalized letters in camel case become lower-case with an underscore. But, you don’t have to remember that, because there’s a category on NSString that will do it for you. You can even specify an “ORDER BY” clause if you want the results returned in a specific order. <br />
  • Don’t guess! When in doubt about the column name, use the category - you’ll be using the same code that we use to create the tables in the first place. <br />
  • <br />
  • There is also an instance method of the same name that can be called to delete an object from the database, but it’s use is discourage and is going to be deprecated. Calling delete on an instance is problematic, because SQLPO maintains a map of all objects that have been loaded. It does this so that if you load the same object separately in two places, both places will be referring to the same object, rather than two separate objects created from the same source data. <br /> <br /> You should release the object before deleting it. If you don’t, the object will continue to exist in memory after being deleted, and could end up getting unintentionally saved back to the database. <br />
  • <br />
  • For this, you should use the property name, not the column name. In this example, we’re telling SQLPO to create two indexes, one on lastname, firstname, and pk (in that order), the other on birthdate, pk. <br />
  • Here is a potential problem. The iPhone does not have virtual memory in the same way that your Mac does. It won’t write volatile pages of memory to the filesystem, and it only has 128 megs to work with. For everything. That means video ram, OS, everything. So, what do you think will happen when we load 1,000 people, each with a one-megapixel photo, into memory so that we can display them in a table? <br />
  • Here is a potential problem. The iPhone does not have virtual memory in the same way that your Mac does. It won’t write volatile pages of memory to the filesystem, and it only has 128 megs to work with. For everything. That means video ram, OS, everything. So, what do you think will happen when we load 1,000 people, each with a one-megapixel photo, into memory so that we can display them in a table? <br />
  • Here is a potential problem. The iPhone does not have virtual memory in the same way that your Mac does. It won’t write volatile pages of memory to the filesystem, and it only has 128 megs to work with. For everything. That means video ram, OS, everything. So, what do you think will happen when we load 1,000 people, each with a one-megapixel photo, into memory so that we can display them in a table? <br />
  • Yeah, crash and burn. With traditional object archiving, it would be complex to deal with this. SQLite lets us get information without loading our whole object into memory, however, and SQLPO makes it easy to do. In order to utilize this functionality, though, you need to understand a concept called “paired arrays”. If you’ve set up a settings bundle for an application and used the MultiValue fields, you have seen paired arrays in action. To do that, you provide one array with the values to be displayed, and the other with the values that will actually be stored in the user defaults system. <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • It may sound a little confusing, but it’s really quite simple in practice. Let’s take a look. <br />
  • So, just to be clear, the first object in the pks array will hold the primary key for the person whose first name is contained in the first object of the firstNames array and whose last name is contained in the first object of the lastNames array. Same goes for the second row, the third row, etc. <br />
  • In this code here, we’re loading the arrays with information about all the people whose birthdate we know. We’re getting their first name and last name so we can display it in a table, and we keep the primary keys so that if the user selects a row, we know which person to load and display. <br />
  • <br />
  • <br />
  • <br />
  • NSMutableArray has the ability to sort its contents based on a comparison method. But, if you sort the arrays, all of a sudden, you have a problem because the fifth object in one array, no longer necessarily corresponds to the fifth object in the other arrays. <br />
  • <br />
  • <br />
  • SQLPO provides a category on NSMutableArray that lets you sort an array and also sort all of its paired arrays so that they all stay in sync. <br /> <br /> That pretty much sums up the basics. You’ve seen how to create, load, save, and delete objects and you’ve seen how to use and sort paired arrays so that you can display data from all the objects in the database with having memory problems. <br /> <br /> Now, let’s look at some of the more involved uses of the database. <br />
  • Here is how it sorts if we sort the firstNames array using sortArrayUsingSelector:withPairedMutableArrays: instead of separately sorting the three arrays. <br />
  • <br />
  • Do you want to know the sum or average value a field? In the old days, that would have involved iterating over all your objects and keeping a running count. Not so with SQLite. SQLite will do aggregations for you without having to load objects into memory, and it’s very, very fast. <br /> <br /> SQLitePersistentObject has a convenience method for getting aggregate values. It returns a single floating point value returned from the SQL query you supply. Here is how you might find the average age of all the people in the database, or of just a subset. There are many aggregation methods, much like you might find in Numbers or Excel, including min, max, sum, average, rounding, etc. <br />
  • SQLitePersistentObjects also creates dynamic methods at runtime for getting aggregations. Currently average, sum, count, min, and max are supported, either for the entire population, or for a subset by specifying “withCriteria” and providing a SQL where clause. <br /> <br /> Additional aggregations are coming soon, including min, max, maybe median. <br /> <br /> Because these are dynamic methods, will get compiler warnings about them. You can squelch them with a category, the way I showed you earlier with the findBy methods. <br />
  • Let’s say you’ve got an object out there that you want to get saved in the database, but you want it be searchable, not stored as a blob, but that object doesn’t conform to SQLitePersistence. What can you do? <br /> <br /> Answer is easy - create a category to conform that class to SQLitePersistence. Here’s that protocol. <br /> <br /> These methods define how SQLPO talks to an object to determine if it can be saved in a column, and how to actually save it. <br /> The first method is used to indicate that this object can be stored in SQLite. Simply return YES in that method to indicate that your object should be saved. <br /> <br /> The second method indicates the type of column that should be used. It should return TEXT, INTEGER, REAL, or BLOB. <br /> <br /> The third method is used to ask the object if it needs to be stored in a BLOB. Now, this probably seems a little redundant, since BLOB was one of the options in the previous method, but simply for performance, it’s faster to specify a BOOL than it is to constantly be doing string comparisons. If you return YES here, you should return BLOB in the columnTypeForObjectStorage method, although SQLite is pretty cool about datatypes, and will convert as necessary. <br /> The last four methods serve the same purpose - one is used for objects that return YES to shouldBeStoredInBlob, and the other is used if the answer is NO. If it’s going to be stored in a BLOB, you have to provide the information in an NSData to make sure it doesn’t get altered in any way. Otherwise, you can return an object. When in doubt, return an NSString, but you can also use NSNumber, or pretty much any other object that prints its value when sent the description method. <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • The project currently has no formal roadmap. Development is driven now by the specific needs of the project members. When one of us needs a certain bit of functionality for a project we’re working on, we propose it to the group, vet it out for possible problems, and then it gets implemented. Very informal. At some point, we plan to sit down and come up with something more formal, and more long-term, but probably won’t be for a few months, given how busy most iPhone developers are right now. <br /> <br /> Some of the things that have been discussed include implementing a validation architecture so that objects can do more robust validation, such things as enforcing unique fields or combinations of fields, notifications to the object that a save has occurred or will occur, and possibly even giving the object the ability to conditionally cancel or prevent a save. <br /> <br /> Transient properties are properties that don’t get saved to the database. Currently read-only properties are treated as transient, but there is beta functionality in the system to allow other properties to be specified as transient. <br /> <br /> There’s always room for performance optimizations. There are several places in the code where there’s room to eke out more performance, and we’ll visit those once we’ve done benchmarking and have a real view of where our slowdowns are. <br /> <br /> Rollback isn’t much of an issue for most iPhone applications, since we tend to make the user commit changes fairly frequently. However, to get wider acceptance on the Mac <br />
  • Honestly, we don’t have benchmarks. We haven’t done rigorous testing or benchmarking yet. All we have is anecdotal evidence from using it in a handful of applications. So far, I have yet to see any issues with performance compared to hand-coding the SQL. <br /> <br /> Now, we know it takes more processing power to dynamically generate SQL on the fly than it does to execute hard-coded SQL stored in string constants. That’s a fact. We also know there are places in the code that can be optimized to work faster. But, it seems to be fast enough for the vast majority of uses on the iPhone. <br />
  • Honestly, we don’t have benchmarks. We haven’t done rigorous testing or benchmarks. All we have is anecdotal evidence from using it in a handful of applications. So far, I have yet to see any issues with performance compared to hand-coding the SQL. <br /> <br /> Now, we know it takes more processing power than hard-coded SQL. That’s a fact. We also know there are places in the code that can be optimized. But, it seems to be fast enough for the vast majority of uses on the iPhone. <br />

Sqlpo Presentation Presentation Transcript

  • 1. SQLitePersistentObjects Wednesday, March 11, 2009
  • 2. SQLite Without the SQL Jeff LaMarche Wednesday, March 11, 2009
  • 3. Contacting Me • jeff_lamarche@mac.com • http://iphonedevelopment.blogspot.com • Twitter: jeff_lamarche Wednesday, March 11, 2009
  • 4. About Me • Writer • Beginning iPhone Development (Apress) • Several Articles for ADC & MacTech Mag • Beginning Cocoa (Apress, in progress) • Regular blog postings Wednesday, March 11, 2009
  • 5. More About Me • Programmer • Primarily contract work • Using Cocoa since about 2000 • OO Programming since early 1990s • Programming since 1980 • Full-time with Cocoa for only a year Wednesday, March 11, 2009
  • 6. Image Source: http://www.macgeek.org/museum/bhapple2plus/page02.html Wednesday, March 11, 2009
  • 7. Wednesday, March 11, 2009
  • 8. Enough About Me (yeah, seriously) Wednesday, March 11, 2009
  • 9. SQLPO SQLitePersistentObjects • Object-Relational Mapping (ORM) Tool or for the more technically-minded: code that takes data from your program and sticks it into or pulls it out of a relational database Wednesday, March 11, 2009
  • 10. In the Beginning... • Early OO programs that needed to use data from a database embedded SQL statements in code • Difficult to maintain • Strings are a “black box” to the compiler Wednesday, March 11, 2009
  • 11. ORM History • 1996 Enterprise Object Frameworks (EOF) • Part of NeXT’s Cocoa-Based WebObjects • Used visual tool to map data objects to database table • Once model created, you interacted with database tables as if they were objects • Didn’t even have to create specific classes if you didn’t want to Wednesday, March 11, 2009
  • 12. ORM History • Basic idea was borrowed by other platforms, changed to work with other languages, sometimes extended to be better • Hibernate, Cayenne, ADO .net, Outlet, Django, many others Wednesday, March 11, 2009
  • 13. ORM History • One language evolved the concept • Ruby on Rails’ ActiveRecord • No longer needed mapping document • Object structure dynamically created based on table structure in database Wednesday, March 11, 2009
  • 14. ORM History • Core Data • Core Data on the Mac is EOF’s stepchild • Core Data is NOT EOF, but shares some DNA Wednesday, March 11, 2009
  • 15. ORM History Apple has not ported Core Data to the iPhone (yet). That fact is what triggered the birth of SQLitePersistentObjects Wednesday, March 11, 2009
  • 16. SQLPO SQLitePersistentObjects • You can always find the latest version here: http://code.google.com/p/sqlitepersistentobjects/ • Add the code from the /src directory to your Xcode project • Make sure to link to the SQLite3 dynamic library at /usr/lib/libsqlite3.dylib Wednesday, March 11, 2009
  • 17. SQLPO SQLitePersistentObjects • We have a support mailing list: http://groups.google.com/group/sqlitepersistentobjects-user • Licensed under the New BSD License • Free, open source. • No attribution required, non-viral, can be used in commercial projects, no need to publish your code • If you can’t use BSD licensed code for some reason, contact me - we’re flexible on the license. Wednesday, March 11, 2009
  • 18. The Basics SQLitePersistentObjects • To create a persistent object, you simply subclass an existing class. #import <Foundation/Foundation.h> #import quot;SQLitePersistentObject.hquot; @interface Person : SQLitePersistentObject { NSString *firstName; NSString *lastName; NSDate *birthdate; int numberOfChildren; float contribution; UIImage *photo; } @property (nonatomic, retain) NSString *firstName; @property (nonatomic, retain) NSString *lastName; @property (nonatomic, retain) NSDate *birthdate; @property int numberOfChildren; @property float contribution; @property (nonatomic, retain) UIImage *photo; @end Wednesday, March 11, 2009
  • 19. The Basics SQLitePersistentObjects • To create a persistent object, you simply subclass an existing class. #import <Foundation/Foundation.h> #import quot;SQLitePersistentObject.hquot; @interface Person : SQLitePersistentObject { NSString *firstName; NSString *lastName; NSDate *birthdate; int numberOfChildren; float contribution; UIImage *photo; } @property (nonatomic, retain) NSString *firstName; @property (nonatomic, retain) NSString *lastName; @property (nonatomic, retain) NSDate *birthdate; @property int numberOfChildren; @property float contribution; @property (nonatomic, retain) UIImage *photo; @end Wednesday, March 11, 2009
  • 20. The Basics SQLitePersistentObjects • To create a persistent object, you simply subclass an existing class. #import quot;Person.hquot; @implementation Person @synthesize firstName; @synthesize lastName; @synthesize birthdate; @synthesize numberOfChildren; @synthesize contribution; @synthesize photo; - (void)dealloc { [firstName release]; [lastName release]; [birthdate release]; [photo release]; [super dealloc]; } @end Wednesday, March 11, 2009
  • 21. The Basics SQLitePersistentObjects • Once you’ve defined a persistent object, creating a new object and saving it to the database couldn’t be easier: Person *newPerson = [[Person alloc] init]; newPerson.firstName = @quot;Marthaquot;; newPerson.lastName = @quot;Washingtonquot;; newPerson.birthdate = [NSDate date]; newPerson.numberOfChildren = 5; newPerson.contribution = 27.32; newPerson.photo = [UIImage imageNamed:@quot;MarthaWashington.pngquot;]; [newPerson save]; [newPerson release]; Wednesday, March 11, 2009
  • 22. The Basics SQLitePersistentObjects • Once you’ve defined a persistent object, creating a new object and saving it to the database couldn’t be easier: Person *newPerson = [[Person alloc] init]; newPerson.firstName = @quot;Marthaquot;; newPerson.lastName = @quot;Washingtonquot;; newPerson.birthdate = [NSDate date]; newPerson.numberOfChildren = 5; newPerson.contribution = 27.32; newPerson.photo = [UIImage imageNamed:@quot;MarthaWashington.pngquot;]; [newPerson save]; [newPerson release]; Wednesday, March 11, 2009
  • 23. The Basics SQLitePersistentObjects • How does it save the data? • Raw datatypes are mapped to appropriate columns • e.g. int goes into INTEGER field, float goes into REAL field • Other persistent objects are stored as references to the row and table where that object is stored Wednesday, March 11, 2009
  • 24. The Basics SQLitePersistentObjects • How does it save the data? (cont) • Objects that aren’t subclasses of SQLitePersistentObject get stored IF they conform to a protocol called SQLitePersistence • We have provided categories for most common objects to conform them to SQLitePersistence • NSString, NSNumber, UIImage, UIColor, NSData, NSDate, NSMutableData Wednesday, March 11, 2009
  • 25. The Basics SQLitePersistentObjects • How does it save the data? (cont) • We also provide category on NSObject as a fallback. Any object that doesn’t conform to SQLitePersistence but that does conform to NSCoding can be stored in the database, but gets archived as a BLOB, which can’t be searched or used in criteria-based queries. Wednesday, March 11, 2009
  • 26. The Basics SQLitePersistentObjects • How does it save the data? (cont) • Dictionaries, Arrays, and Sets do not get stored in a column in the object’s table, but rather get stored in a child table. • Each item in collection gets one row in the child table • Raw Datatypes and non-persistent objects get stored right in child table • Persistent objects get stored as references Wednesday, March 11, 2009
  • 27. The Basics SQLitePersistentObjects • How does it save the data? (cont) • Every objects gets assigned a primary key value, which is an integer that acts as a unique identifier for that object • Stored in a private instance variable called pk • Used in some cases load objects from database Wednesday, March 11, 2009
  • 28. The Basics SQLitePersistentObjects • Loading an object from the database is accomplished through class methods. This is the simplest one: Person *martha = [Person findByPK:1]; Wednesday, March 11, 2009
  • 29. The Basics SQLitePersistentObjects • You can load all objects into an array. Be careful doing this, however, as it is not a very efficient use of memory in most cases. NSArray *people = [Person allObjects]; Wednesday, March 11, 2009
  • 30. The Basics SQLitePersistentObjects • SQLPO also creates dynamic find methods based on your property names: NSArray *people = [Person allObjects]; NSArray *people = [Person findByLastName:@quot;Washingtonquot;]; Wednesday, March 11, 2009
  • 31. The Basics SQLitePersistentObjects • Dynamic find by methods support SQL wildcards NSArray *people = [Person allObjects]; NSArray *people = [Person findByLastName:@quot;Washingtonquot;]; NSArray *people = [Person findByLastName:@quot;Wash%quot;]; Wednesday, March 11, 2009
  • 32. The Basics SQLitePersistentObjects #import <Foundation/Foundation.h> #import quot;SQLitePersistentObject.hquot; @interface Person : SQLitePersistentObject { NSString *firstName; NSString *lastName; NSDate *birthdate; int numberOfChildren; float contribution; UIImage *photo; } @property (nonatomic, retain) NSString *firstName; @property (nonatomic, retain) NSString *lastName; @property (nonatomic, retain) NSDate *birthdate; @property int numberOfChildren; @property float contribution; @property (nonatomic, retain) UIImage *photo; @end @interface Person (squelch) + (id)findByName:(NSString *)theName; @end Wednesday, March 11, 2009
  • 33. The Basics SQLitePersistentObjects #import <Foundation/Foundation.h> #import quot;SQLitePersistentObject.hquot; @interface Person : SQLitePersistentObject { NSString *firstName; NSString *lastName; NSDate *birthdate; int numberOfChildren; float contribution; UIImage *photo; } @property (nonatomic, retain) NSString *firstName; @property (nonatomic, retain) NSString *lastName; @property (nonatomic, retain) NSDate *birthdate; @property int numberOfChildren; @property float contribution; @property (nonatomic, retain) UIImage *photo; @end @interface Person (squelch) + (id)findByName:(NSString *)theName; @end Wednesday, March 11, 2009
  • 34. The Basics SQLitePersistentObjects • If you need more flexibility, you can always specify the exact criteria by supplying a SQL where clause with findByCriteria: NSArray *people = [Person findByCriteria:@quot;WHERE first_name = 'John' and last_name like 'S%' and date(birthdate) <= date('now')quot;]; Wednesday, March 11, 2009
  • 35. The Basics SQLitePersistentObjects • If you need more flexibility, you can always specify the exact criteria by supplying a SQL where clause with findByCriteria: NSArray *people = [Person findByCriteria:@quot;WHERE first_name = 'John' and last_name like 'S%' and date(birthdate) <= date('now')quot;]; Note: Property firstName becomes column first_name Wednesday, March 11, 2009
  • 36. The Basics SQLitePersistentObjects • You can find out the column name for a property name like so: #import quot;NSString-SQLiteColumnName.hquot; … NSString *columnName = [@quot;firstNamequot; stringAsSQLColumnName]; Wednesday, March 11, 2009
  • 37. The Basics SQLitePersistentObjects • If you only want the first object that meets your criteria, you can do that also: Person *firstPerson = [Person findFirstByCriteria:@quot;WHERE first_name = 'John' and last_name like 'S%' and date(birthdate) <= date('now')quot;]; Wednesday, March 11, 2009
  • 38. The Basics SQLitePersistentObjects • Deleting an object should be done using the class method deleteObject:cascade:, which takes the primary key of the object to be deleted. [Person deleteObject:5 cascade:YES]; Wednesday, March 11, 2009
  • 39. The Basics SQLitePersistentObjects • Database tables can benefit from adding indices to them. SQLitePersistentObjects has a mechanism for adding indices without writing SQL. Override this method in your class: +(NSArray *)indices { return nil; } Wednesday, March 11, 2009
  • 40. The Basics SQLitePersistentObjects • Method should return an array of arrays. Each contained array represents one index and should have the properties to be indexed in the order they should be in the index. +(NSArray *)indices { NSArray *firstIndex = [NSArray arrayWithObjects:@quot;lastNamequot;, @quot;firstNamequot;, @quot;pkquot;, nil]; NSArray *secondIndex = [NSArray arrayWithObjects:@quot;birthdatequot;, @quot;pkquot;, nil]; return [NSArray arrayWithObjects:firstIndex, secondIndex, nil]; } Wednesday, March 11, 2009
  • 41. The Basics SQLitePersistentObjects • Let’s look at our class declaration again. #import <Foundation/Foundation.h> #import quot;SQLitePersistentObject.hquot; @interface Person : SQLitePersistentObject { NSString *firstName; NSString *lastName; NSDate *birthdate; int numberOfChildren; float contribution; UIImage *photo; } @property (nonatomic, retain) NSString *firstName; @property (nonatomic, retain) NSString *lastName; @property (nonatomic, retain) NSDate *birthdate; @property int numberOfChildren; @property float contribution; @property (nonatomic, retain) UIImage *photo; @end Wednesday, March 11, 2009
  • 42. The Basics SQLitePersistentObjects • Let’s look at our class declaration again. #import <Foundation/Foundation.h> #import quot;SQLitePersistentObject.hquot; @interface Person : SQLitePersistentObject { NSString *firstName; NSString *lastName; NSDate *birthdate; int numberOfChildren; float contribution; UIImage *photo; } @property (nonatomic, retain) NSString *firstName; @property (nonatomic, retain) NSString *lastName; @property (nonatomic, retain) NSDate *birthdate; @property int numberOfChildren; @property float contribution; @property (nonatomic, retain) UIImage *photo; @end Wednesday, March 11, 2009
  • 43. The Basics SQLitePersistentObjects • Let’s look at our class declaration again. Wednesday, March 11, 2009
  • 44. The Basics SQLitePersistentObjects • Let’s talk about Paired Arrays. • Simple Concept - Multiple Arrays • Every array has same number of rows • Object at same index in each array corresponds to information about the same object • e.g. fifth object in one array might hold Joe’s age, and the fifth object in the other array might hold Joe’s last name. Wednesday, March 11, 2009
  • 45. The Basics SQLitePersistentObjects • Let’s talk about Paired Arrays (cont) • Can have as many arrays as necessary. • SQLPO has built-in method to return specified paired arrays. • It packs all the paired arrays together inside another array • First array in the array always contains a list of the primary keys. Wednesday, March 11, 2009
  • 46. The Basics SQLitePersistentObjects Example: Three NSArrays pks firstNames lastNames 1 Martha Washington 2 Joe Smith 3 Sally Ride 4 George Washington 5 Buster Keaton Wednesday, March 11, 2009
  • 47. The Basics SQLitePersistentObjects Example: Three NSArrays pks firstNames lastNames 1 Martha Washington 2 Joe Smith 3 Sally Ride 4 George Washington 5 Buster Keaton Wednesday, March 11, 2009
  • 48. The Basics SQLitePersistentObjects Example: Three NSArrays pks firstNames lastNames 1 Martha Washington 2 Joe Smith 3 Sally Ride 4 George Washington 5 Buster Keaton Wednesday, March 11, 2009
  • 49. The Basics SQLitePersistentObjects • This means we can load in only the information we need to display in the table, along with the information we need to load the full object if the user selects it. Wednesday, March 11, 2009
  • 50. The Basics SQLitePersistentObjects • In our controller class, we’ll need mutable arrays to hold the data. #import <UIKit/UIKit.h> @interface PeopleListViewController : UITableViewController { NSMutableArray *pks; NSMutableArray *firstNames; NSMutableArray *lastNames; } @property (nonatomic, retain) NSMutableArray *pks; @property (nonatomic, retain) NSMutableArray *firstNames; @property (nonatomic, retain) NSMutableArray *lastNames; - (void)refreshData; @end • We also declare a method for loading the arrays. Wednesday, March 11, 2009
  • 51. The Basics SQLitePersistentObjects • Getting the paired arrays is simple enough: - (void)refreshData { NSArray *array = [Person pairedArraysForProperties:[NSArray arrayWithObjects:@quot;firstNamequot;, @quot;lastNamequot;, nil] withCriteria:@quot;where birthdate is not nullquot;]; self.pks = [array objectAtIndex:0]; self.firstNames = [array objectAtIndex:1]; self.lastNames = [array objectAtIndex:2]; } • Just tell it which properties you want, and it will give you all those plus the primary keys. Wednesday, March 11, 2009
  • 52. The Basics SQLitePersistentObjects • In our Table View Data Source, we just get a count of one of the arrays so we know the number of rows we need in our table: - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section { return [pks count]; } Wednesday, March 11, 2009
  • 53. The Basics SQLitePersistentObjects • We can then use the information from the arrays to populate our table: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @quot;Cellquot;; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease]; } NSUInteger row = [indexPath row]; cell.text = [NSString stringWithFormat:@quot;%@, %@quot;, [lastNames objectAtIndex:row], [firstNames objectAtIndex:row]]; return cell; } Wednesday, March 11, 2009
  • 54. The Basics SQLitePersistentObjects • When the user selects a row, we grab the primary key for the selected row, and use that to load the Person: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { int thePk = [[pks objectAtIndex:[indexPath row]] intValue]; Person *thePerson = (Person *)[Person findByPK:thePk]; // Do something with the person here... } Wednesday, March 11, 2009
  • 55. The Basics SQLitePersistentObjects Easy Enough, but there’s a problem. What if you want to sort the arrays? Wednesday, March 11, 2009
  • 56. The Basics SQLitePersistentObjects Example: Three NSArrays pks firstNames lastNames 1 Martha Washington 2 Joe Smith 3 Sally Ride 4 George Washington 5 Buster Keaton Wednesday, March 11, 2009
  • 57. The Basics SQLitePersistentObjects Example: Three NSArrays pks firstNames lastNames 1 Buster Keaton 2 George Ride 3 Joe Smith 4 Martha Washington 5 Sally Washington Wednesday, March 11, 2009
  • 58. The Basics SQLitePersistentObjects • Categories to the Rescue! • Just call sortArrayUsingSelector:withPairedMutableArrays: [lastNames sortArrayUsingSelector:@selector(compare:) withPairedMutableArrays:firstNames, pks, nil]; • Now all three arrays are sorted based on the last name of the people represented in the arrays. Wednesday, March 11, 2009
  • 59. The Basics SQLitePersistentObjects Example: Three NSArrays pks firstNames lastNames 1 Martha Washington 2 Joe Smith 3 Sally Ride 4 George Washington 5 Buster Keaton Wednesday, March 11, 2009
  • 60. The Basics SQLitePersistentObjects Example: Three NSArrays pks firstNames lastNames 5 Buster Keaton 4 George Washington 2 Joe Smith 1 Martha Washington 3 Sally Ride Wednesday, March 11, 2009
  • 61. The Not-So-Basics SQLitePersistentObjects • SQLite makes aggregations easy (if you know SQL) double averageAge = [Person performSQLAggregation: @quot;select avg(date('now') - date(birthdate)) from people where birthdate is not nullquot;]; Wednesday, March 11, 2009
  • 62. The Not-So-Basics SQLitePersistentObjects • But, if you don’t know SQL… you’re not totally out of luck. SQLPO creates dynamic methods for common aggregations based on your objects’ properties: NSNumber *average = [Person averageOfContribution]; NSNumber *washAverage = [Person averageOfContributionWithCriteria:@quot;where name = 'Washington'quot;]; NSNumber *sum = [Person sumOfContribution]; NSNumber *washSum = [Person sumOfContributionWithCriteria:@quot;where name = 'Washington'quot;]; NSNumber *count = [Person countOfPk]; NSNumber *min = [Person minOfContribution]; NSNumber *washMin = [Person minOfContributionWithCriteria:@quot;where name = 'Washington'quot;]; NSNumber *max = [Person maxOfContribution]; NSNumber *washMax = [Person maxOfContributionWithCriteria:@quot;where name = 'Washington'quot;]; Wednesday, March 11, 2009
  • 63. The Not-So-Basics SQLitePersistentObjects • Defining how non-standard, non-persistent objects get stored. @protocol SQLitePersistence @required + (BOOL)canBeStoredInSQLite; + (NSString *)columnTypeForObjectStorage; + (BOOL)shouldBeStoredInBlob; @optional + (id)objectWithSqlColumnRepresentation:(NSString *)columnData; - (NSData *)sqlBlobRepresentationOfSelf; + (id)objectWithSQLBlobRepresentation:(NSData *)data; - (NSString *)sqlColumnRepresentationOfSelf; @end Wednesday, March 11, 2009
  • 64. The Not-So-Basics SQLitePersistentObjects • The Instance Manager • SQLPO has a singleton class that manages the database instance. • Mostly, this class is used behind-the-scenes without any need for you to interact with it. • But... it’s there if you need it. Wednesday, March 11, 2009
  • 65. The Not-So-Basics SQLitePersistentObjects • Getting the shared instance: SQLiteInstanceManager *manager = [SQLiteInstanceManager sharedManager]; Wednesday, March 11, 2009
  • 66. The Not-So-Basics SQLitePersistentObjects • Once you have it, what can you do with it? • Get a reference to the database sqlite3 *db = [manager database]; • Execute arbitrary SQL Updates: [manager executeUpdateSQL:@quot;update foo set bar = 1quot;]; Wednesday, March 11, 2009
  • 67. The Not-So-Basics SQLitePersistentObjects • Once you have it, what can you do with it? (cont) • Find out if a table exists in the database. BOOL exists = [manager tableExists:@quot;superheroesquot;]; • Change database configuration and do maintenance [manager vacuum]; [manager setCacheSize:100] Wednesday, March 11, 2009
  • 68. Performance SQLitePersistentObjects • What Lies Ahead • validation architecture & willSave/didSave/okayToSave: • transient properties • performance optimizations • rollback • refactoring to generalize code where makes sense • ??? (ideas welcome) Wednesday, March 11, 2009
  • 69. Performance SQLitePersistentObjects • How well does it perform? • Umm... good question. • Can we get back to you on that? Wednesday, March 11, 2009
  • 70. SQLitePersistentObjects Questions? Wednesday, March 11, 2009