目录
一、SQLite数据库
1、基本使用
2、事务
二、FMDB
1、基本使用
2、事务
3、线程安全
一、SQLite数据库
1、基本使用
SQLite数据库是基于C实现的、移动端开发常用的、一款轻量级数据库,它占用的资源非常低,可能只需要几百K的内存就够了,它的处理速度非常快,比MySQL、PostgreSQL这两款著名的数据库都还快。
我们需要通过C函数和SQL语句的配合来实现SQLite数据库的开发。
常用的C函数有3个:
- 打开数据库
sqlite3_open
- 执行SQL语句
sqlite3_exec
- 关闭数据库
sqlite3_close
常用的SQL语句有6条:
- 创建表
CREATE TABLE
例如:CREATE TABLE IF NOT EXISTS 't_book' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'price' FLOAT NOT NULL, 'abstract' TEXT)
- 删除表
DROP TABLE
例如:DROP TABLE IF EXISTS 't_book'
- 增数据
INSERT INTO
例如:INSERT INTO 't_book' VALUES (NULL, '子夜', 46.0, '天亮之前,有一个时候是非常暗的,星也没有,月亮也没有')
- 删数据
DELETE FROM
例如:删除全部DELETE FROM 't_book'
,条件删除DELETE FROM 't_book' WHERE name = '子夜'
- 改数据
UPDATE
例如:修改全部UPDATE 't_book' SET price = 11.11
,条件修改UPDATE 't_book' SET price = 1111.1 WHERE name = '子夜'
- 查数据
SELECT * FROM
例如:查询全部SELECT * FROM 't_book'
,条件查询SELECT * FROM 't_book' WHERE name = '子夜'
举个例子。
-----------BookModel.h-----------
#import <UIKit/UIKit.h>
@interface BookModel : NSObject
@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) CGFloat price;
@property (strong, nonatomic) NSString *abstract;
@end
-----------BookModel.m-----------
#import "BookModel.h"
@implementation BookModel
- (NSString *)description {
return [NSString stringWithFormat:@"%@ %.2f %@", self.name, self.price, self.abstract];
}
@end
-----------SQLiteViewController.h-----------
#import <UIKit/UIKit.h>
@interface SQLiteViewController : UIViewController
@end
-----------SQLiteViewController.m-----------
#import "SQLiteViewController.h"
#import <sqlite3.h>
#import "BookModel.h"
@interface SQLiteViewController ()
@end
@implementation SQLiteViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@", NSHomeDirectory());
// 1、打开数据库(如果指定路径下的数据库存在则直接打开,不存在会创建一个再打开)
sqlite3 *db = nil;
NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
int openDBResult = sqlite3_open(sqliteDBPath.UTF8String, &db);
if (openDBResult == SQLITE_OK) {
NSLog(@"打开数据库成功");
} else {
NSLog(@"打开数据库失败:%d", openDBResult);
}
// 2、创建表
NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_book' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'price' FLOAT NOT NULL, 'abstract' TEXT)";
int creatTableResult = sqlite3_exec(db, creatTableWords.UTF8String, nil, nil, nil);
if (creatTableResult == SQLITE_OK) {
NSLog(@"创建表成功");
} else {
NSLog(@"创建表失败:%d", creatTableResult);
}
// 3、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
BookModel *ziye = [[BookModel alloc] init];
ziye.name = @"子夜";
ziye.price = 46.0;
ziye.abstract = @"天亮之前,有一个时候是非常暗的,星也没有,月亮也没有";
NSString *insertDataWords = [NSString stringWithFormat:@"INSERT INTO 't_book' VALUES (NULL, '%@', %.2f, '%@')", ziye.name, ziye.price, ziye.abstract];
int insertDataResult = sqlite3_exec(db, insertDataWords.UTF8String, nil, nil, nil);
if (insertDataResult == SQLITE_OK) {
NSLog(@"增数据成功");
} else {
NSLog(@"增数据失败:%d", insertDataResult);
}
// 4、删数据
NSString *deleteDataWords = @"DELETE FROM 't_book' WHERE name = '子夜' and abstract != '天亮之前,有一个时候是非常暗的,星也没有,月亮也没有'";
int deleteDataResult = sqlite3_exec(db, deleteDataWords.UTF8String, nil, nil, nil);
if (deleteDataResult == SQLITE_OK) {
NSLog(@"删数据成功");
} else {
NSLog(@"删数据失败:%d", deleteDataResult);
}
// 5、改数据
NSString *updateDataWords = @"UPDATE 't_book' SET price = 11.11";
int updateDataResult = sqlite3_exec(db, updateDataWords.UTF8String, nil, nil, nil);
if (updateDataResult == SQLITE_OK) {
NSLog(@"改数据成功");
} else {
NSLog(@"改数据失败:%d", updateDataResult);
}
// 6、查数据
NSMutableArray *selectDataArray = [NSMutableArray array];
sqlite3_stmt *stmt = nil; // 伴随指针
NSString *selectDataWords = @"SELECT * FROM 't_book' WHERE id >= 3";
int selectDataResult = sqlite3_prepare(db, selectDataWords.UTF8String, -1, &stmt, nil);
if (selectDataResult == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
BookModel *bookModel = [[BookModel alloc] init];
bookModel.name = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];
bookModel.price = sqlite3_column_double(stmt, 2);
bookModel.abstract = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 3)];
[selectDataArray addObject:bookModel];
}
NSLog(@"查数据成功:%@", selectDataArray);
} else {
NSLog(@"查数据失败了:%d", selectDataResult);
}
sqlite3_finalize(stmt); // 释放伴随指针
// 7、删除表
NSString *dropTableWords = @"DROP TABLE IF EXISTS 't_book'";
int dropTableResult = sqlite3_exec(db, dropTableWords.UTF8String, nil, nil, nil);
if (dropTableResult == SQLITE_OK) {
NSLog(@"删除表成功");
} else {
NSLog(@"删除表失败:%d", dropTableResult);
}
// 8、关闭数据库
int closeDBResult = sqlite3_close(db);
if (closeDBResult == SQLITE_OK) {
NSLog(@"关闭数据库成功");
} else {
NSLog(@"关闭数据库失败:%d", closeDBResult);
}
}
@end
2、事务
所谓事务就是指把多条SQL语句当作一个操作序列来执行,只有所有的SQL语句都执行成功这个操作序列才算执行成功,其中任意一条SQL语句执行这个操作序列就算执行失败,失败后我们可以把数据回滚到原始状态。比如张三给李四转钱,张三扣钱这个操作和李四加钱这个操作就应该当做一个操作序列来执行,只有张三扣钱成功 + 李四加钱成功同时发生这个操作才能算成功,如果不采用事务来做,那很可能出现张三扣钱成功了,李四加钱的时候刚好断网了,这样结果就是张三白白丢了钱。事务一般是以开启事务BEGIN TRANSACTION
开始,提交事务COMMIT TRANSACTION
或回滚事务ROLLBACK TRANSACTION
结束。
举个例子。
-----------SQLiteViewController.h-----------
#import <UIKit/UIKit.h>
@interface SQLiteViewController : UIViewController
@end
-----------SQLiteViewController.m-----------
#import "SQLiteViewController.h"
#import <sqlite3.h>
@interface SQLiteViewController ()
@end
@implementation SQLiteViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@", NSHomeDirectory());
// 1、打开数据库
sqlite3 *db = nil;
NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
int openDBResult = sqlite3_open(sqliteDBPath.UTF8String, &db);
if (openDBResult == SQLITE_OK) {
NSLog(@"打开数据库成功");
} else {
NSLog(@"打开数据库失败:%d", openDBResult);
}
// 2、创建表
NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_money' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'money' FLOAT NOT NULL)";
int creatTableResult = sqlite3_exec(db, creatTableWords.UTF8String, nil, nil, nil);
if (creatTableResult == SQLITE_OK) {
NSLog(@"创建表成功");
} else {
NSLog(@"创建表失败:%d", creatTableResult);
}
// 3、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
NSString *insertDataWords1 = @"INSERT INTO 't_money' VALUES (NULL, '张三', 100)";
int insertDataResult1 = sqlite3_exec(db, insertDataWords1.UTF8String, nil, nil, nil);
if (insertDataResult1 == SQLITE_OK) {
NSLog(@"增数据成功1");
} else {
NSLog(@"增数据失败1:%d", insertDataResult1);
}
NSString *insertDataWords2 = @"INSERT INTO 't_money' VALUES (NULL, '李四', 100)";
int insertDataResult2 = sqlite3_exec(db, insertDataWords2.UTF8String, nil, nil, nil);
if (insertDataResult2 == SQLITE_OK) {
NSLog(@"增数据成功2");
} else {
NSLog(@"增数据失败2:%d", insertDataResult2);
}
//-----------开启事务-----------//
NSString *beginTransactionWords = @"BEGIN TRANSACTION";
sqlite3_exec(db, beginTransactionWords.UTF8String, nil, nil, nil);
//-----------开启事务-----------//
// 4、改数据
NSString *updateDataWords1 = @"UPDATE 't_money' SET money = 90 WHERE name = '张三'";
int updateDataResult1 = sqlite3_exec(db, updateDataWords1.UTF8String, nil, nil, nil);
NSString *updateDataWords2 = @"UPDATE 't_money' SET money = 110 WHERE name = '李四'";
int updateDataResult2 = sqlite3_exec(db, updateDataWords2.UTF8String, nil, nil, nil);
if (updateDataResult1 == SQLITE_OK && updateDataResult2 == SQLITE_OK) { // 两个都改成功,才算成功
NSLog(@"改数据成功");
//-----------提交事务-----------//
NSString *commitTransactionWords = @"COMMIT TRANSACTION";
sqlite3_exec(db, commitTransactionWords.UTF8String, nil, nil, nil);
//-----------提交事务-----------//
} else { // 任意一个改失败,就算失败
NSLog(@"改数据失败:%d %d", updateDataResult1, updateDataResult2);
//-----------回滚事务-----------//
NSString *rollbackTransactionWords = @"ROLLBACK TRANSACTION";
sqlite3_exec(db, rollbackTransactionWords.UTF8String, nil, nil, nil);
//-----------回滚事务-----------//
}
// 5、关闭数据库
int closeDBResult = sqlite3_close(db);
if (closeDBResult == SQLITE_OK) {
NSLog(@"关闭数据库成功");
} else {
NSLog(@"关闭数据库失败:%d", closeDBResult);
}
}
@end
二、FMDB
1、基本使用
FMDB是对SQLite数据库C语言API的OC封装,所以使用起来更加面向对象,省去了冗长的C语言API的编写。更重要的是FMDB提供了线程安全的数据库操作方法,能够有效地保证数据安全。
常用的类有3个:
- FMDatabase:可以把它理解为一个数据库,一个FMDatabase对象就是一个单独的SQLite数据库
- FMResultSet:FMDatabase查询后的结果集合
- FMDatabaseQueue:用来在多线程中操作FMDatabase,保证数据库访问的线程安全
常用的OC方法有2个:
- 查询操作使用
executeQuery
- 其它操作使用
executeUpdate
常用的SQL语句当然还是上面的6条:
- 创建表
CREATE TABLE
例如:CREATE TABLE IF NOT EXISTS 't_book' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'price' FLOAT NOT NULL, 'abstract' TEXT)
- 删除表
DROP TABLE
例如:DROP TABLE IF EXISTS 't_book'
- 增数据
INSERT INTO
例如:INSERT INTO 't_book' VALUES (NULL, '子夜', 46.0, '天亮之前,有一个时候是非常暗的,星也没有,月亮也没有')
- 删数据
DELETE FROM
例如:删除全部DELETE FROM 't_book'
,条件删除DELETE FROM 't_book' WHERE name = '子夜'
- 改数据
UPDATE
例如:修改全部UPDATE 't_book' SET price = 11.11
,条件修改UPDATE 't_book' SET price = 1111.1 WHERE name = '子夜'
- 查数据
SELECT * FROM
例如:查询全部SELECT * FROM 't_book'
,条件查询SELECT * FROM 't_book' WHERE name = '子夜'
举个例子。
-----------BookModel.h-----------
#import <UIKit/UIKit.h>
@interface BookModel : NSObject
@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) CGFloat price;
@property (strong, nonatomic) NSString *abstract;
@end
-----------BookModel.m-----------
#import "BookModel.h"
@implementation BookModel
- (NSString *)description {
return [NSString stringWithFormat:@"%@ %.2f %@", self.name, self.price, self.abstract];
}
@end
-----------FMDBViewController.h-----------
#import <UIKit/UIKit.h>
@interface SQLiteViewController : UIViewController
@end
-----------FMDBViewController.m-----------
#import "FMDBViewController.h"
#import <FMDB.h>
#import "BookModel.h"
@interface FMDBViewController ()
@end
@implementation FMDBViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@", NSHomeDirectory());
// 1、打开数据库(如果指定路径下的数据库存在则直接打开,不存在会创建一个再打开)
NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
FMDatabase *db = [[FMDatabase alloc] initWithPath:sqliteDBPath];
if ([db open]) {
NSLog(@"打开数据库成功");
} else {
NSLog(@"打开数据库失败");
}
// 2、创建表
NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_book' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'price' FLOAT NOT NULL, 'abstract' TEXT)";
if ([db executeUpdate:creatTableWords]) {
NSLog(@"创建表成功");
} else {
NSLog(@"创建表失败");
}
// 3、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
BookModel *ziye = [[BookModel alloc] init];
ziye.name = @"子夜";
ziye.price = 46.0;
ziye.abstract = @"11天亮之前,有一个时候是非常暗的,星也没有,月亮也没有";
NSString *insertDataWords = [NSString stringWithFormat:@"INSERT INTO 't_book' VALUES (NULL, '%@', %.2f, '%@')", ziye.name, ziye.price, ziye.abstract];
if ([db executeUpdate:insertDataWords]) {
NSLog(@"增数据成功");
} else {
NSLog(@"增数据失败");
}
// 4、删数据
NSString *deleteDataWords = @"DELETE FROM 't_book' WHERE name = '子夜' and abstract != '天亮之前,有一个时候是非常暗的,星也没有,月亮也没有'";
if ([db executeUpdate:deleteDataWords]) {
NSLog(@"删数据成功");
} else {
NSLog(@"删数据失败");
}
// 5、改数据
NSString *updateDataWords = @"UPDATE 't_book' SET price = 11.11";
if ([db executeUpdate:updateDataWords]) {
NSLog(@"改数据成功");
} else {
NSLog(@"改数据失败");
}
// 6、查数据
NSMutableArray *selectDataArray = [NSMutableArray array];
NSString *selectDataWords = @"SELECT * FROM 't_book' WHERE id >= 3";
FMResultSet *resultSet = [db executeQuery:selectDataWords];
while ([resultSet next]) { // [resultSet next],代表在结果集合里面一行一行的查询,查到一条处理一条,直到没有
BookModel *bookModel = [[BookModel alloc] init];
bookModel.name = [resultSet stringForColumn:@"name"];
bookModel.price = [resultSet doubleForColumn:@"price"];
bookModel.abstract = [resultSet stringForColumn:@"abstract"];
[selectDataArray addObject:bookModel];
}
[resultSet close]; // 关闭resultSet,不写的话会报warning
for (BookModel *bookModel in selectDataArray) {
NSLog(@"查数据成功:%@", bookModel);
}
// 7、删除表
NSString *dropTableWords = @"DROP TABLE IF EXISTS 't_book'";
if ([db executeUpdate:dropTableWords]) {
NSLog(@"删除表成功");
} else {
NSLog(@"删除表失败");
}
// 8、关闭数据库
if ([db close]) {
NSLog(@"关闭数据库成功");
} else {
NSLog(@"关闭数据库失败");
}
}
@end
2、事务
举个例子。
-----------FMDBViewController.h-----------
#import <UIKit/UIKit.h>
@interface SQLiteViewController : UIViewController
@end
-----------FMDBViewController.m-----------
#import "FMDBViewController.h"
#import <FMDB.h>
#import "BookModel.h"
@interface FMDBViewController ()
@end
@implementation FMDBViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@", NSHomeDirectory());
// 1、打开数据库(如果指定路径下的数据库存在则直接打开,不存在会创建一个再打开)
NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
FMDatabase *db = [[FMDatabase alloc] initWithPath:sqliteDBPath];
if ([db open]) {
NSLog(@"打开数据库成功");
} else {
NSLog(@"打开数据库失败");
}
// 2、创建表
NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_money' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'money' FLOAT NOT NULL)";
if ([db executeUpdate:creatTableWords]) {
NSLog(@"创建表成功");
} else {
NSLog(@"创建表失败");
}
// 3、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
NSString *insertDataWords1 = @"INSERT INTO 't_money' VALUES (NULL, '张三', 100)";
if ([db executeUpdate:insertDataWords1]) {
NSLog(@"增数据成功1");
} else {
NSLog(@"增数据失败1");
}
NSString *insertDataWords2 = @"INSERT INTO 't_money' VALUES (NULL, '李四', 100)";
if ([db executeUpdate:insertDataWords2]) {
NSLog(@"增数据成功2");
} else {
NSLog(@"增数据失败2");
}
// 4、改数据
//-----------开启事务-----------//
[db beginTransaction];
//-----------开启事务-----------//
NSString *updateDataWords1 = @"UPDATE 't_money' SET money = 90 WHERE name = '张三'";
NSString *updateDataWords2 = @"UPDATE 't_money' SET money = 110 WHERE name = '李四'";
if ([db executeUpdate:updateDataWords1] && [db executeUpdate:updateDataWords2]) { // 两个都改成功,才算成功
NSLog(@"改数据成功");
//-----------提交事务-----------//
[db commit];
//-----------提交事务-----------//
} else { // 任意一个改失败,就算失败
NSLog(@"改数据失败");
//-----------回滚事务-----------//
[db rollback];
//-----------回滚事务-----------//
}
// 5、关闭数据库
if ([db close]) {
NSLog(@"关闭数据库成功");
} else {
NSLog(@"关闭数据库失败");
}
}
@end
3、线程安全
线程安全的数据库操作方法非常简单,我们只需要在FMDatabase使用的基础上套一层FMDatabaseQueue、把所有的操作都放到dbQueue的block回调里就可以了。
我们把事务的例子搞成线程安全的。
-----------FMDBViewController.h-----------
#import <UIKit/UIKit.h>
@interface SQLiteViewController : UIViewController
@end
-----------FMDBViewController.m-----------
#import "FMDBViewController.h"
#import <FMDB.h>
#import "BookModel.h"
@interface FMDBViewController ()
@end
@implementation FMDBViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@", NSHomeDirectory());
NSString *sqliteDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"db.sqlite"];
FMDatabaseQueue *dbQueue = [FMDatabaseQueue databaseQueueWithPath:sqliteDBPath];
[dbQueue inDatabase:^(FMDatabase *db) {
// 1、创建表
NSString *creatTableWords = @"CREATE TABLE IF NOT EXISTS 't_money' ('id' INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, 'name' TEXT NOT NULL, 'money' FLOAT NOT NULL)";
if ([db executeUpdate:creatTableWords]) {
NSLog(@"创建表成功");
} else {
NSLog(@"创建表失败");
}
// 2、增数据(主键传NULL就会自动增加,第一条数据会设置为1)
NSString *insertDataWords1 = @"INSERT INTO 't_money' VALUES (NULL, '张三', 100)";
if ([db executeUpdate:insertDataWords1]) {
NSLog(@"增数据成功1");
} else {
NSLog(@"增数据失败1");
}
NSString *insertDataWords2 = @"INSERT INTO 't_money' VALUES (NULL, '李四', 100)";
if ([db executeUpdate:insertDataWords2]) {
NSLog(@"增数据成功2");
} else {
NSLog(@"增数据失败2");
}
// 3、改数据
//-----------开启事务-----------//
[db beginTransaction];
//-----------开启事务-----------//
NSString *updateDataWords1 = @"UPDATE 't_money' SET money = 90 WHERE name = '张三'";
NSString *updateDataWords2 = @"UPDATE 't_money' SET money = 110 WHERE name = '李四'";
if ([db executeUpdate:updateDataWords1] && [db executeUpdate:updateDataWords2]) { // 两个都改成功,才算成功
NSLog(@"改数据成功");
//-----------提交事务-----------//
[db commit];
//-----------提交事务-----------//
} else { // 任意一个改失败,就算失败
NSLog(@"改数据失败");
//-----------回滚事务-----------//
[db rollback];
//-----------回滚事务-----------//
}
}];
}
@end