在iOS开发之数据存储一中已对偏好设置、归档等存储方式进行了介绍,以上几种方式都有一个致命的缺点,那就是无法存储大批量的数据。本文重点介绍SQLite在iOS开发中的使用。
Start
在iOS中使用SQLite3,首先要添加库文件libsqlite3.dylib和导入主头文件.
#import <sqlite3.h>
libsqlite.png
swift需要建立桥接文件
引入头文件.png1.OC中使用SQLite
操作数据库之前必须先创建数据库文件和要操作的表,所以使用SQLite3,首先要打开数据库文件,然后指定或创建一张表。
1.1 创建并打开数据库
// 打开数据库
- (void)openDatabase:(NSString *)SQLiteName {
//1. 获取数据库存储路径
NSString *dbName = [SQLiteName documentDir];
//2. 打开数据库
// 如果数据库不存在,怎新建并打开一个数据库,否则直接打开
if (sqlite3_open(dbName.UTF8String, &_db) != SQLITE_OK) {
NSLog(@"创建/打开数据库失败。");
}
//3. 创建表
if ([self createTable]) {
NSLog(@"创建表成功");
} else {
NSLog(@"创建表失败");
}
}
/**
* 创建数据表
*/
- (BOOL)createTable {
NSString *sql = @"CREATE TABLE IF NOT EXISTS t_person (id integer PRIMARY KEY AUTOINCREMENT,name text,age integer)";
return [self execSql:sql];
}
1.2 封装执行sql语句方法
使用 sqlite3_exec() 方法可以执行任何SQL语句,比如创表、更新、插入和删除操作。但是一般不用它执行查询语句,因为它不会返回查询到的数据。
/**
* 执行除查询以外的sql语句
*/
- (BOOL)execSql:(NSString *)sql {
if (sqlite3_exec(_db, sql.UTF8String, nil, nil, nil) != SQLITE_OK) {
return NO;
}
return YES;
}
1.3执行查询语句
前面说过一般不使用 sqlite3_exec() 方法查询数据。因为查询数据必须要获得查询结果,所以查询相对比较麻烦。有以下几个步骤:
- sqlite3_prepare_v2() : 检查sql的合法性(准备数据)
- **sqlite3_step() **: 逐行获取查询结果,不断重复,直到最后一条记录
- sqlite3_coloum_xxx() : 获取对应类型的内容,iCol对应的就是SQL语句中字段的顺序,从0开始。根据实际查询字段的属性,使用sqlite3_column_xxx取得对应的内容即可。
- sqlite3_finalize() : 释放stmt
**
* 返回指定sql查询语句运行的结果集
*
* @param sql sql
*
* @return 结果集
*/
- (NSArray *)execRecordSql:(NSString *)sql {
// 1. 评估准备SQL语法是否正确
sqlite3_stmt *stmt = NULL;
/**
* 准备: 理解为预编译SQL语句, 检测里面是否有错误等等, 它可以提供性能
*
* @param db 已经开打的数据库对象
* @param cSQL 需要执行的SQL语句
* @param -1 需要执行的SQL语句的长度, 传入-1系统自动计算
* @param stmt 预编译之后的句柄, 已经要想取出数据, 就需要这个句柄
* @param NULL 一般传NULL
*
* @return
*/
if (sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL) != SQLITE_OK) {
NSLog(@"准备数据失败");
}
NSMutableArray *records = [NSMutableArray array];
// 2.查询数据
// sqlite3_step代表取出一条数据, 如果取到了数据就会返回SQLITE_ROW
while (sqlite3_step(stmt) == SQLITE_ROW) {
// 3. 获取/显示查询结果
// sqlite3_column_xxx方法的第二个参数与sql语句中的字段顺寻一一对应(从0开始)
// 获取一条记录的数据
NSDictionary *dict = [self recordWithStmt:stmt];
[records addObject:dict];
}
// 4. 释放句柄
sqlite3_finalize(stmt);
// 返回查询到的数据
return records;
}
/**
获取一条记录的值
- parameter stmt: 预编译好的SQL语句
- returns: 字典
*/
- (NSDictionary *)recordWithStmt:(sqlite3_stmt *)stmt {
//1.拿到当前这条数据的所有列
int count = sqlite3_column_count(stmt);
// 定义字典存储查询到的数据
NSMutableDictionary *record = [[NSMutableDictionary alloc] init];
for (int index = 0; index < count; index++) {
// 2. 拿到每一列的名称
NSString *name = [NSString stringWithCString:sqlite3_column_name(stmt, index) encoding:NSUTF8StringEncoding];
NSLog(@"%@",name);
// 3.拿到每一列的类型 SQLITE_INTEGER
int type = sqlite3_column_type(stmt, index);
switch (type) {
case SQLITE_INTEGER://整形
[record setObject:@(sqlite3_column_int64(stmt, index)) forKey:name];
break;
case SQLITE_FLOAT:
[record setObject:@(sqlite3_column_double(stmt, index)) forKey:name];
break;
case SQLITE3_TEXT:
// 文本类型
[record setObject:[NSString stringWithUTF8String:sqlite3_column_text(stmt, index)] forKey:name];
break;
case SQLITE_NULL:
// 空类型
[record setObject:[[NSNull alloc]init] forKey:name];
break;
default:
// 二进制类型 SQLITE_BLOB
// 一般情况下, 不会往数据库中存储二进制数据
break;
}
}
return record;
}
2. Swift中使用SQLite
在Swift中使用与OC基本一致。
2.1 打开数据库
/**
打开数据库
- parameter SQLiteName: 数据库名称
*/
func openDatabase(SQLiteName: String) {
//1. 拿到数据库的存储路径
let path = SQLiteName.documentDir()
// print(path)
let cPath = path.cStringUsingEncoding(NSUTF8StringEncoding)!
//2. 打开数据库
/**
* 打开数据库方法
* open方法特点: 如果指定路径对应的数据库文件已经存在, 就会直接打开
* 如果指定路径对应的数据库文件不存在, 就会创建一个新的
* @param cPath 需要打开的数据库文件的路径,转为C语言字符串
* @param db 打开之后的数据库对象 (指针), 以后所有的数据库操作, 都必须要拿到这个指针才能进行相关操作
*
* @return 返回整形值
*/
if sqlite3_open(cPath, &db) != SQLITE_OK {
print("打开数据库失败")
return
}
// 3. 创建表
if createTable() {
print("创建表成功")
} else {
print("创建表失败")
}
}
/**
创建表
- returns: 是否创建成功
*/
private func createTable() -> Bool {
// 1.编写SQL语句
// 建议: 在开发中编写SQL语句, 如果语句过长, 不要写在一行
// 开发技巧: 在做数据库开发时, 如果遇到错误, 可以先将SQL打印出来, 拷贝到PC工具中验证之后再进行调试
let sql = "CREATE TABLE IF NOT EXISTS T_Person( \n" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, \n" +
"name TEXT, \n" +
"age INTEGER \n" +
"); \n"
// 2. 执行sql语句
return execSQL(sql)
}
2.2 封装执行sql语句方法
/**
执行除查询以外的sql语句
- parameter sql: sql语句
- returns: 是否执行成功 true 成功
*/
func execSQL(sql: String) -> Bool {
// 0.将Swift字符串转换为C语言字符串
let cSQL = sql.cStringUsingEncoding(NSUTF8StringEncoding)!
/**
* 执行sql语句
*
* @param COpaquePointer 已经打开的数据库对象
* @param 需要执行的sql语句
* @param 执行SQL语句之后的回调, 一般传nil
* @param 是第三个参数的第一个参数, 一般传nil
* @param 错误信息, 一般传nil
*/
if sqlite3_exec(db, cSQL, nil, nil, nil) != SQLITE_OK {
return false
}
return true
}
2.3执行查询语句
/**
执行查询语句
查询所有的数据
:returns: 查询到的字典数组
*/
func execRecordSql(sql: String) -> [[String: AnyObject]] {
// 0.将Swift字符串转换为C语言字符串
let cSQL = sql.cStringUsingEncoding(NSUTF8StringEncoding)!
// 1.准备数据
var stmt: COpaquePointer = nil
/**
* 准备: 理解为预编译SQL语句, 检测里面是否有错误等等, 它可以提供性能
*
* @param db 已经开打的数据库对象
* @param cSQL 需要执行的SQL语句
* @param -1 需要执行的SQL语句的长度, 传入-1系统自动计算
* @param stmt 预编译之后的句柄, 已经要想取出数据, 就需要这个句柄
* @param nil 一般传nil
*
* @return
*/
if sqlite3_prepare_v2(db, cSQL, -1, &stmt, nil) != SQLITE_OK
{
print("准备数据失败")
}
// 准备成功
var records = [[String: AnyObject]]()
// 2.查询数据
// sqlite3_step代表取出一条数据, 如果取到了数据就会返回SQLITE_ROW
while sqlite3_step(stmt) == SQLITE_ROW
{
// 获取一条记录的数据
let record = recordWithStmt(stmt)
// 将当前获取到的这一条记录添加到数组中
records.append(record)
}
// 返回查询到的数据
return records
}
/**
获取一条记录的值
- parameter stmt: 预编译好的SQL语句
- returns: 字典
*/
private func recordWithStmt(stmt: COpaquePointer) ->[String: AnyObject]
{
// 2.1拿到当前这条数据所有的列
let count = sqlite3_column_count(stmt)
// print(count)
// 定义字典存储查询到的数据
var record = [String: AnyObject]()
for index in 0..<count
{
// 2.2拿到每一列的名称
let cName = sqlite3_column_name(stmt, index)
let name = String(CString: cName, encoding: NSUTF8StringEncoding)!
// print(name)
// 2.3拿到每一列的类型 SQLITE_INTEGER
let type = sqlite3_column_type(stmt, index)
// print("name = \(name) , type = \(type)")
switch type
{
case SQLITE_INTEGER:
// 整形
let num = sqlite3_column_int64(stmt, index)
record[name] = Int(num)
case SQLITE_FLOAT:
// 浮点型
let double = sqlite3_column_double(stmt, index)
record[name] = Double(double)
case SQLITE3_TEXT:
// 文本类型
let cText = UnsafePointer<Int8>(sqlite3_column_text(stmt, index))
let text = NSString(CString: cText, encoding: NSUTF8StringEncoding)!
record[name] = text
case SQLITE_NULL:
// 空类型
record[name] = NSNull()
default:
// 二进制类型 SQLITE_BLOB
// 一般情况下, 不会往数据库中存储二进制数据
print("")
}
}
return record
}
End
总得来说,SQLite3的使用还是比较麻烦的,因为都是些c语言的函数,理解起来也有些困难。不过在一般开发过程中,使用的都是第三方开源库 FMDB,它封装了这些基本的c语言方法,使得我们在使用时更加容易理解,提高开发效率。
网友评论