使用iOS原生sqlite3框架对sqlite数据库进行操作

简介:

使用iOS原生sqlite3框架对sqlite数据库进行操作

一、引言

      sqlite数据库是一种小型数据库,由于其小巧与简洁,在移动开发领域应用深广,sqlite数据库有一套完备的sqlite语句进行管理操作,一些常用的语句和可视化的开发工具在上篇博客中有介绍,地址如下:

sqlite数据库常用语句及可视化工具介绍:http://my.oschina.net/u/2340880/blog/600820

      在iOS的原生开发框架中可以对sqlite数据库进行很好的支持,这个框架中采用C风格且通过指针移动进行数据的操作,使用起来有些不便,我们可以对一些数据库的常用操作进行一些面向对象的封装。

二、libsqlite3系统库中操作数据库的常用方法

    libsqlite3是对sqlite数据库进行操作的系统库,在使用前,我们需要先导入,点击Xcode的Build Phases标签,展开Link Binary With Libraries,点击+号,在弹出的窗口中搜索libsqlite3.0,将其导入进工程,过程如下图:

在需要操作sqlite数据的文件中导入如下头文件:

?
1
#import <sqlite3.h>

数据库文件的操作是由一个sqlite3类型的指针操作管理的,如下方法进行数据库的打开:

?
1
2
sqlite3 *sqlite;
sqlite3_open(dataBaePath, &sqlite)

sqlite3_open方法返回一个int值,实际上,在使用libsqlite3框架中的大多方法时都会返回一个int值,这个int值代表着方法执行的相应结果状态,这些状态再sqlite3.h文件中通过宏来定义,列举如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define SQLITE_OK           0   //操作成功
/* 以下是错误代码 */
#define SQLITE_ERROR        1   /* SQL数据库错误或者丢失*/
#define SQLITE_INTERNAL     2   /* SQL内部逻辑错误 */
#define SQLITE_PERM         3   /* 没有访问权限 */
#define SQLITE_ABORT        4   /* 回调请求终止 */
#define SQLITE_BUSY         5   /* 数据库文件被锁定 */
#define SQLITE_LOCKED       6   /* 数据库中有表被锁定 */
#define SQLITE_NOMEM        7   /* 分配空间失败 */
#define SQLITE_READONLY     8   /* 企图向只读属性的数据库中做写操作 */
#define SQLITE_INTERRUPT    9   /* 通过sqlite3_interrupt()方法终止操作*/
#define SQLITE_IOERR       10   /* 磁盘发生错误 */
#define SQLITE_CORRUPT     11   /* 数据库磁盘格式不正确 */
#define SQLITE_NOTFOUND    12   /* 调用位置操作码 */
#define SQLITE_FULL        13   /* 由于数据库已满造成的添加数据失败 */
#define SQLITE_CANTOPEN    14   /* 不法打开数据库文件 */
#define SQLITE_PROTOCOL    15   /* 数据库锁协议错误 */
#define SQLITE_EMPTY       16   /* 数据库为空 */
#define SQLITE_SCHEMA      17   /* 数据库模式更改 */
#define SQLITE_TOOBIG      18   /* 字符或者二进制数据超出长度 */
#define SQLITE_CONSTRAINT  19   /* 违反协议终止 */
#define SQLITE_MISMATCH    20   /* 数据类型不匹配 */
#define SQLITE_MISUSE      21   /* 库使用不当 */
#define SQLITE_NOLFS       22   /* 使用不支持的操作系统 */
#define SQLITE_AUTH        23   /* 授权拒绝 */
#define SQLITE_FORMAT      24   /* 辅助数据库格式错误 */
#define SQLITE_RANGE       25   /* sqlite3_bind 第二个参数超出范围 */
#define SQLITE_NOTADB      26   /* 打开不是数据库的文件 */
#define SQLITE_NOTICE      27   /* 来自sqlite3_log()的通知 */
#define SQLITE_WARNING     28   /* 来自sqlite3_log() 的警告*/
#define SQLITE_ROW         100  /* sqlite3_step() 方法准备好了一行数据 */
#define SQLITE_DONE        101  /* sqlite3_step() 已完成执行*/

执行非查询类的语句,例如创建,添加,删除等操作,使用如下方法:

?
1
2
3
char  * err;
sqlite3 *sql;
sqlite3_exec(sql, sqlStr, NULL, NULL, &err);

sqlite3_exec方法中第一个参数为成功执行了打开数据库操作的sqlite3指针,第二个参数为要执行的sql语句,最后一个参数为错误信息字符串。

执行查询语句的方法比较复杂,通过如下方法:

