zoukankan      html  css  js  c++  java
  • 使用 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:@";
    ; Dump generated with SQLiteManager4iOS 
    ;
    ; By Misato (2011)
    "];
        [dump appendString:[NSString stringWithFormat:@"; database %@;
    ", [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:@"%@;
    ",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:@");
    "];
                
            }
            
        }
        
        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 不怕崩溃,可以去试试,对于我而言,不是我对其不感兴趣不愿意研究他,只是,在对性能没有要求的前提下花精力去造车轮子完全没有必要,真心话.

  • 相关阅读:
    Jenkins安装2
    11月14 vagrant 虚拟机连接
    11月13 ssh 和 netstat
    Jenkins安装
    11月12号 springboot1.5 引入redis
    11月12号 用户登录输入密码错误达到指定次数后,锁定账户 004
    js模式-观察者模式
    立即执行函数
    vue 获取数据
    在登陆退出时候使用Vuex
  • 原文地址:https://www.cnblogs.com/YouXianMing/p/3611142.html
Copyright © 2011-2022 走看看