IT tutorials
 
Mobile
 

iOS SDK : Basic SQLite Database Manipulation (part 2) - Select

11/12/2011 4:12:19 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

Select

You select one or more records from a SQL database using a select statement. Because a select statement usually returns multiple rows, you must loop through the row set if you wish to obtain all records.

while (sqlite3_step(statement) == SQLITE_ROW){
//process row here
}

Obtaining SQLite Column Values

You obtain column values through a method in Listing 16-5. Using these methods will become more apparent after the next task.

Listing 5. Methods for obtaining column data (from SQLite online documentation)
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
int sqlite3_column_type(sqlite3_stmt*, int iCol);
sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);

Note

The int iCol arguments in the methods in Listing 16-5 are a zero-based index into the columns in the results of the sqlite3_stmt, not an index into the columns of a SQLite database table.


Try This: Opening and Querying a Database

  1. Return to your MyDBProject in Xcode.

  2. In Classes, create a new group called Model.

  3. Create a new Objective-C class in the Model group called PhotosDAO. Create another Objective-C class in the same group called PhotoDAO.

  4. Add a name, photoID, and photo property to PhotoDAO.h and PhotoDAO.m (Listings 6 and 7).

    Listing 6. PhotoDAO.h
    #import <Foundation/Foundation.h>
    @interface PhotoDAO : NSObject {
    NSString * name;
    NSInteger photoID;
    UIImage * photo;
    }
    @property (nonatomic, retain) NSString * name;
    @property (nonatomic, assign) NSInteger photoID;
    @property (nonatomic, retain) UIImage * photo;
    @end

    Listing 7. PhotoDAO.m
    #import "PhotoDAO.h"
    @implementation PhotoDAO
    @synthesize name;
    @synthesize photoID;
    @synthesize photo;
    - (void) dealloc {
    [name release];
    [photo release];
    [super dealloc];
    }
    @end

  5. Open PhotosDAO.h and import SQLite3. Add a reference to the database you will use (Listing 8).

    Listing 8. PhotosDAO.h
    #import <Foundation/Foundation.h>
    #import <sqlite3.h>
    @interface PhotosDAO : NSObject {
    sqlite3 *database;
    }
    - (NSMutableArray *) getAllPhotos;
    @end

  6. Add a getAllPhotos method to PhotosDAO and implement the method (Listing 9).

    Listing 9. PhotosDAO.m
    #import "PhotosDAO.h"
    #import "PhotoDAO.h"
    @implementation PhotosDAO
    - (NSMutableArray *) getAllPhotos {
    NSMutableArray * photosArray = [[NSMutableArray alloc] init];
    @try {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *theDBPath = [[[NSBundle mainBundle] resourcePath]
    stringByAppendingPathComponent:@"myDatabase.sqlite"];
    BOOL success = [fileManager fileExistsAtPath:theDBPath];
    if (!success) {
    NSLog(@"Failed to find database file '%@'.", theDBPath);
    }
    if (!(sqlite3_open([theDBPath UTF8String], &database) == SQLITE_OK)) {
    NSLog(@"An error opening database, normally 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];
    [aPhoto release];
    }
    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, normally error handling here.");
    }
    } @catch (NSException *e) {
    NSLog(@"An exception occurred: %@", [e reason]);
    return nil;
    }
    return photosArray;
    }
    @end



  7. Open MyDBProjectViewController.h and add an NSMutableArray property to hold the photos. Add an IBOutlet for a UIImageView. Add a UILabel named theLabel, add an IBAction, and name the method changeImage (Listing 10).

    Listing 10. MyDBProjectViewController.h
    #import <UIKit/UIKit.h>
    @interface MyDBProjectViewController : UIViewController {
    NSMutableArray * photos;
    UIImageView * theImageView;
    UILabel * theLabel;
    }
    @property (nonatomic, retain) NSMutableArray * photos;
    @property (nonatomic, retain) IBOutlet UIImageView * theImageView;
    @property (nonatomic, retain) IBOutlet UILabel * theLabel;
    - (IBAction) changeImage: (id) sender;
    @end

  8. Open MyDBProjectViewController.m and synthesize photos and theImageView (Listing 11).

    Listing 11. MyDBProjectViewController.m
    #import "MyDBProjectViewController.h"
    #import "PhotoDAO.h";
    #import "PhotosDAO.h";
    @implementation MyDBProjectViewController
    @synthesize photos;
    @synthesize theImageView;
    @synthesize theLabel;
    - (void)viewDidLoad {
    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];
    }
    - (IBAction) changeImage: (id) sender {
    static NSInteger currentElement = 0;
    if(++currentElement == [self.photos count]) currentElement = 0;
    PhotoDAO * aPhoto =
    (PhotoDAO *) [self.photos objectAtIndex: currentElement];
    [self.theLabel setText:aPhoto.name];
    [self.theImageView setImage:aPhoto.photo];
    }
    - (void)dealloc {
    [photos release];
    [theImageView release];
    [theLabel release];
    [super dealloc];
    }
    @end



  9. Implement the viewDidLoad and changeImage methods so that they match Listing 11.

  10. Save your changes and open MyDBProjectViewController.xib. Add a toolbar, a label, and a UIImageView (Figure 1). Change the button’s title to Next. Remove the text from the label.

    Figure 1. Adding a UIImageView and a UIToolBar to the view’s canvas
  11. Connect the File’s Owner theLabel outlet to the label added to the toolbar. Connect the theImageView outlet to the UIImageView. Connect the changeImage action to the Next button. Save your changes.

  12. Run the application in iPhone Simulator, as shown in Figures 2 and 3.

    Figure 2. Running the application (first image)

    Figure 3. Running the application (second image)