?
1
2
3
4
5
6
7
8
9
     sqlite3 * sqlite;
     sqlite3_stmt *stmt =nil;
     int  code = sqlite3_prepare_v2(sqlite, sqlStr, -1, &stmt, NULL);
      while  (sqlite3_step(stmt)==SQLITE_ROW) {
          char  * cString =( char *)sqlite3_column_text(stmt, 0);
          NSString * value = [NSString stringWithCString:cString?cString: "NULL"  encoding:NSUTF8StringEncoding];
          NSNumber * value = [NSNumber numberWithLongLong:sqlite3_column_int64(stmt, 1)];
         }
          sqlite3_finalize(stmt);

stmt是一个数据位置指针,标记查询到数库的数据位置,sqlite3_prepare_v2()方法进行数据库查询的准备工作,第一个参数为成功打开的数据库指针,第二个参数为要执行的查询语句,第三个参数为sqlite3_stmt指针的地址,这个方法也会返回一个int值,作为标记状态是否成功。

sqlite3_step方法对stmt指针进行移动,会逐行进行移动,这个方法会返回一个int值,如果和SQLITE_ROW宏对应,则表明有此行数据,可以通过while循环来对数据进行读取。

sqlite3_column_XXX()是取行中每一列的数据,根据数据类型的不同,sqlite3_column_XXX()有一系列对应的方法,这个方法中第一个参数是stmt指针,第二个参数为列序号。

sqlite3_finalize()方法对stmt指针进行关闭。

三、面向对象的sqlite数据库操作框架封装

        网上不乏有许多优秀的第三方sqlite数据库使用框架,FFDM就是其中之一,并且apple自带的coreData也十分优秀。这篇博客中所述内容并不全面,代码也并不十分完善健壮,封装出来的代码除了能够完成基本的数据库操作外,更多主要是对设计思路的示例。

1.面向对象的sqlite管理类的设计思路

        为了便于使用,在设计时,我们尽量将libsqlite3中的方法不暴漏在使用层,通过面向应用的接口来进行方法的设计,设计思路类图如下:

图中,文件管理中心对文件进行存取删改管理,不暴漏在外,数据库管理中心负责对数据库的创建,删除打开等操作,具体的数据操作由数据库操作对象来完成。

2.文件管理中心方法的编写

        文件管理中心主要负责对数据库文件的存取,可以实现如下方法:

YHBaseCecheCenter.h

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
  *  @brief 获取数据库方法的地址
  *
  *  @return 地址字符串
  *
  */
-(NSString *)getDataBaseFilePath;
/**
  *  @brief 获取某个数据库的大小
  *
  *  @param name 数据库名称
  *
  *  @return 文件大小 单位M
  *
  */
-( float )getSizeFromDataBaseName:(NSString *)name;

YHBaseCecheCenter.m

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-(NSString *)getDataBaseFilePath{
     return  NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
}
-( float )getSizeFromDataBaseName:(NSString *)name{
     NSString * path = [NSString stringWithFormat:@ "/%@/%@" ,NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject,name];
     return   [self fileSizeAtPath:path]/(1024.0*1024.0);
}
//获取文件大小
- ( long  long ) fileSizeAtPath:(NSString*) filePath{
     NSFileManager* manager = [NSFileManager defaultManager];
     if  ([manager fileExistsAtPath:filePath]){
         return  [[manager attributesOfItemAtPath:filePath error:nil] fileSize];
     }
     return  0;
}

在iOS系统中因为其沙盒结构的限制,数据库必须方法documents目录下才能正常打开使用。

3.数据库管理中心的设计

        数据库管理中心主要负责对数据库的宏观操作,采用类方法的设计模式,如下

YHBaseSQLiteManager.h

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
  *  @brief 打开一个数据库 如果不存在则会创建
  *
  *  @param name 数据库名称
  *
  *  @return 数据库操作对象 如果创建失败会返回nil
  *
  */
+(YHBaseSQLiteContext *)openSQLiteWithName:(NSString *)name;
/**
  *  @brief 获取数据库文件的大小 单位M
  *
  *  @param dataBase 数据库上下文对象
  *
  *  @return 数据库文件大小
  */
+( float )getSizeOfDataBase:(YHBaseSQLiteContext *)dataBase;
/**
  *  @brief 获取数据库文件的大小 单位M
  *
  *  @param dataBaseName 数据库名称
  *
  *  @return 数据库文件大小
  */
+( float )getSizeOfDataBaseName:(NSString *)dataBaseName;
/**
  *  @brief 删除所有数据库
  *
  */
+( void )removeDataBase;

 YHBaseSQLiteManager.m

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+(YHBaseSQLiteContext *)openSQLiteWithName:(NSString *)name{
     
     NSString * path =  [[YHBaseCecheCenter sharedTheSingletion]getDataBaseFilePath];
     YHBaseSQLiteContext * context = [[YHBaseSQLiteContext alloc]init];
     context.name = name;
     BOOL  success = [context openDataBaeWithName:[NSString stringWithFormat:@ "%@/%@" ,path,name]];
     if  (success) {
         return  context;
     } else {
         return  nil;
     }
}
+( float )getSizeOfDataBase:(YHBaseSQLiteContext *)dataBase{
     return  [[YHBaseCecheCenter sharedTheSingletion]getSizeFromDataBaseName:dataBase.name];
}
+( float )getSizeOfDataBaseName:(NSString *)dataBaseName{
     return  [[YHBaseCecheCenter sharedTheSingletion]getSizeFromDataBaseName:dataBaseName];
}
+( void )removeDataBase{
     NSString * path =  [[YHBaseCecheCenter sharedTheSingletion]getDataBaseFilePath];
     return  [[YHBaseCecheCenter sharedTheSingletion]removeCacheFromPath:path];
}

4.数据库操作对象

        将操作数据库的核心方法封装在这个类中:

YHBaseSQLiteContext.h

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
  *操作的数据库名称
  */
@property(nonatomic,strong)NSString * name;
/**
  *内含sqlite3 对象
  */
@property(nonatomic,assign)sqlite3 * sqlite3_db;
/**
  * @brief 打开一个数据库 不存在则创建
  *
  * @param path 数据库路径
  *
  * @return 是否操作成功
  */
-( BOOL )openDataBaeWithName:(NSString *)path;
/**
  *  @brief 再数据库中创建一张表 如果已经存在 会返回错误信息
  *
  *  @param name 表的名称
  *
  *  @prarm dic 表中的键 其中字典中需传入 键名:类型  类型的宏定义在YHBaseSQLTypeHeader.h中
  *
  *  @param callBack 结果回调
  */
-( void )createTableWithName:(NSString *)name
             keysDictionary:(NSDictionary<NSString*,NSString*> *) dic
                   callBack:( void  (^)(YHBaseSQLError * error))complete;
 
/**
  *  @brief 向表中添加一条数据
  *
  *  @param dataDic 添加数据的键值对
  *
  *  @param name 插入表的名称
  *
  *  @complete 回调
  */
-( void )insertData:(NSDictionary<NSString *,id>*)dataDic
         intoTable:(NSString *)name
          callBack:( void  (^)(YHBaseSQLError * error))complete;
/**
  *  @brief 向表中添加一个键
  *
  *  @param kName 添加的键
  *
  *  @prarm type 类型
  *
  *  @prarm tableName 表名称
  *
  *  @prarm complete 结果回调
  */
-( void )addKey:(NSString *)kName
       keyType:(NSString *)type
     intoTable:(NSString *)tableName
      callBack:( void (^)(YHBaseSQLError *error))complete;
/**
  *  @brief 修改数据
  *
  *  @param dataDic 新的键值
  *
  *  @param wlStr 条件字符串 一般通过主键找到对应数据修改 可以为nil
  *
  *  @param complete 结果回调
  */
-( void )update:(NSDictionary<NSString*,id> *)dataDic
       inTable:(NSString *)tableName
   whileString:(NSString *)wlStr
      callBack:( void (^)(YHBaseSQLError * error))complete;
/**
  *  @brief 删除数据
  *
  *  @param tableName 表名
  *
  *  @param wlStr 条件字符串 一般通过主键找到对应数据删除 可以为nil 不传这个参数将删除所有数据
  *
  */
-( void )deleteDataFromTable:(NSString *)tableName
                whereString:(NSString *)wlStr
                   callBack:( void (^)(YHBaseSQLError * error))complete;
/**
  *  @brief 删除一张表
  *
  *  @param tableName 表名
  *
  */
-( void )dropTable:(NSString *)tableName
         callBack:( void (^)(YHBaseSQLError * error))complete;
/**
  *  @brief 查询数据
  *
  *  @param keys 要查询的键值 及其对应的数据类型 可以为nil则查询全部
  *
  *  @param tableName 表名
  *
  *  @param orderKey 进行排序的键值 可以为nil 则不排序
  *
  *  @param type 排序方式 在YHBaseSQLTypeHeader中有宏定义
  *
  *  @param wlstr 查询条件 同于查询单个数据
  *
  *  @param complete dataArray为查询到的数据 其内为字典
  *
  */
-( void )selectKeys:(NSArray<NSDictionary *> *)keys
         fromTable:(NSString*)tableName
           orderBy:(NSString *)orderKey
         orderType:(NSString *)type
          whileStr:(NSString *)wlstr
          callBack:( void (^)(NSArray<NSDictionary *> * dataArray,YHBaseSQLError * error))complete;
/**
  *  @brief 关闭数据库上下文操作
  *  调用此方法后 这个context对象将不再有效 如果再需要使用 需要YHBaseSQLiteManager中的类方法再次返回
  */
-( void )closeContext;

YHBaseSQLiteContext.m

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81