SQLite3以及FMDB的简单实用

本文详细介绍iOS中SQLite数据库的基本操作及FMDB框架的应用。包括数据库的创建、表的建立、查询、更新等核心功能,并演示如何使用FMDB简化SQLite数据库操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文来自简书,原文地址:http://www.jianshu.com/p/897733b577f3
SQLite

SQLite是一款轻型的嵌入式数据库,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。同时它的处理速度也是非常快速的。

  1. 因为在iOS中SQLite3依赖于系统提供的libsqlite.tbd这个类库,所以在使用之前首先我们需要导入这个类库;
  2. 打开数据库,打开数据库成功之后进行创表操作;

    // 数据库在沙盒中的存放路径;
     NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"shops.db"];
    
     //1.0 打开数据库;
     /* 参数一:数据库文件存放路径;如果数据库文件不存在, 系统会自动创建文件并且自动初始化数据库
        参数二:数据库实例对象;
      */
     if (sqlite3_open(filename.UTF8String, &_db) == SQLITE_OK) {
    
         const char *sql = "CREATE TABLE IF NOT EXISTS t_shop (id integer PRIMARY KEY, name text NOT NULL, price real);";
    
         //2.0 执行操作;
         char *errmsg = NULL;
         /* 参数一:数据库实例对象;
            参数二:执行的SQL语句;
          */
    
         if (sqlite3_exec(self.db, sql, NULL, NULL, &errmsg)!= SQLITE_OK) {
    
              NSLog(@"创表失败--%s", errmsg);
         }
     } else {
         NSLog(@"打开数据库失败");
     }

    在上面的代码中,我们做了两个操作,首先打开数据库我们使用了sqlite3_open这个方法,该方法根据文件路径打开数据库,如果不存在,则会创建一个新的数据库。如果result等于常量SQLITE_OK,则表示成功打开数据库。该方法有两个参数,如下:

    int sqlite3_open(
     const char *filename,   // 数据库的文件路径
     sqlite3 **ppDb          // 数据库实例
    );

    其次,我们使用sqlite3_exec方法来执行建表操作。该方法适合于查询结果集之外的任何操作。

    2.执行任何SQL语句
    int sqlite3_exec(
     sqlite3*,                                  // 一个打开的数据库实例
     const char *sql,                           // 需要执行的SQL语句
     int (*callback)(void*,int,char**,char**),  // SQL语句执行完毕后的回调
     void *,                                    // 回调函数的第1个参数
     char **errmsg                              // 错误信息
    );
  3. 执行查询操作;

    NSString * sql=[NSString stringWithFormat:@"SELECT name,price FROM %@;",tableName];
    
     // sqlite3_stmt实例,用来获得数据库数据
     sqlite3_stmt *stmt = NULL;
    
     // 预准备,其实就是分析SQL语句的合法性
     if (sqlite3_prepare_v2(self.db, sql.UTF8String, -1, &stmt, NULL) == SQLITE_OK) {
         // 返回SQLITE_ROW代表成功取出一条数据
         while (sqlite3_step(stmt) == SQLITE_ROW) {
    
             const char *name = (const char *)sqlite3_column_text(stmt, 0);
    
             const char *price = (const char *)sqlite3_column_text(stmt, 1);
         }
     }

    上面用到了两个方法:

    3.检查SQL语句的合法性(查询前的准备)
    int sqlite3_prepare_v2(
     sqlite3 *db,            // 数据库实例
     const char *zSql,       // 需要检查的SQL语句
     int nByte,              // SQL语句的最大字节长度,-1代表系统会帮我们自动计算
     sqlite3_stmt **ppStmt,  // sqlite3_stmt实例,用来获得数据库数据
     const char **pzTail
    );
    4.查询一行数据
    int sqlite3_step(sqlite3_stmt*); // 如果查询到一行数据,就会返回SQLITE_ROW

    查询分三个阶段:
    准备阶段:sqlite3_prepare_v2()
    执行阶段:sqlte3_step()
    终止阶段:sqlite3_finalize()

  4. 使用约束变量
    sqlite3_bind是用来给sqlite3_stmt *pStmt语句增加值的,对于不同类型的参数要选用不同的函数。
    例如,要执行带两个约束变量的插入操作,第一个变量是int类型,第二个是C字符串:
    char *sql = "insert into oneTable values (?, ?);";
    sqlite3_stmt *stmt;
    if (sqlite3_prepare_v2(database, sql, -1, &stmt, nil) == SQLITE_OK) {
     sqlite3_bind_int(stmt, 1, 235);
     sqlite3_bind_text(stmt, 2, "valueString", -1, NULL);
    }
    if (sqlite3_step(stmt) != SQLITE_DONE)
    NSLog(@"Something is Wrong!");
    sqlite3_finalize(stmt);
    这里,sqlite3_bind_int(stmt, 1, 235);有三个参数:
    第一个是sqlite3_stmt类型的变量
    第二个是所约束变量的标签index,(从1开始)
    第三个参数是要加的值
    有一些函数多出两个变量,例如
    sqlite3_bind_text(stmt, 2, "valueString", -1, NULL);
    第四个参数代表第三个参数中需要传递的长度。对于C字符串来说,-1表示传递全部字符串。
    第五个参数是一个回调函数,比如执行后做内存清除工作。
  5. 利用stmt获得某一字段的值(字段的下标从0开始)

    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 void *sqlite3_column_blob(sqlite3_stmt*, int iCol); // 二进制文本数据
    const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);  // 字符串数据

    因为获取某一列的值我们不知道具体的类型,所以我们可以遍历字段,然后根据类型自动匹配:

     //存放最终的返回结果;
     NSMutableArray * muArray=[NSMutableArray array];
    
     //2.0 执行预处理 获取结果,如果下一行有记录,就会自动返回SQLITE_ROW,同时会自动将指针移动到下一行;
     while (sqlite3_step(ppStmt) == SQLITE_ROW) {
    
         //存放当前行的数据;
         NSMutableDictionary * mnDic=[NSMutableDictionary dictionary];
         [muArray addObject:mnDic];
    
         //获取当前行 中 列的个数;
         int columnCount= sqlite3_column_count(ppStmt);
    
         for(int i=0;i<columnCount;i++ )
         {
    
          //获取列名称;
          NSString * columnName=[NSString stringWithUTF8String:sqlite3_column_name(ppStmt, i)];
    
             //获取列的类型
           int columnType= sqlite3_column_type(ppStmt, i);
    
             //匹配类型;
             id value;
             //#define SQLITE_INTEGER  1
             //#define SQLITE_FLOAT    2
             //#define SQLITE_BLOB     4
             //#define SQLITE_NULL     5
             //#define SQLITE3_TEXT    3
             switch (columnType) {
                 case SQLITE_INTEGER:
                 {
                     value= @(sqlite3_column_int(ppStmt, i));
                     break;
                 }
                 case SQLITE_FLOAT:
                 {
                     value= @(sqlite3_column_double(ppStmt, i));
                     break;
                 }
                 case SQLITE_BLOB:
                 {
                     value= CFBridgingRelease(sqlite3_column_blob(ppStmt, i));
                     break;
                 }
                 case SQLITE_NULL:
                 {
                     value= @"";
                     break;
                 }
                  case SQLITE3_TEXT:
                 {
                     const char * columnValue=(const char *)sqlite3_column_text(ppStmt, i);
                     value=[NSString stringWithUTF8String:columnValue];
                     break;
                 }
    
                 default:
                     break;
             }
    
             [mnDic setValue:value forKeyPath:columnName];
         }
     }
    
     //3.0 关闭数据库
     sqlite3_finalize(ppStmt);//销毁准备语句,防止内存泄漏;
     [self closeDBWithUID:uid];
     return  muArray;
