使用 SQLiteManager 操作 sqlite3 数据库

使用 SQLiteManager 操作 sqlite3 数据库

SQLiteManager  

https://github.com/misato/SQLiteManager4iOS

      本人以前从事过嵌入式开发,后来转职为iOS开发,即使如此,也绝不想去碰C语言级别的面向过程的 sqlite3 来操作数据库,做高级语言开发还去折腾面向过程的东西,一个小小的nil没判断好就导致程序崩溃, 这就是 sqlite3 APIs 给你带来的问题,只有封装成面向对象的接口才有可能会去用.有一个封装得挺好的 FMDB 是不二的选择,但这个 SQLiteManager 属于轻量级封装,满足了最最基本的需求,但是,他是在开了ARC的情形下使用的,也就是说,没开ARC后会各种泄露......

      上面的链接可以下载源码,我修改了源码,适用于我自己,提供如下:

SQLiteManager.h

#import <Foundation/Foundation.h>

#import "sqlite3.h"





enum errorCodes {

    kDBNotExists,

    kDBFailAtOpen, 

    kDBFailAtCreate,

    kDBErrorQuery,

    kDBFailAtClose

};



@interface SQLiteManager : NSObject {



    sqlite3 *db; // The SQLite db reference

    NSString *databaseName; // The database name

}



- (id)initWithDatabaseNamed:(NSString *)name;



// SQLite Operations

- (NSError *) openDatabase;

- (NSError *) doQuery:(NSString *)sql;

- (NSError *)doUpdateQuery:(NSString *)sql withParams:(NSArray *)params;

- (NSArray *) getRowsForQuery:(NSString *)sql;

- (NSError *) closeDatabase;

- (NSInteger)getLastInsertRowID;



- (NSString *)getDatabaseDump;



@end

SQLiteManager.m

#import "SQLiteManager.h"





#define FOLDER_PATH   @"/Library/Caches/YOU_FOLDER_NAME"





// Private methods

@interface SQLiteManager (Private)



- (NSString *)getDatabasePath;

- (NSError *)createDBErrorWithDescription:(NSString*)description andCode:(int)code;



@end







@implementation SQLiteManager



#pragma mark Init & Dealloc



/**

 * Init method.

 * Use this method to initialise the object, instead of just "init".

 *

 * @param name the name of the database to manage.

 *

 * @return the SQLiteManager object initialised.

 */



- (id)initWithDatabaseNamed:(NSString *)name; {

    self = [super init];

    if (self != nil) {

        databaseName = [[NSString alloc] initWithString:name];

        db = nil;

    }

    return self;

}



#pragma mark SQLite Operations



/**

 * Open or create a SQLite3 database.

 *

 * If the db exists, then is opened and ready to use. If not exists then is created and opened.

 *

 * @return nil if everything was ok, an NSError in other case.

 *

 */



- (NSError *) openDatabase {

    

    NSError *error = nil;

    

    NSString *databasePath = [self getDatabasePath];

    NSLog(@"%@", databasePath);

    

    const char *dbpath = [databasePath UTF8String];

    int result = sqlite3_open(dbpath, &db);

    if (result != SQLITE_OK) {

        const char *errorMsg = sqlite3_errmsg(db);

        NSString *errorStr = [NSString stringWithFormat:@"The database could not be opened: %@",[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]];

        error = [self createDBErrorWithDescription:errorStr    andCode:kDBFailAtOpen];

    }

    

    return error;

}





/**

 * Does an SQL query.

 *

 * You should use this method for everything but SELECT statements.

 *

 * @param sql the sql statement.

 *

 * @return nil if everything was ok, NSError in other case.

 */



- (NSError *)doQuery:(NSString *)sql {

    

    NSError *openError = nil;

    NSError *errorQuery = nil;

    

    //Check if database is open and ready.

    if (db == nil) {

        openError = [self openDatabase];

    }

    

    if (openError == nil) {

        sqlite3_stmt *statement;

        const char *query = [sql UTF8String];

        sqlite3_prepare_v2(db, query, -1, &statement, NULL);

        

        if (sqlite3_step(statement) == SQLITE_ERROR) {

            const char *errorMsg = sqlite3_errmsg(db);

            errorQuery = [self createDBErrorWithDescription:[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]

                                                    andCode:kDBErrorQuery];

        }

        sqlite3_finalize(statement);

        errorQuery = [self closeDatabase];

    }

    else {

        errorQuery = openError;

    }

    

    return errorQuery;

}