Note

You would normally never load an entire database at once in a real application, especially when using large blobs, like this example. Memory is limited in an iOS device—only load what you need when you need it.


The Model-View-Controller

When writing a program for any platform, you should adhere to the MVC design pattern as closely as possible. Rather than placing the database logic in a view or view controller, you created separate classes, insulating the view and controller layers from the database layer. The MyDBProjectViewController knows nothing about the underlying SQLite3 library; the view controller only knows about PhotosDAO and PhotoDAO. Notice you further separated the code by placing it in its own group, Model, under Classes. All this separation makes debugging and maintaining the program easier.

Opening the Database

To keep the task’s length manageable and focused, rather than creating several data access methods in PhotosDAO, you only created one.

- (NSMutableArray *) getAllPhotos;

This method returns an array of PhotoDAO objects. The getAllPhotos method first finds the database and opens it. Because the database is in the resources folder, you can access it directly using the bundle’s resourcePath. (When you want to create an application that uses canned [predefined] data, this task illustrated how to create that data in advance [using SQLite Manager in Firefox] and then embed it in your application.)

NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *theDBPath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent: @"myDatabase.sqlite"];

After obtaining the database’s path, you open it.

if (!(sqlite3_open([theDBPath UTF8String], &database) == SQLITE_OK))

Notice that you obtain the UTF8String from the NSString before passing the sqlite3_open method the path. Since opening the database is a common activity, you might want to move that portion of the code into its own method for easy reuse.

Querying the Data

After opening the database, you query it for the photo records. If you have ever worked with a database using code, for instance, Java Database Connectivity (JDBC), then this code should look familiar. The getAllPhotos method first creates the SQL select string. Next, the method places the string in a statement and then queries the database. After obtaining the data, getAllPhotos loops through each record.

For each new record, getAllPhotos creates a new PhotoDAO. The newly created PhotoDAO object’s values are then set to the appropriate values from the current record. After initializing the PhotoDAO object, getAllPhotos places the object into PhotosDAO’s photosArray.

Loading a Blob into NSData

This code snippet is useful. It shows you a quick, easy way to load a blob, any blob, into an NSData object. First, load the blob into a C string.

const char * rawData = sqlite3_column_blob(statement, 2);

Second, obtain the blob’s byte size.

int rawDataLength = sqlite3_column_bytes(statement, 2);

Third, create an NSData class using the C string and size variables.

NSData *data = [NSData dataWithBytes:rawData length:rawDataLength];

As you already know the database blob is an image, you initialize the PhotoDAO’s photo property using the UIImage’s initWithData method.

aPhoto.photo = [[UIImage alloc] initWithData:data];

This same technique works for other binary data as well (replacing UIImage with the appropriate class).

Closing the Database

When finished using a statement, you release its resources by finalizing the statement.

if(sqlite3_finalize(statement) != SQLITE_OK)

After you no longer need the database, you close it.

if (sqlite3_close(database) != SQLITE_OK)

Selecting all records only has limited value. Rarely will you use SQL statements where you do not wish to limit the results returned. For this, you typically add parameters to your SQL statements and then replace the parameters with values in your program. This is called binding your program’s values to the statements’ parameters. Programs usually also allow more than simply selecting data; most applications allow users to add, edit, and delete records.

 
Others
 
- 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
- Android Views (part 2) - Button and ImageButton
 
25 Inspiring Game of Thrones Quotes
 
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