FMDB

FMDB

FMDB学习还是直接看源码更好,这里只是做一点简单的学习和翻译工作FMDB GIT 源码;

FMDB有三个主要的类

  • <FMDatabase> 是用来执行SQL语句的类,是一个单一的SQLite数据库

  • <FMResultSet> 是一个用FMDatabase类执行查询语句后返回的结果集

  • <FMDatabaseQueue> 在多线程执行查询和更新时使用的类.

创建数据库

创建数据库需要指定一个唯一路径作为SQLite数据库的目录.
这个路径可以是下面三种情况之一:

  • 文件路径,这个路径可以不存在,如果不存在则会创建一个给你
  • 可以是空字符串<@"">.传入空字符串会在临时目录创建一个空数据库,当FMDatabase连接关闭时,会自动删除这个数据库
  • 可以传nil,会在内存中创建一个数据库.当FMDatabase连接关闭时,会自动删除这个数据库.
路径
1
2
3
4
5
6
7
8
9
10
11
12
13
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;

NSString *dbPath = [docsPath stringByAppendingPathComponent:@"test.db"];

FMDatabase *db = [FMDatabase databaseWithPath:dbPath];

//访问修改数据库必须保证数据库是打开的,这样才能保证数据库的读写.

//打开数据库,如果没有,会创建一个数据库.
if(![db open]) {

return;
}

Demo

Demo链接

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
//创建单表,简单demo.若数据庞大有依赖关系,通常为了方便管理,会创建多个table,如订单和订单详情这两个table.
//
// Created by 李泽宇 on 16/3/21.
// Copyright © 2016年 丶信步沧桑. All rights reserved.

#import "XBViewController.h"
#import "FMDB.h"
#import "XBStatusBarHUD.h"
#import "XBDatabase.h"
#import <objc/runtime.h>


@interface XBViewController ()

// 属性名 对应 字段名. 因下面想练习一下runtime,顾数据库中的表的字段名与属性名保持一致,来保证条件查询时,sql语句的拼接
@property (weak, nonatomic) IBOutlet UITextField *name;
@property (weak, nonatomic) IBOutlet UITextField *age;
@property (weak, nonatomic) IBOutlet UITextField *height;


// 单例FMDatabase
@property (nonatomic, strong) XBDatabase *db;

@end

@implementation XBViewController

/// 懒加载
- (FMDatabase *)db {

if (!_db) {

_db = [XBDatabase sharedDatabaseManager];

}

return _db;
}


- (void)viewDidLoad {

[super viewDidLoad];

//访问修改数据库必须保证数据库是打开的,这样才能保证数据库的读写.
//打开数据库,如果没有,会创建一个数据库.
if (![self.db open]) {
NSLog(@"失败");
return;
}

// 拼接创建表的sql语句, 字段名与属性名一致.
NSString *tableString = @"CREATE TABLE IF NOT EXISTS T_Person ('Id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 'name' TEXT, 'age' INTEGER, 'height' REAL)";

// executeUpdate: 此方法用于创建, 更新, 插入..返回值代表成功与否
BOOL isSuccess = [self.db executeUpdate:tableString];

// 判断成功失败
if (!isSuccess) {
[XBStatusBarHUD message:@"创表失败"];
return;
}

// XBStatusBarHUD: 还未完成的小小指示器,会在statusBar弹出一个window显示提示内容.
[XBStatusBarHUD message:@"创表成功"];

}

#pragma -Mark - 监听事件
/// 保存/插入/添加按钮
- (IBAction)saveButtonClick:(id)sender {

// 插入数据
BOOL isSave = [self.db executeUpdate:@"INSERT INTO T_Person (name, age, height) VALUES (?, ?, ?)",self.name.text,self.age.text,self.height.text];

// 判断成功与否
if (!isSave) {
// 回顾一下 UIAlertView, UIAlertView在iOS_9.0过期. UIAlertController iOS_8.0开始
if ([UIDevice currentDevice].systemVersion.doubleValue >= 9.0) {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"友情提示" message:@"保存失败" preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];

[alert addAction:action];

[self presentViewController:alert animated:YES completion:nil];

} else {

UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"友情提示" message:@"保存失败" delegate:self cancelButtonTitle:@"确定" otherButtonTitles: nil];

[alertView show];
}
}