/**

 * Does an SQL parameterized query.

 *

 * You should use this method for parameterized INSERT or UPDATE statements.

 *

 * @param sql the sql statement using ? for params.

 *

 * @param params NSArray of params type (id), in CORRECT order please.

 *

 * @return nil if everything was ok, NSError in other case.

 */



- (NSError *)doUpdateQuery:(NSString *)sql withParams:(NSArray *)params {

    

    NSError *openError = nil;

    NSError *errorQuery = nil;

    

    //Check if database is open and ready.

    if (db == nil) {

        openError = [self openDatabase];

    }

    

    if (openError == nil) {

        sqlite3_stmt *statement;

        const char *query = [sql UTF8String];

        sqlite3_prepare_v2(db, query, -1, &statement, NULL);

        

        //BIND the params!

        int count =0;

        for (id param in params ) {

            count++;

            if ([param isKindOfClass:[NSString class]] )

                sqlite3_bind_text(statement, count, [param UTF8String], -1, SQLITE_TRANSIENT);

            if ([param isKindOfClass:[NSNumber class]] ) {

                if (!strcmp([param objCType], @encode(float)))

                    sqlite3_bind_double(statement, count, [param doubleValue]);

                else if (!strcmp([param objCType], @encode(int)))

                    sqlite3_bind_int(statement, count, [param intValue]);

                else if (!strcmp([param objCType], @encode(BOOL)))

                    sqlite3_bind_int(statement, count, [param intValue]);

                else

                    NSLog(@"unknown NSNumber");

            }

            if ([param isKindOfClass:[NSDate class]]) {

               sqlite3_bind_double(statement, count, [param timeIntervalSince1970]);

            }

            if ([param isKindOfClass:[NSData class]] ) {

                sqlite3_bind_blob(statement, count, [param bytes], [param length], SQLITE_STATIC);

            }

        }

        

        if (sqlite3_step(statement) == SQLITE_ERROR) {

            const char *errorMsg = sqlite3_errmsg(db);

            errorQuery = [self createDBErrorWithDescription:[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]

                                                    andCode:kDBErrorQuery];

        }

        sqlite3_finalize(statement);

        errorQuery = [self closeDatabase];

    }

    else {

        errorQuery = openError;

    }

    

    return errorQuery;

}



- (NSInteger)getLastInsertRowID {



    NSError *openError = nil;

    

    sqlite3_int64 rowid = 0;

    

    //Check if database is open and ready.

    if (db == nil) {

        openError = [self openDatabase];

    }

    

    if (openError == nil) {

        rowid = sqlite3_last_insert_rowid(db);

    }

    

    return (NSInteger)rowid;

}



/**

 * Does a SELECT query and gets the info from the db.

 *

 * The return array contains an NSDictionary for row, made as: key=columName value= columnValue.

 *

 * For example, if we have a table named "users" containing:

 * name | pass

 * -------------

 * admin| 1234

 * pepe | 5678

 *

 * it will return an array with 2 objects:

 * resultingArray[0] = name=admin, pass=1234;

 * resultingArray[1] = name=pepe, pass=5678;

 *

 * So to get the admin password:

 * [[resultingArray objectAtIndex:0] objectForKey:@"pass"];

 *

 * @param sql the sql query (remember to use only a SELECT statement!).

 *

 * @return an array containing the rows fetched.

 */



