IT tutorials
 
Mobile
 

iOS SDK : Basic SQLite Database Manipulation (part 3) - SQLite Binding, Inserting, Updating, and Deleting

11/12/2011 4:16:02 PM
- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire

SQLite Binding, Inserting, Updating, and Deleting

SQL allows limiting data to only the data needed via the where clause. For instance, the following statement only selects records whose age column is greater than 30.

select * from mytable where age > 30

When placing SQL statements like this into a SQLite statement, you can parameterize the where clause’s value. For instance, to parameterize age’s value, write the following code.

select * from mytable where age > ?

You then bind your program’s value to the SQL statement’s parameter.

Binding

You bind one of your program’s values to a SQL statement’s parameter using a bind method (Listing 12). Different data types have different bind methods.

Listing 12. SQLite bind methods (from the SQLite online documentation)
int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n,
void(*)(void*));
int sqlite3_bind_double(sqlite3_stmt*, int, double);
int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
int sqlite3_bind_null(sqlite3_stmt*, int);
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n,
void(*)(void*));
int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int,
void(*)(void*));
int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);

Note

Bindings start with 1 rather than 0.


For instance, the following code snippet shows a SQL statement and its subsequent binding (without the error checking shown).

const char * select = "Select * from photos where name = ?";
sqlite3_stmt *select_statement;
sqlite3_prepare_v2(database, select, -1, &select_statement, NULL);
sqlite3_bind_text(&select_statement, 1, [photo.name UTF8String], -1,
SQLITE_TRANSIENT);

The first argument is a pointer to the prepared statement. The second argument is the SQL statement’s parameter number. The third argument is the value that should be bound to the SQL statement’s parameter. The fourth argument is the number of bytes in the value—if negative, the length is automatically determined from the C string.

Insert, Update, and Delete

There is little difference between the steps for inserting, updating, or deleting records using the SQLite C library. The primary difference is you only call the sqlite3_step method once. Usually, you use insert, update, or delete with bindings. For instance,

insert into customers (name, age, company, location) values (?, ?, ?, ?);

or

update customers set location = ? where company = ?;

or

delete customers where company = ?;

In the following task, you insert, update, and delete a record.

Try This: Inserting, Updating, and Deleting Records

  1. Open the MyDBProject project in Xcode.

  2. Add a class method named moveDatabase to PhotosDAO. Remember, a class method uses a plus rather than a minus.

  3. Implement the method in PhotosDAO.m as in Listing 13.

    Listing 13. The moveDatabase and getAllPhotos methods
    + (void) moveDatabase {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *theDBPath = [[[NSBundle mainBundle] resourcePath]
    stringByAppendingPathComponent:@"myDatabase.sqlite"];
    NSError *error;
    BOOL success;
    NSArray * paths =
    NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
    NSUserDomainMask, YES);
    NSString * docsDir = [paths objectAtIndex:0];
    NSString * newPath =
    [docsDir stringByAppendingPathComponent:@"myDatabase.sqlite"];
    [fileManager removeItemAtPath:newPath error: &error];
    success = [fileManager copyItemAtPath:theDBPath
    toPath:newPath error: &error];
    if (!success) {
    NSLog(@"Failed to copy database...error handling here %@.",
    [error localizedDescription]);
    }
    }

    - (NSMutableArray *) getAllPhotos {
    NSMutableArray * photosArray = [[NSMutableArray alloc] init];
    @try {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray * paths = NSSearchPathForDirectoriesInDomains
    (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString * docsDir = [paths objectAtIndex:0];
    NSString * theDBPath = [docsDir stringByAppendingPathComponent:
    @"myDatabase.sqlite"];
    BOOL success = [fileManager fileExistsAtPath:theDBPath];
    if (!success) {
    NSLog(@"Failed to find database file '%@'.");
    }
    if (!(sqlite3_open([theDBPath UTF8String], &database) ==
    SQLITE_OK)) {
    NSLog(@"An error opening database, handle error here.");
    }
    const char *sql = "SELECT id,name,photo FROM photos";
    sqlite3_stmt *statement;
    if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) !=
    SQLITE_OK) {
    NSLog(@"Error, failed to prepare statement, handle error here.");
    }
    while (sqlite3_step(statement) == SQLITE_ROW) {
    PhotoDAO * aPhoto = [[PhotoDAO alloc] init];
    aPhoto.photoID = sqlite3_column_int(statement, 0);
    aPhoto.name = [NSString stringWithUTF8String:(char *)
    sqlite3_column_text(statement, 1)];
    const char * rawData = sqlite3_column_blob(statement, 2);
    int rawDataLength = sqlite3_column_bytes(statement, 2);
    NSData *data = [NSData dataWithBytes:rawData length:
    rawDataLength];
    aPhoto.photo = [[UIImage alloc] initWithData:data];
    [photosArray addObject:aPhoto];
    }
    if(sqlite3_finalize(statement) != SQLITE_OK){
    NSLog(@"Failed to finalize data statement, error handling here.");
    }
    if (sqlite3_close(database) != SQLITE_OK) {
    NSLog(@"Failed to close database, error handling here.");
    }
    } @catch (NSException *e) {
    NSLog(@"An exception occurred: %@", [e reason]);
    return nil;
    }
    return photosArray;
    }



  4. Modify the getAllPhotos method in PhotosDAO to obtain the records from the documents directory (Listing 13).

  5. Open MYDBProjectViewController.m and add a call to the moveDatabase method to the first line of viewDidLoad (Listing 14).