FMDB

FMDB是iOS平台的SQLite数据库框架,它以OC的方式封装了SQLite的C语言API,使用起来更加面向对象,省去了很多麻烦的C语言代码,对比苹果自带的Core Data框架,更加轻量级和灵活,提供了多线程安全的数据库操作方法,有效地防止数据混乱。
FMDB有三个主要的类:
FMDatabase:一个FMDatabase对象就代表一个单独的SQLite数据库
FMResultSet:使用FMDatabase执行查询后的结果集
FMDatabaseQueue:用于在多线程中执行多个查询或更新,它是线程安全的
在FMDB中,除查询以外的所有操作,都称为“更新”
create、drop、insert、update、delete

更新操作
使用executeUpdate:方法执行更新:

- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments

查询操作

- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments

例如:

// 查询数据
FMResultSet *result = [_db executeQuery:@"SELECT * FROM t_student"];

// 遍历结果集
while ([result next]) {
    NSString *name = [rs stringForColumn:@"name"];
    int age = [rs intForColumn:@"age"];
    double score = [rs doubleForColumn:@"score"];
}

FMDatabaseQueue
FMDatabase这个类是线程不安全的,如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题,为了保证线程安全,FMDB提供了非常方便快捷的FMDatabaseQueue类:

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
[queue inDatabase:^(FMDatabase *db) {
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];

    FMResultSet *rs = [db executeQuery:@"select * from t_student"];
    while ([rs next]) {
        // …
    }
}];

使用事物:

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];

    FMResultSet *result = [db executeQuery:@"select * from t_student"];
    while ([result next]) {
        // …
    }
}];
#import <Foundation/Foundation.h>