- (NSArray *)getRowsForQuery:(NSString *)sql {

    

    NSMutableArray *resultsArray = [[NSMutableArray alloc] initWithCapacity:1];

    

    if (db == nil) {

        [self openDatabase];

    }

    

    sqlite3_stmt *statement;

    const char *query = [sql UTF8String];

    int returnCode = sqlite3_prepare_v2(db, query, -1, &statement, NULL);

    

    if (returnCode == SQLITE_ERROR) {

        const char *errorMsg = sqlite3_errmsg(db);

        NSError *errorQuery = [self createDBErrorWithDescription:[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]

                                                         andCode:kDBErrorQuery];

        NSLog(@"%@", errorQuery);

    }

    

    while (sqlite3_step(statement) == SQLITE_ROW) {

        int columns = sqlite3_column_count(statement);

        NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:columns];

        

        for (int i = 0; i<columns; i++) {

            const char *name = sqlite3_column_name(statement, i);

            

            NSString *columnName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];

            

            int type = sqlite3_column_type(statement, i);

            

            switch (type) {

                case SQLITE_INTEGER:

                {

                    int value = sqlite3_column_int(statement, i);

                    [result setObject:[NSNumber numberWithInt:value] forKey:columnName];

                    break;

                }

                case SQLITE_FLOAT:

                {

                    float value = sqlite3_column_double(statement, i);

                    [result setObject:[NSNumber numberWithFloat:value] forKey:columnName];

                    break;

                }

                case SQLITE_TEXT:

                {

                    const char *value = (const char*)sqlite3_column_text(statement, i);

                    [result setObject:[NSString stringWithCString:value encoding:NSUTF8StringEncoding] forKey:columnName];

                    break;

                }

                    

                case SQLITE_BLOB:

                {

                    int bytes = sqlite3_column_bytes(statement, i);

                    if (bytes > 0) {

                        const void *blob = sqlite3_column_blob(statement, i);

                        if (blob != NULL) {

                            [result setObject:[NSData dataWithBytes:blob length:bytes] forKey:columnName];

                        }

                    }

                    break;

                }

                    

                case SQLITE_NULL:

                    [result setObject:[NSNull null] forKey:columnName];

                    break;

                    

                default:

                {

                    const char *value = (const char *)sqlite3_column_text(statement, i);

                    [result setObject:[NSString stringWithCString:value encoding:NSUTF8StringEncoding] forKey:columnName];

                    break;

                }

                    

            } //end switch

            

            

        } //end for

        

        [resultsArray addObject:result];

        

    } //end while

    sqlite3_finalize(statement);

    

    [self closeDatabase];

    

    return resultsArray;

    

}





/**

 * Closes the database.

 *

 * @return nil if everything was ok, NSError in other case.

 */



- (NSError *) closeDatabase {

    

    NSError *error = nil;

    

    

    if (db != nil) {

        if (sqlite3_close(db) != SQLITE_OK){

            const char *errorMsg = sqlite3_errmsg(db);

            NSString *errorStr = [NSString stringWithFormat:@"The database could not be closed: %@",[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]];

            error = [self createDBErrorWithDescription:errorStr andCode:kDBFailAtClose];

        }

        

        db = nil;

    }

    

    return error;

}





/**

 * Creates an SQL dump of the database.

 *

 * This method could get a csv format dump with a few changes.

 * But i prefer working with sql dumps ;)

 *

 * @return an NSString containing the dump.

 */



- (NSString *)getDatabaseDump {

    

    NSMutableString *dump = [[NSMutableString alloc] initWithCapacity:256];

    

    // info string ;) please do not remove it

    [dump appendString:@";\n; Dump generated with SQLiteManager4iOS \n;\n; By Misato (2011)\n"];

    [dump appendString:[NSString stringWithFormat:@"; database %@;\n", [databaseName lastPathComponent]]];

    

    // first get all table information

    

    NSArray *rows = [self getRowsForQuery:@"SELECT * FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';"];

    // last sql query returns something like:

    // {

    // name = users;

    // rootpage = 2;

    // sql = "CREATE TABLE users (id integer primary key autoincrement, user text, password text)";

    // "tbl_name" = users;

    // type = table;

    // }

    

    //loop through all tables

    for (int i = 0; i<[rows count]; i++) {

        

        NSDictionary *obj = [rows objectAtIndex:i];

        //get sql "create table" sentence

        NSString *sql = [obj objectForKey:@"sql"];

        [dump appendString:[NSString stringWithFormat:@"%@;\n",sql]];

        

        //get table name

        NSString *tableName = [obj objectForKey:@"name"];

        

        //get all table content

        NSArray *tableContent = [self getRowsForQuery:[NSString stringWithFormat:@"SELECT * FROM %@",tableName]];

        

        for (int j = 0; j<[tableContent count]; j++) {

            NSDictionary *item = [tableContent objectAtIndex:j];

            

            //keys are column names

            NSArray *keys = [item allKeys];

            

            //values are column values

            NSArray *values = [item allValues];

            

            //start constructing insert statement for this item

            [dump appendString:[NSString stringWithFormat:@"insert into %@ (",tableName]];

            

            //loop through all keys (aka column names)

            NSEnumerator *enumerator = [keys objectEnumerator];

            id obj;

            while (obj = [enumerator nextObject]) {

                [dump appendString:[NSString stringWithFormat:@"%@,",obj]];

            }

            

            //delete last comma

            NSRange range;

            range.length = 1;

            range.location = [dump length]-1;

            [dump deleteCharactersInRange:range];

            [dump appendString:@") values ("];

            

            // loop through all values

            // value types could be:

            // NSNumber for integer and floats, NSNull for null or NSString for text.

            

            enumerator = [values objectEnumerator];

            while (obj = [enumerator nextObject]) {

                //if it's a number (integer or float)

                if ([obj isKindOfClass:[NSNumber class]]){

                    [dump appendString:[NSString stringWithFormat:@"%@,",[obj stringValue]]];

                }

                //if it's a null

                else if ([obj isKindOfClass:[NSNull class]]){

                    [dump appendString:@"null,"];

                }

                //else is a string ;)

                else{

                    [dump appendString:[NSString stringWithFormat:@"'%@',",obj]];

                }

                

            }

            

            //delete last comma again

            range.length = 1;

            range.location = [dump length]-1;

            [dump deleteCharactersInRange:range];

            

            //finish our insert statement

            [dump appendString:@");\n"];

            

        }

        

    }

    

    return dump;

}