Listing 14. The viewDidLoad method
- (void)viewDidLoad {
[PhotosDAO moveDatabase];
PhotosDAO * myPhotos = [[PhotosDAO alloc] init];
self.photos = [myPhotos getAllPhotos];
[self.theImageView setImage:((PhotoDAO *)[self.photos
objectAtIndex:0]).photo];
[self.theLabel setText:((PhotoDAO *)
[self.photos objectAtIndex:0]).name];
[myPhotos release];
[super viewDidLoad];
}


The first thing you did was make the database writable. The Resources folder is read-only. Saving changes requires the database to be writable, so you copied the database to your documents directory. You also modified the getAllPhotos method so that it obtained the database from the application’s document directory rather than the resources directory.

Try This: Inserting Records

  1. Add any photo from your computer to the project’s Resources group and remember the photo’s name so that you can use it later in the addThirdPhoto method.

  2. Add a new method to PhotosDAO called addPhoto. Implement the method (Listing 15).

    Listing 15. The addPhoto method
    - (void) addPhoto : (PhotoDAO *) photo {
    const char * sql = "insert into photos (name, photo) values (?, ?)";
    sqlite3_stmt *insert_statement = nil;
    NSArray * paths = NSSearchPathForDirectoriesInDomains
    (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString * docsDir = [paths objectAtIndex:0];
    NSString * thePath = [docsDir stringByAppendingPathComponent:
    @"myDatabase.sqlite"];
    sqlite3_open([thePath UTF8String], &database);
    sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL);
    sqlite3_bind_text(insert_statement, 1, [photo.name UTF8String], -1,
    SQLITE_TRANSIENT);
    NSData * binData = UIImagePNGRepresentation(photo.photo);
    sqlite3_bind_blob(insert_statement, 2, [binData bytes],
    [binData length], SQLITE_TRANSIENT);
    sqlite3_step(insert_statement);
    sqlite3_finalize(insert_statement);
    sqlite3_close(database);
    }

  3. Create a new IBAction in MyDBProjectViewController called addThirdPhoto (Listing 16).

    Listing 16. The addThirdPhoto IBAction
    - (IBAction) addThirdPhoto: (id) sender {
    static BOOL wasAdded;
    if (!wasAdded) {
    PhotosDAO * myPhotos = [[PhotosDAO alloc] init];
    PhotoDAO * aPhoto = [[PhotoDAO alloc] init];
    // Use the name of your photo in the next line
    NSString * imgPath = [[[NSBundle mainBundle] resourcePath]
    stringByAppendingPathComponent:@"photo3.png"];
    aPhoto.name = @"Another Photo";
    aPhoto.photo = [[UIImage alloc] initWithContentsOfFile:imgPath];
    [myPhotos addPhoto:aPhoto];
    [self.photos release];
    self.photos = [myPhotos getAllPhotos];
    [myPhotos release];
    wasAdded = YES;
    }
    }

  4. Save your changes and open MyDBProjectViewController.xib and add a new Bar Button item to the toolbar. Change the bar button’s title to Add.

  5. Connect the addThirdPhoto action to the Add button.

  6. Save your changes and click Run to view the application in the iPhone Simulator (Figure 4).

    Figure 4. Running the application with an add button


