1.由来
在学习sqlite的时候,发现OC中提供的API是面向过程的方式,如果在开发中用到,那么每次都会重复的去走这个流程,打开数据库,执行sql,细节上的处理等。于是就在sqlite3的基础上,进行了通用封装,在以后的开发中直接使用即可,提高开发效率。
源代码托管:https://github.com/zhangint/OCSQLite/tree/master
2.注意
该封装只是针对通用的接口和使用方式,使用起来也比较简单,sql语句,数据类型,数据值即可。
1.默认表的第一列为主键,创建表的时候需要注意。
2.如若有什么特殊功能或者细节控制,则该接口并不适用。
3.并不提供线程安全保证,需要用户自己进行控制。
3.运行效果接口说明
查询效果:
插入效果:
接口说明:
为了满足多行多列数据的插入和查询,采用了二位数组的方式,当某一列的数据值为空时,需填充[NSNull null];
直接贴头文件内容吧,描述的也很清晰
内部定义数据说明
//sqlite支持的5种数据类型
typedef NS_ENUM(NSInteger,DataType)
{
DTNULL = 0,//NULL
DTINT,//整数
DTREAL,//浮点数
DTTEXT,//文本数据
DTBLOB //二进制数据
};
头文件接口
//@brief:打开数据库文件
//@param:dbFile 数据库文件路径
//@return: 0 成功 其它 失败的错误码
- (NSInteger)openDB:(NSString *)dbFile;
//@brief:关闭数据库连接
//@return: YES 成功 NO 失败
- (NSInteger)closeDB;
//@breif: sql命令,返回执行状态
//@return: 0 成功
// 其它 错误码
//@attention: 主要执行查询,状态获取等
- (NSInteger)execsql:(NSString *)sql;
//@brief: 执行SQL查询语句
//@param: dataType 指定查询返回的数据类型(注意需为DataType的枚举值)
//@return: 返回为双重数组,第一层为一行数据封装,第二层为列数据封装
- (NSArray<NSArray *> *)sqlQuery:(NSString *)sql
dataType:(NSArray<NSNumber *> *)dataType;
//@brief: 数据插入
//@param: sql 插入的sql语句
// data 绑定的值,而为数组形式,先封装一行数据,然后再封装
// datatype 每一列数据的值
//@return: >=0 影响的行数
// -1 失败
//@attention: 内部不检测值和类型是否匹配,需用户自己校验
- (NSInteger)sqlInsert:(NSString*)sql data:(NSArray<NSArray *> *)data datatype:(NSArray *)datatype;
//@brief: 根据错误码,返回错误描述信息
- (NSString *)lastError:(NSInteger)errcode;
4.测试用例
1.多行数据插入
- (void)insertClicked:(id)sender
{
//在插入数据的时候,先创建表
NSString *sql = @"create table if not exists test_table_1 \ (id integer primary key autoincrement,\ name,\ passwd,\ address,\ age,\ photo,\ rank)";
NSLog(@"create table status:%ld",[_dbcon execsql:sql]);
//构造一行的数据
NSMutableArray *rowData_1 = [[NSMutableArray alloc] init];
[rowData_1 addObject:@"zhangint"];
[rowData_1 addObject:@"123456"];
[rowData_1 addObject:@"chengdu"];
[rowData_1 addObject:[NSNumber numberWithInt:25]];
NSString *pic1 = [[NSBundle mainBundle] pathForResource:@"zhangint" ofType:nil];
NSData *pic1_data = [NSData dataWithContentsOfFile:pic1];
unsigned char* msg = (unsigned char*)[pic1_data bytes];
[rowData_1 addObject:pic1_data]; //二进制
[rowData_1 addObject:[NSNumber numberWithFloat:2.3]]; //浮点数
NSMutableArray *rowData_2 = [[NSMutableArray alloc] init];
[rowData_2 addObject:@"love dog"];
[rowData_2 addObject:@"56789"];
[rowData_2 addObject:[NSNull null]]; //string为空
[rowData_2 addObject:[NSNumber numberWithInt:33]];
NSString *pic2 = [[NSBundle mainBundle] pathForResource:@"xiaoyu" ofType:nil];
NSData *pic2_data = [NSData dataWithContentsOfFile:pic2];
[rowData_2 addObject:pic2_data];
[rowData_2 addObject:[NSNumber numberWithFloat:3.3]];
NSMutableArray *rowData_3 = [[NSMutableArray alloc] init];
[rowData_3 addObject:@"xiaoxiaoxiao"];
[rowData_3 addObject:@"9990"];
[rowData_3 addObject:@"beijing"];
[rowData_3 addObject:[NSNumber numberWithInt:32]];
[rowData_3 addObject:[NSNull null]]; //二进制位空
[rowData_3 addObject:[NSNumber numberWithFloat:5.4]];
//数据值的初始化
NSArray *tableArr = [[NSArray alloc] initWithObjects:rowData_1,rowData_2,rowData_3,nil];
//数据类型的初始化
NSArray *dataType = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:DTTEXT],[NSNumber numberWithInt:DTTEXT],[NSNumber numberWithInt:DTINT],[NSNumber numberWithInt:DTBLOB],[NSNumber numberWithInt:DTREAL],nil];
NSString *insertsql = @"insert into test_table_1 values(null,?,?)";
NSLog(@"insert res:%ld",[_dbcon sqlInsert:insertsql data:tableArr datatype:dataType]);
2.数据查询
- (void) searchClicked:(id)sender
{
NSString *sql = @"select * from test_table_1";
NSArray *dataType = [[NSArray alloc] initWithObjects:[NSNumber numberWithBool:DTINT],nil];
NSArray<NSArray *> *searchRes;
searchRes = [_dbcon sqlQuery:sql dataType:dataType];
NSInteger rows = [searchRes count];
NSLog(@"search rows:%ld",rows);
for (int i=0; i<rows; i++)
{
NSArray *rowData = searchRes[i];
NSNumber *idnum = rowData[0];
NSString *name = rowData[1];
NSString *passed = rowData[2];
NSString *address = rowData[3];
NSNumber *age = rowData[4];
//NSData *img = rowData[5];
NSNumber *rank = rowData[6];
NSLog(@"id:%d name:%@ passwd:%@ address:%@ age:%d photo not show rank:%.1f",idnum.intValue,name,passed,address,age.intValue,rank.floatValue);
}
_searchRes = searchRes;
5.源代码
//
// sqliteInter.h
// sqlitePro
//
// Created by zp on 15/12/26.
// Copyright © 2015年 ZP. All rights reserved.
//
//@brief: sqlite通用接口封装
//@attention: 1.本身并不提供线程安全,用户可以通过Serial Dispatch Queue的方式,保证安全可靠访问
// 2.只是对通用的使用方式进行了封装,很多细节或者特殊的使用方式并未覆盖
#import <Foundation/Foundation.h>
#import <sqlite3.h>
//sqlite支持的5种数据类型
typedef NS_ENUM(NSInteger,//文本数据
DTBLOB //二进制数据
};
@interface sqliteInter : NSObject
//错误码
@property (assign,nonatomic) NSInteger lastErrCode;
//@brief:打开数据库文件
//@param:dbFile 数据库文件路径
//@return: 0 成功 其它 失败的错误码
- (NSInteger)openDB:(NSString *)dbFile;
//@brief:关闭数据库连接
//@return: YES 成功 NO 失败
- (NSInteger)closeDB;
//@breif: sql命令,返回执行状态
//@return: 0 成功
// 其它 错误码
//@attention: 主要执行查询,状态获取等
- (NSInteger)execsql:(NSString *)sql;
//@brief: 执行SQL查询语句
//@param: dataType 指定查询返回的数据类型(注意需为DataType的枚举值)
//@return: 返回为双重数组,第一层为一行数据封装,第二层为列数据封装
- (NSArray<NSArray *> *)sqlQuery:(NSString *)sql
dataType:(NSArray<NSNumber *> *)dataType;
//@brief: 数据插入
//@param: sql 插入的sql语句
// data 绑定的值,而为数组形式,先封装一行数据,然后再封装
// datatype 每一列数据的值
//@return: >=0 影响的行数
// -1 失败
//@attention: 内部不检测值和类型是否匹配,需用户自己校验
- (NSInteger)sqlInsert:(NSString*)sql data:(NSArray<NSArray *> *)data datatype:(NSArray *)datatype;
//@brief: 根据错误码,返回错误描述信息
- (NSString *)lastError:(NSInteger)errcode;
@end
//
// sqliteInter.m
// sqlitePro
//
// Created by zp on 15/12/26.
// Copyright © 2015年 ZP. All rights reserved.
//
/**@brief:sqlite数据接口封装,方便复用 * * */
#import "sqliteInter.h"
@interface sqliteInter ()
//注意:sqlite3 为struct类型,不能用strong属性
@property (assign,nonatomic) sqlite3 *database;
@property (assign,nonatomic) sqlite3_stmt *stmt;
@end
@implementation sqliteInter
- (NSInteger)openDB:(NSString *)dbFile
{
//如果数据库已经打开,则先关闭
if (_database)
{
_lastErrCode = [self closeDB];
if (_lastErrCode != sqlITE_OK)
{
_lastErrCode = 0;
return 0;
}
}
_lastErrCode = sqlite3_open([dbFile UTF8String],&_database);
return _lastErrCode;
}
- (NSInteger)closeDB
{
if (_database)
{
NSInteger num = 0;
//会关闭2次,2次失败,则失败
while (num < 2)
{
_lastErrCode = sqlite3_close(_database);
//关闭成功
if (sqlITE_OK == _lastErrCode)
{
_database = nil;
break;
}
sqlite3_finalize(_stmt);
num++;
}
}
return _lastErrCode;
}
- (NSInteger)execsql:(NSString *)sql
{
//这个地方并不校验 _database == nil,errorcode 会有描述
_lastErrCode = sqlite3_exec(_database,[sql UTF8String],NULL,NULL);
//返回错误码
return _lastErrCode;
}
- (NSArray<NSArray *> *)sqlQuery:(NSString *)sql
dataType:(NSArray<NSNumber *> *)dataType
{
NSMutableArray *dataSet = [[NSMutableArray alloc] init];
NSInteger preRes = sqlite3_prepare_v2(_database,-1,&_stmt,nil);
if (preRes == sqlITE_OK)
{
while (sqlite3_step(_stmt) == sqlITE_ROW)
{
NSMutableArray *dataArr = [[NSMutableArray alloc] init];
int colNum = sqlite3_data_count(_stmt);
//获取列的数量
for (int i=0; i<colNum; i++)
{
[dataArr addObject:[self get_col_data:dataType[i].intValue statement:_stmt col:i]];
}
[dataSet addObject:dataArr];
}
}
sqlite3_finalize(_stmt);
return dataSet;
}
- (id) get_col_data:(DataType)dbtype statement:(sqlite3_stmt *)stmt col:(NSInteger)col
{
switch (dbtype) {
case DTNULL:
return [NSNull null];
break;
case DTINT:{
//(int)col 强制转化,避免警告
NSNumber *number = [NSNumber numberWithInt:sqlite3_column_int(stmt,(int)col)];
return number;
break;
}
case DTREAL:{
NSNumber *number = [NSNumber numberWithFloat:sqlite3_column_double(stmt,(int)col)];
return number;
}
case DTTEXT:{
NSString *cellData;
char* str = (char*)(char*)sqlite3_column_text(stmt,(int)col);
if (str != NULL)
{
cellData = [NSString stringWithFormat:@"%s",str];
}
else
{
//当为空的时候,为一个空对象
cellData = @"";
}
return cellData;
}
//二进制的处理
case DTBLOB:{
NSInteger len = sqlite3_column_bytes(stmt,(int)col);
NSData *cellData = [[NSData alloc] initWithBytes:sqlite3_column_blob(stmt,(int)col) length:len];
return cellData;
}
default:
break;
}
return [NSNull null];
}
- (NSInteger)sqlInsert:(NSString*)sql data:(NSArray<NSArray *> *)data datatype:(NSArray *)datatype
{
NSInteger effectNum = 0;
int preStatus = sqlite3_prepare_v2(_database,NULL);
if (preStatus == sqlITE_OK)
{
for (int i=0; i<data.count; i++)
{
NSArray *rowData = data[i];
for (int col=0; col<rowData.count; col++)
{
NSNumber *type = datatype[col];
switch (type.intValue) {
case DTNULL:
sqlite3_bind_null(_stmt,col+1);
break;
case DTINT:{
NSNumber *number = rowData[col];
sqlite3_bind_int(_stmt,col+1,number.intValue);
break;
}
case DTREAL:{
NSNumber *real = rowData[col];
sqlite3_bind_double(_stmt,real.floatValue);
break;
}
case DTTEXT:{
NSString *str;
if (rowData[col] == [NSNull null])
{
str = @"";
}
else
{
str = rowData[col];
}
sqlite3_bind_text(_stmt,[str UTF8String],NULL);
break;
}
case DTBLOB:{
NSData *msg;
if (rowData[col] == [NSNull null])
{
msg = [[NSData alloc] initWithBytes:@"" length:1];
}
else
{
msg = rowData[col];
}
sqlite3_bind_blob(_stmt,[msg bytes],(int)[msg length],NULL);
break;
}
default:
break;
} /*switch */
} //for
if (sqlite3_step(_stmt) == sqlITE_DONE)
{
effectNum++;
}
//需要重置statement
sqlite3_reset(_stmt);
} /*for*/
} /*if*/
sqlite3_finalize(_stmt);
return effectNum;
//这种用法不对
//return sqlite3_changes(_database);
}
- (NSString *)lastError:(NSInteger)errcode
{
return [NSString stringWithUTF8String:sqlite3_errstr((int)errcode)];
}
@end