@interface StatusTool : NSObject
/**
 *  根据请求参数去沙盒中加载缓存的微博数据
 *
 *  @param params 请求参数
 */
+ (NSArray *)statusesWithParams:(NSDictionary *)params;

/**
 *  存储微博数据到沙盒中
 *
 *  @param statuses 需要存储的微博数据
 */
+ (void)saveStatuses:(NSArray *)statuses;
@end

具体实现:

#import "StatusTool.h"
#import "FMDB.h"

@implementation StatusTool

static FMDatabase *_db;
+ (void)initialize
{
    // 1.通过指定SQLite数据库文件路径来创建FMDatabase对象
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"Statuses.db"];
    _db = [FMDatabase databaseWithPath:path];

    //2.0 打开数据库
    [_db open];

    // 3.0创表
    [_db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_status (id integer PRIMARY KEY, status blob NOT NULL, idstr text NOT NULL);"];
}

+ (NSArray *)statusesWithParams:(NSDictionary *)params
{
    // 根据请求参数生成对应的查询SQL语句
    NSString *sql = nil;
    if (params[@"since_id"]) {
        sql = [NSString stringWithFormat:@"SELECT * FROM t_status WHERE idstr > %@ ORDER BY idstr DESC LIMIT 20;", params[@"since_id"]];
    } else if (params[@"max_id"]) {
        sql = [NSString stringWithFormat:@"SELECT * FROM t_status WHERE idstr <= %@ ORDER BY idstr DESC LIMIT 20;", params[@"max_id"]];
    } else {
        sql = @"SELECT * FROM t_status ORDER BY idstr DESC LIMIT 20;";
    }

    // 执行SQL
    FMResultSet *set = [_db executeQuery:sql];
    NSMutableArray *statuses = [NSMutableArray array];
    while (set.next) {
        //可以根据columnName来获取数据,跟不用根据列号;
        NSData *statusData = [set objectForColumnName:@"status"];
        NSDictionary *status = [NSKeyedUnarchiver unarchiveObjectWithData:statusData];
        [statuses addObject:status];
    }
    return statuses;
}

+ (void)saveStatuses:(NSArray *)statuses
{
    // 要将一个对象存进数据库的blob字段,最好先转为NSData,一个对象要遵守NSCoding协议,实现协议中相应的方法,才能转成NSData
    for (NSDictionary *status in statuses) {
        // NSDictionary --> NSData
        NSData *statusData = [NSKeyedArchiver archivedDataWithRootObject:status];
        [_db executeUpdateWithFormat:@"INSERT INTO t_status(status, idstr) VALUES (%@, %@);", statusData, status[@"idstr"]];

    }
}
@end
FMDB的封装,使你的实体类具备数据库操作的功能,极大简化你的数据库操作,对于自己的扩展也非常简单。 该框架是本人在项目中用到的对FMDB的封装,它的特点如下: 1.自动创建数据库、自动创建数据库表。 2.自动检测字段添加新字段。 3.一行代码实现数据库的CURD操作。 4.源码及其简单,易于理解掌握。 5.扩展自己的功能也非常得简单,容易。 6,只关心字段字段值,完全不用关心数据库操作逻辑. 特别提示:字段值全部以字符串的形式统一处理 常用的api如下: /** 获取数据库单例函数 */ (instancetype)intance; /** 数据库中是否存在表 */ - (BOOL)isExistWithTableName:(NSString*)name; /** 默认建立主键id 创建表(如果存在则不创建) , keys 数据存放要求@[字段名称1,字段名称2] */ -(BOOL)createTableWithTableName:(NSString*)name keys:(NSArray*)keys; /** 插入 只关心keyvalue @{key:value,key:value} */ -(BOOL)insertIntoTableName:(NSString*)name Dict:(NSDictionary*)dict; /** 根据条件查询字段 返回的数组是字典( @[@{key:value},@{key:value}] ) ,where形式 @[@"key",@"=",@"value",@"key",@">=",@"value"] */ -(NSArray*)queryWithTableName:(NSString*)name keys:(NSArray*)keys where:(NSArray*)where; /** 全部查询 返回的数组是字典( @[@{key:value},@{key:value}] ) */ -(NSArray*)queryWithTableName:(NSString*)name; /** 根据key更新value 形式 @[@"key",@"=",@"value",@"key",@">=",@"value"] */ -(BOOL)updateWithTableName:(NSString*)name valueDict:(NSDictionary*)valueDict where:(NSArray*)where; /** 根据表名表字段删除表内容 where形式 @[@"key",@"=",@"value",@"key",@">=",@"value"] */ -(BOOL)deleteWithTableName:(NSString*)name where:(NSArray*)where; /** 根据表名删除表格全部内容 */ -(BOOL)clearTable:(NSString*)name; /** 删除表 */ -(BOOL)dropTable:(NSString*)name;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值