The addPhoto method (Listing 15) allows new photos to be inserted. To keep this example simple, the add button invokes the addThirdPhoto method that merely gets the photo from your resources group. The addPhoto method first creates a SQL string with parameters. The method then replaces the question marks by binding them to the appropriate value. For instance, the name column is text, so addPhoto binds it to a C string. The UIImage is binary, so it is bound to a blob. After binding, addPhoto then inserts the record by calling the sqlite3_step method. This method is called only once, as no data is returned from the insert statement. Notice, for brevity, an examination of the return code is omitted, as is other error handling from Listing 6 forward.

Try This: Updating Records

  1. Return to the Xcode project.

  2. Add another photo to your Resources folder and remember its name to use in the changePhotosImage method added in Step 5.

  3. Add a new NSInteger called currentID to MyDBProjectViewController.m. Change the changeImage method to update this new variable with the current photo’s id from the database (Listing 17).

    Listing 17. The currentID variable, and modified changeImage
    NSInteger currentID = 0;
    - (IBAction) changeImage: (id) sender {
    static NSInteger currentElement = 0;
    if(++currentElement == [self.photos count])
    currentElement = 0;
    PhotoDAO * aPhoto = (PhotoDAO *)
    [self.photos objectAtIndex: currentElement];
    currentID = aPhoto.photoID;
    [self.theLabel setText:aPhoto.name];
    [self.theImageView setImage:aPhoto.photo];
    }

  4. Add a new method called changeAPhotoImage to PhotosDAO (Listing 18).

    Listing 18. The changeAPhotoImage method
    - (void) changeAPhotoImage: (UIImage *) image theID: (NSInteger) photoID {
    const char * sql = "update photos set photo = ? where id = ?";
    sqlite3_stmt *update_statement = nil;
    NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
    NSUserDomainMask, YES);
    NSString * docsDir = [paths objectAtIndex:0];
    NSString * thePath =
    [docsDir stringByAppendingPathComponent:@"myDatabase.sqlite"];
    sqlite3_open([thePath UTF8String], &database);
    sqlite3_prepare_v2(database, sql, -1, &update_statement, NULL);
    NSData * binData = UIImagePNGRepresentation(image);
    sqlite3_bind_blob(update_statement, 1, [binData bytes],
    [binData length],SQLITE_TRANSIENT);
    sqlite3_bind_int(update_statement, 2, photoID);
    sqlite3_step(update_statement);
    sqlite3_finalize(update_statement);
    sqlite3_close(database);
    }



  5. Add a new IBAction called changePhotosImage to MyDBProjectViewController (Listing 19). Save your changes.

    Listing 19. The changePhotosImage method
    -(IBAction) changePhotosImage: (id) sender {
    PhotosDAO * myPhotos = [[PhotosDAO alloc] init];
    NSString * imgPath = [[[NSBundle mainBundle] resourcePath]
    stringByAppendingPathComponent:@"photo4.png"];
    [myPhotos changeAPhotoImage:[[UIImage alloc] initWithContentsOfFile:
    imgPath] theID: currentID];
    [self.photos release];
    self.photos = [myPhotos getAllPhotos];
    [myPhotos release];
    }

  6. Open MyDBProjectViewController.xib and add another bar button to the toolbar. Change the button’s title to Change.

  7. Connect the changePhotosImage action to the Change button.

  8. Save and exit Interface Builder. Click Build And Go to run the application in the iPhone Simulator (Figure 5).

    Figure 5. Changing the image