[XBStatusBarHUD message:@"保存成功"];

}


/// 全部查询
- (IBAction)queryButtonClick:(id)sender {

NSString *selectSQL = [NSString stringWithFormat:@"SELECT id, name, age, height FROM T_Person;"];

// executeQuery: 返回一个FMResultSet类型的结果集合.
FMResultSet *resultAll = [self.db executeQuery:selectSQL];

// 创表时id为 自增长 主键; 数据库若有数据,id可取值. 通过返回的resultAll调用intForColumn: 传入字段名(列名) 来查询这一列的值
if (![resultAll intForColumn:@"id"]) {

[XBStatusBarHUD message:@"数据库无数据"];

return;
}

// 可通过循环调用 next 函数, 来遍历查询结果;
while ([resultAll next]) {

int Id = [resultAll intForColumn:@"id"];

NSString *name = [resultAll stringForColumn:@"name"];

int age = [resultAll intForColumn:@"age"];

double height = [resultAll doubleForColumn:@"height"];

NSLog(@"ID----%d, name----%@, age----%d, height----%.2f",Id, name, age, height);
}

}

/// 条件查询
- (IBAction)criteriaqueriesButtonClick:(id)sender {

// if (![self.db open]) {
// return;
// }

// 拼接SQL语句, 此处拼接[self sqlString]使用运行时
NSString *SQL =[NSString stringWithFormat:@"SELECT * %@", [self sqlString]];

// 查看拼接的SQL语句
NSLog(@"%@",SQL);

FMResultSet *resultAll = [self.db executeQuery:SQL];

// 判断是否查询到数据
if (![resultAll intForColumn:@"id"]) {
[XBStatusBarHUD message:@"查无此项"];
return;
}

while ([resultAll next]) {

int Id = [resultAll intForColumn:@"id"];

NSString *name = [resultAll stringForColumn:@"name"];

int age = [resultAll intForColumn:@"age"];

double height = [resultAll doubleForColumn:@"height"];

NSLog(@"ID----%d, name----%@, age----%d, height----%.2f",Id, name, age, height);
}

[XBStatusBarHUD message:@"查询成功"];
}

/// 删除语句.
- (IBAction)deleteButtonClick:(id)sender {

// if (![self.db open]) {
// return;
// }

// 字段若为TEXT = '' 一定要加单引号,不然会造成 text类型找不到错误.而INTEGER,REAL类型不会,为防止错误,所有字段的值都加''
NSString *deleteSQL = [NSString stringWithFormat:@"DELETE %@",[self sqlString]];

BOOL isDelete = [self.db executeUpdate:deleteSQL];

if (!isDelete) {

[XBStatusBarHUD message:@"删除失败"];

return;
}

[XBStatusBarHUD message:@"删除成功"];
}


#pragma -Mark - Runtime 拼接字符串
- (NSString *)sqlString {


unsigned int outCount;


Ivar *ivars = class_copyIvarList([XBViewController class], &outCount);


NSMutableDictionary *dictM = [[NSMutableDictionary alloc]init];



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

Ivar ivar = ivars[i];

const char *type = ivar_getTypeEncoding(ivar);

NSString *typeOc = [[NSString stringWithUTF8String:type] substringWithRange:NSMakeRange(2, 11)];

if (![typeOc isEqualToString:@"UITextField"]) {
continue;
}


const char *field = ivar_getName(ivar);

// NSLog(@"%s",field);

NSString *fieldString = [[NSString stringWithUTF8String:field] substringFromIndex:1];


UITextField *value = [self valueForKey:fieldString];



if ([value.text isEqualToString:@""]) {
continue;
}

// NSLog(@"-----%@",value.text);

[dictM setValue:value.text forKey:fieldString];

}

NSMutableString *sqlM = [NSMutableString stringWithString:@"FROM T_Person WHERE "];

[dictM enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
// NSLog(@"key-%@: value-%@",key,obj);

[sqlM appendFormat: @"%@ = '%@' AND ",key ,obj];

}];


NSUInteger lenth = sqlM.length;

NSRange range = NSMakeRange(0, lenth - 5);

NSString * sql = [NSString stringWithFormat:@"%@;",[sqlM substringWithRange:range]];



return sql;
}

Runtime链接