@end





#pragma mark -

@implementation SQLiteManager (Private)



/**

 * Gets the database file path (in NSDocumentDirectory).

 *

 * @return the path to the db file.

 */



- (NSString *)getDatabasePath{

    

    if([[NSFileManager defaultManager] fileExistsAtPath:databaseName]){

        // Already Full Path

        return databaseName;

    } else {

        // 判断文件夹是否存在,不存在则创建文件夹

        NSString *docsDir = [NSHomeDirectory() stringByAppendingString:FOLDER_PATH];

        if (SQLiteManager_fileOrFolderExistFromSandbox(docsDir) == NO) {

            SQLiteManager_createFolderForSandbox(docsDir);

        }

        

        return [docsDir stringByAppendingPathComponent:databaseName];

    }

}



#pragma mark 沙盒中创建文件夹

BOOL SQLiteManager_createFolderForSandbox(NSString *filePath)

{

    return [[NSFileManager defaultManager] createDirectoryAtPath:filePath

                                     withIntermediateDirectories:YES attributes:nil error:nil];

}



#pragma mark 文件或者文件夹是否存在

BOOL SQLiteManager_fileOrFolderExistFromSandbox(NSString *filePath)

{

    return [[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:NO];

}



/**

 * Creates an NSError.

 *

 * @param description the description wich can be queried with [error localizedDescription];

 * @param code the error code (code erors are defined as enum in the header file).

 *

 * @return the NSError just created.

 *

 */



- (NSError *)createDBErrorWithDescription:(NSString*)description andCode:(int)code {

    

    NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:description, NSLocalizedDescriptionKey, nil];

    NSError *error = [NSError errorWithDomain:@"SQLite Error" code:code userInfo:userInfo];

    

    return error;

}



@end


以下是增删改查以及常规操作:

打开数据库

    //打开数据库

    SQLiteManager *dbManager = [[SQLiteManager alloc] initWithDatabaseNamed:@"Y.X.db"];

创建表

    NSError *error = nil;

    //创建表

    /*

     id        int    主键

     user      text

     password  text

     */

    error = [dbManager doQuery:@"CREATE TABLE IF NOT EXISTS users (id integer primary key autoincrement, user text, password text);"];

    if (error != nil) {

        NSLog(@"Error: %@",[error localizedDescription]);

    }

插入

    //在表中插入一条记录

    error =  [dbManager doQuery:@"insert into users (user, password) values ('YouXian','19871220');"];

    if (error != nil) {

        NSLog(@"Error: %@",[error localizedDescription]);

    }

查询

    //查询记录

    [[dbManager getRowsForQuery:@"SELECT * FROM users"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

        NSLog(@"%@", obj);

    }];

修改

    //修改一条记录

    error =  [dbManager doQuery:@"update users set user = 'YX',password = '1' where id = 1;"];

    if (error != nil) {

        NSLog(@"Error: %@",[error localizedDescription]);

    }

删除

    //删除一条记录

    error =  [dbManager doQuery:@"delete from users where id = 1;"];

    if (error != nil) {

        NSLog(@"Error: %@",[error localizedDescription]);

    }

 

心得:

      如果觉得自己想去捣鼓 C 语言级别的 API 不怕崩溃,可以去试试,对于我而言,不是我对其不感兴趣不愿意研究他,只是,在对性能没有要求的前提下花精力去造车轮子完全没有必要,真心话.

 

 

 

 

 

 

 

你可能感兴趣的:(sqlite3)