Updating a record is as straightforward as inserting it. The changeAPhotoImage first creates a SQL string with parameters. It then binds a file’s binary data to photo and an integer to id. After binding, it then calls the step function once, finalizes the statement, and closes the database. Notice that updating requires the record’s id, as SQLite uses the id to update the correct record. To accommodate this requirement, you added a currentID variable and changed the changeImage method to set the currentID with the currently selected photo record.

Try This: Deleting Records

  1. Quit the application and return to Xcode.

  2. Add a new method called deletePhoto to PhotosDAO (Listing 20).

    Listing 20. The deletePhoto method in PhotosDAO
    - (void) deletePhoto: (NSInteger) photoID {
    const char * sql = "delete from photos where id = ?";
    sqlite3_stmt *delete_statement = nil;
    NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
    NSUserDomainMask, YES);
    NSString * docsDir = [paths objectAtIndex:0];

    NSString * thePath = [docsDir stringByAppendingPathComponent:
    @"myDatabase.sqlite"];
    sqlite3_open([thePath UTF8String], &database);
    sqlite3_prepare_v2(database, sql, -1, &delete_statement, NULL);
    sqlite3_bind_int(delete_statement, 1, photoID);
    sqlite3_step(delete_statement);
    sqlite3_finalize(delete_statement);
    sqlite3_close(database);
    }



  3. Create a new IBAction called deletePhoto to MyDBProjectViewController (Listing 21).

    Listing 21. The deletePhoto IBAction in MyDBProjectViewController
    - (IBAction) deletePhoto : (id) sender {
    PhotosDAO * myPhotos = [[PhotosDAO alloc] init];
    [myPhotos deletePhoto:currentID];
    [self.photos release];
    self.photos = [myPhotos getAllPhotos];
    currentElement = 0;
    [myPhotos release];
    }

  4. Move the static NSInteger currentElement from the changeImage method in Listing 16-11 to just below the currentID variable (Listing 22). Remove the static qualifier.

    Listing 22. Placing currentElement at the class’s top so it’s shared in the class
    @implementation MyDBProjectViewController
    @synthesize photos;
    @synthesize theImageView;
    @synthesize theLabel;
    NSInteger currentID = 0;
    NSInteger currentElement = 0;

  5. Save your changes and open MyDBProjectViewController.xib.

  6. Add another button to the toolbar and change its title to Delete.

  7. Resize the image and move the label to above the toolbar, as you are running out of space on the toolbar (Figure 6).

    Figure 6. The MyDBProjectViewController view’s canvas
  8. Connect the button to the deletePhoto action.

  9. Click Run and try deleting a photo.

    The delete statement follows the same pattern as insert and update. The only real difference is the SQL string.

    const char * sql = "delete from photos where id = ?";
 
Others
 
- iOS SDK : Basic SQLite Database Manipulation (part 2) - Select
- iOS SDK : Basic SQLite Database Manipulation (part 1) - Opening the Database, Statements, Preparing Statements, and Executing Statements
- The Anatomy of a Mobile Site : PRIMARY SITE CONTENT (part 3) - Forms
- The Anatomy of a Mobile Site : PRIMARY SITE CONTENT (part 2) - Embedding Images and Media
- The Anatomy of a Mobile Site : PRIMARY SITE CONTENT (part 1) - Text, Typography& Pagination
- iPad Does Not Show Up in iTunes & Synchronization Problems
- iPad Troubleshooting : Re-register with Your iTunes Account
- XNA Game Studio 3.0 : Making a Prettier Clock with 3-D Text (part 2)
- XNA Game Studio 3.0 : Making a Prettier Clock with 3-D Text (part 1)
- Android Views (part 3) - CheckBoxes, RadioButtons, and Spinners
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
programming4us programming4us
 
Popular tags
 
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS