SQLiteManager

https://github.com/misato/SQLiteManager4iOS

本人以前从事过嵌入式开发,后来转职为iOS开发,即使如此,也绝不想去碰C语言级别的面向过程的 sqlite3 来操作数据库,做高级语言开发还去折腾面向过程的东西,一个小小的nil没判断好就导致程序崩溃, 这就是 sqlite3 APIs 给你带来的问题,只有封装成面向对象的接口才有可能会去用.有一个封装得挺好的 FMDB 是不二的选择,但这个 SQLiteManager 属于轻量级封装,满足了最最基本的需求,但是,他是在开了ARC的情形下使用的,也就是说,没开ARC后会各种泄露......

上面的链接可以下载源码,我修改了源码,适用于我自己,提供如下:

SQLiteManager.h

#import <Foundation/Foundation.h>
#import "sqlite3.h" enum errorCodes {
kDBNotExists,
kDBFailAtOpen,
kDBFailAtCreate,
kDBErrorQuery,
kDBFailAtClose
}; @interface SQLiteManager : NSObject { sqlite3 *db; // The SQLite db reference
NSString *databaseName; // The database name
} - (id)initWithDatabaseNamed:(NSString *)name; // SQLite Operations
- (NSError *) openDatabase;
- (NSError *) doQuery:(NSString *)sql;
- (NSError *)doUpdateQuery:(NSString *)sql withParams:(NSArray *)params;
- (NSArray *) getRowsForQuery:(NSString *)sql;
- (NSError *) closeDatabase;
- (NSInteger)getLastInsertRowID; - (NSString *)getDatabaseDump; @end

SQLiteManager.m

#import "SQLiteManager.h"

#define FOLDER_PATH   @"/Library/Caches/YOU_FOLDER_NAME"

// Private methods
@interface SQLiteManager (Private) - (NSString *)getDatabasePath;
- (NSError *)createDBErrorWithDescription:(NSString*)description andCode:(int)code; @end @implementation SQLiteManager #pragma mark Init & Dealloc /**
* Init method.
* Use this method to initialise the object, instead of just "init".
*
* @param name the name of the database to manage.
*
* @return the SQLiteManager object initialised.
*/ - (id)initWithDatabaseNamed:(NSString *)name; {
self = [super init];
if (self != nil) {
databaseName = [[NSString alloc] initWithString:name];
db = nil;
}
return self;
} #pragma mark SQLite Operations /**
* Open or create a SQLite3 database.
*
* If the db exists, then is opened and ready to use. If not exists then is created and opened.
*
* @return nil if everything was ok, an NSError in other case.
*
*/ - (NSError *) openDatabase { NSError *error = nil; NSString *databasePath = [self getDatabasePath];
NSLog(@"%@", databasePath); const char *dbpath = [databasePath UTF8String];
int result = sqlite3_open(dbpath, &db);
if (result != SQLITE_OK) {
const char *errorMsg = sqlite3_errmsg(db);
NSString *errorStr = [NSString stringWithFormat:@"The database could not be opened: %@",[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]];
error = [self createDBErrorWithDescription:errorStr andCode:kDBFailAtOpen];
} return error;
} /**
* Does an SQL query.
*
* You should use this method for everything but SELECT statements.
*
* @param sql the sql statement.
*
* @return nil if everything was ok, NSError in other case.
*/ - (NSError *)doQuery:(NSString *)sql { NSError *openError = nil;
NSError *errorQuery = nil; //Check if database is open and ready.
if (db == nil) {
openError = [self openDatabase];
} if (openError == nil) {
sqlite3_stmt *statement;
const char *query = [sql UTF8String];
sqlite3_prepare_v2(db, query, -, &statement, NULL); if (sqlite3_step(statement) == SQLITE_ERROR) {
const char *errorMsg = sqlite3_errmsg(db);
errorQuery = [self createDBErrorWithDescription:[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]
andCode:kDBErrorQuery];
}
sqlite3_finalize(statement);
errorQuery = [self closeDatabase];
}
else {
errorQuery = openError;
} return errorQuery;
} /**
* Does an SQL parameterized query.
*
* You should use this method for parameterized INSERT or UPDATE statements.
*
* @param sql the sql statement using ? for params.
*
* @param params NSArray of params type (id), in CORRECT order please.
*
* @return nil if everything was ok, NSError in other case.
*/ - (NSError *)doUpdateQuery:(NSString *)sql withParams:(NSArray *)params { NSError *openError = nil;
NSError *errorQuery = nil; //Check if database is open and ready.
if (db == nil) {
openError = [self openDatabase];
} if (openError == nil) {
sqlite3_stmt *statement;
const char *query = [sql UTF8String];
sqlite3_prepare_v2(db, query, -, &statement, NULL); //BIND the params!
int count =;
for (id param in params ) {
count++;
if ([param isKindOfClass:[NSString class]] )
sqlite3_bind_text(statement, count, [param UTF8String], -, SQLITE_TRANSIENT);
if ([param isKindOfClass:[NSNumber class]] ) {
if (!strcmp([param objCType], @encode(float)))
sqlite3_bind_double(statement, count, [param doubleValue]);
else if (!strcmp([param objCType], @encode(int)))
sqlite3_bind_int(statement, count, [param intValue]);
else if (!strcmp([param objCType], @encode(BOOL)))
sqlite3_bind_int(statement, count, [param intValue]);
else
NSLog(@"unknown NSNumber");
}
if ([param isKindOfClass:[NSDate class]]) {
sqlite3_bind_double(statement, count, [param timeIntervalSince1970]);
}
if ([param isKindOfClass:[NSData class]] ) {
sqlite3_bind_blob(statement, count, [param bytes], [param length], SQLITE_STATIC);
}
} if (sqlite3_step(statement) == SQLITE_ERROR) {
const char *errorMsg = sqlite3_errmsg(db);
errorQuery = [self createDBErrorWithDescription:[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]
andCode:kDBErrorQuery];
}
sqlite3_finalize(statement);
errorQuery = [self closeDatabase];
}
else {
errorQuery = openError;
} return errorQuery;
} - (NSInteger)getLastInsertRowID { NSError *openError = nil; sqlite3_int64 rowid = ; //Check if database is open and ready.
if (db == nil) {
openError = [self openDatabase];
} if (openError == nil) {
rowid = sqlite3_last_insert_rowid(db);
} return (NSInteger)rowid;
} /**
* Does a SELECT query and gets the info from the db.
*
* The return array contains an NSDictionary for row, made as: key=columName value= columnValue.
*
* For example, if we have a table named "users" containing:
* name | pass
* -------------
* admin| 1234
* pepe | 5678
*
* it will return an array with 2 objects:
* resultingArray[0] = name=admin, pass=1234;
* resultingArray[1] = name=pepe, pass=5678;
*
* So to get the admin password:
* [[resultingArray objectAtIndex:0] objectForKey:@"pass"];
*
* @param sql the sql query (remember to use only a SELECT statement!).
*
* @return an array containing the rows fetched.
*/ - (NSArray *)getRowsForQuery:(NSString *)sql { NSMutableArray *resultsArray = [[NSMutableArray alloc] initWithCapacity:]; if (db == nil) {
[self openDatabase];
} sqlite3_stmt *statement;
const char *query = [sql UTF8String];
int returnCode = sqlite3_prepare_v2(db, query, -, &statement, NULL); if (returnCode == SQLITE_ERROR) {
const char *errorMsg = sqlite3_errmsg(db);
NSError *errorQuery = [self createDBErrorWithDescription:[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]
andCode:kDBErrorQuery];
NSLog(@"%@", errorQuery);
} while (sqlite3_step(statement) == SQLITE_ROW) {
int columns = sqlite3_column_count(statement);
NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:columns]; for (int i = ; i<columns; i++) {
const char *name = sqlite3_column_name(statement, i); NSString *columnName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; int type = sqlite3_column_type(statement, i); switch (type) {
case SQLITE_INTEGER:
{
int value = sqlite3_column_int(statement, i);
[result setObject:[NSNumber numberWithInt:value] forKey:columnName];
break;
}
case SQLITE_FLOAT:
{
float value = sqlite3_column_double(statement, i);
[result setObject:[NSNumber numberWithFloat:value] forKey:columnName];
break;
}
case SQLITE_TEXT:
{
const char *value = (const char*)sqlite3_column_text(statement, i);
[result setObject:[NSString stringWithCString:value encoding:NSUTF8StringEncoding] forKey:columnName];
break;
} case SQLITE_BLOB:
{
int bytes = sqlite3_column_bytes(statement, i);
if (bytes > ) {
const void *blob = sqlite3_column_blob(statement, i);
if (blob != NULL) {
[result setObject:[NSData dataWithBytes:blob length:bytes] forKey:columnName];
}
}
break;
} case SQLITE_NULL:
[result setObject:[NSNull null] forKey:columnName];
break; default:
{
const char *value = (const char *)sqlite3_column_text(statement, i);
[result setObject:[NSString stringWithCString:value encoding:NSUTF8StringEncoding] forKey:columnName];
break;
} } //end switch } //end for [resultsArray addObject:result]; } //end while
sqlite3_finalize(statement); [self closeDatabase]; return resultsArray; } /**
* Closes the database.
*
* @return nil if everything was ok, NSError in other case.
*/ - (NSError *) closeDatabase { NSError *error = nil; if (db != nil) {
if (sqlite3_close(db) != SQLITE_OK){
const char *errorMsg = sqlite3_errmsg(db);
NSString *errorStr = [NSString stringWithFormat:@"The database could not be closed: %@",[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]];
error = [self createDBErrorWithDescription:errorStr andCode:kDBFailAtClose];
} db = nil;
} return error;
} /**
* Creates an SQL dump of the database.
*
* This method could get a csv format dump with a few changes.
* But i prefer working with sql dumps ;)
*
* @return an NSString containing the dump.
*/ - (NSString *)getDatabaseDump { NSMutableString *dump = [[NSMutableString alloc] initWithCapacity:]; // info string ;) please do not remove it
[dump appendString:@";\n; Dump generated with SQLiteManager4iOS \n;\n; By Misato (2011)\n"];
[dump appendString:[NSString stringWithFormat:@"; database %@;\n", [databaseName lastPathComponent]]]; // first get all table information NSArray *rows = [self getRowsForQuery:@"SELECT * FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';"];
// last sql query returns something like:
// {
// name = users;
// rootpage = 2;
// sql = "CREATE TABLE users (id integer primary key autoincrement, user text, password text)";
// "tbl_name" = users;
// type = table;
// } //loop through all tables
for (int i = ; i<[rows count]; i++) { NSDictionary *obj = [rows objectAtIndex:i];
//get sql "create table" sentence
NSString *sql = [obj objectForKey:@"sql"];
[dump appendString:[NSString stringWithFormat:@"%@;\n",sql]]; //get table name
NSString *tableName = [obj objectForKey:@"name"]; //get all table content
NSArray *tableContent = [self getRowsForQuery:[NSString stringWithFormat:@"SELECT * FROM %@",tableName]]; for (int j = ; j<[tableContent count]; j++) {
NSDictionary *item = [tableContent objectAtIndex:j]; //keys are column names
NSArray *keys = [item allKeys]; //values are column values
NSArray *values = [item allValues]; //start constructing insert statement for this item
[dump appendString:[NSString stringWithFormat:@"insert into %@ (",tableName]]; //loop through all keys (aka column names)
NSEnumerator *enumerator = [keys objectEnumerator];
id obj;
while (obj = [enumerator nextObject]) {
[dump appendString:[NSString stringWithFormat:@"%@,",obj]];
} //delete last comma
NSRange range;
range.length = ;
range.location = [dump length]-;
[dump deleteCharactersInRange:range];
[dump appendString:@") values ("]; // loop through all values
// value types could be:
// NSNumber for integer and floats, NSNull for null or NSString for text. enumerator = [values objectEnumerator];
while (obj = [enumerator nextObject]) {
//if it's a number (integer or float)
if ([obj isKindOfClass:[NSNumber class]]){
[dump appendString:[NSString stringWithFormat:@"%@,",[obj stringValue]]];
}
//if it's a null
else if ([obj isKindOfClass:[NSNull class]]){
[dump appendString:@"null,"];
}
//else is a string ;)
else{
[dump appendString:[NSString stringWithFormat:@"'%@',",obj]];
} } //delete last comma again
range.length = ;
range.location = [dump length]-;
[dump deleteCharactersInRange:range]; //finish our insert statement
[dump appendString:@");\n"]; } } return dump;
} @end #pragma mark -
@implementation SQLiteManager (Private) /**
* Gets the database file path (in NSDocumentDirectory).
*
* @return the path to the db file.
*/ - (NSString *)getDatabasePath{ if([[NSFileManager defaultManager] fileExistsAtPath:databaseName]){
// Already Full Path
return databaseName;
} else {
// 判断文件夹是否存在,不存在则创建文件夹
NSString *docsDir = [NSHomeDirectory() stringByAppendingString:FOLDER_PATH];
if (SQLiteManager_fileOrFolderExistFromSandbox(docsDir) == NO) {
SQLiteManager_createFolderForSandbox(docsDir);
} return [docsDir stringByAppendingPathComponent:databaseName];
}
} #pragma mark 沙盒中创建文件夹
BOOL SQLiteManager_createFolderForSandbox(NSString *filePath)
{
return [[NSFileManager defaultManager] createDirectoryAtPath:filePath
withIntermediateDirectories:YES attributes:nil error:nil];
} #pragma mark 文件或者文件夹是否存在
BOOL SQLiteManager_fileOrFolderExistFromSandbox(NSString *filePath)
{
return [[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:NO];
} /**
* Creates an NSError.
*
* @param description the description wich can be queried with [error localizedDescription];
* @param code the error code (code erors are defined as enum in the header file).
*
* @return the NSError just created.
*
*/ - (NSError *)createDBErrorWithDescription:(NSString*)description andCode:(int)code { NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:description, NSLocalizedDescriptionKey, nil];
NSError *error = [NSError errorWithDomain:@"SQLite Error" code:code userInfo:userInfo]; return error;
} @end

以下是增删改查以及常规操作:

打开数据库

    //打开数据库
SQLiteManager *dbManager = [[SQLiteManager alloc] initWithDatabaseNamed:@"Y.X.db"];

创建表

    NSError *error = nil;
//创建表
/*
id int 主键
user text
password text
*/
error = [dbManager doQuery:@"CREATE TABLE IF NOT EXISTS users (id integer primary key autoincrement, user text, password text);"];
if (error != nil) {
NSLog(@"Error: %@",[error localizedDescription]);
}

插入

    //在表中插入一条记录
error = [dbManager doQuery:@"insert into users (user, password) values ('YouXian','19871220');"];
if (error != nil) {
NSLog(@"Error: %@",[error localizedDescription]);
}

查询

    //查询记录
[[dbManager getRowsForQuery:@"SELECT * FROM users"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"%@", obj);
}];

修改

    //修改一条记录
error = [dbManager doQuery:@"update users set user = 'YX',password = '1' where id = 1;"];
if (error != nil) {
NSLog(@"Error: %@",[error localizedDescription]);
}

删除

    //删除一条记录
error = [dbManager doQuery:@"delete from users where id = 1;"];
if (error != nil) {
NSLog(@"Error: %@",[error localizedDescription]);
}

心得:

如果觉得自己想去捣鼓 C 语言级别的 API 不怕崩溃,可以去试试,对于我而言,不是我对其不感兴趣不愿意研究他,只是,在对性能没有要求的前提下花精力去造车轮子完全没有必要,真心话.

使用 SQLiteManager 操作 sqlite3 数据库的更多相关文章

  1. 《Python操作SQLite3数据库》快速上手教程

    为什么使用SQLite数据库? 对于非常简单的应用而言,使用文件作为持久化存储通常就足够了,但是大多数复杂的数据驱动的应用需要全功能的关系型数据库.SQLite的目标则是介于两者之间的中小系统.它有以 ...

  2. 多人操作sqlite3数据库冲突问题解决方法

    问题描述:sqlite3数据放置在某一台电脑的某个共享文件夹下,操作数据库的应用程序安装在同一局域网下的很多台电脑上,由于存在多人同时使用该应用程序,所以存在多人同时操作数据库的情况.经过测试发现,最 ...

  3. (一一三)使用系统自带框架操作SQLite3数据库

    系统自带的框架是基于C语言的,使用比较繁琐. 下面是使用步骤: 首先导入libsqlite3.0.dylib. ①在Document目录下打开数据库,如果没有则创建. NSString *sqlite ...

  4. iOS: 学习笔记, 使用FMDatabase操作sqlite3

    使用FMDatabase操作sqlite3数据库非常简单和方便 // // main.m // iOSDemo0602_sqlite3 // // Created by yao_yu on 14-6- ...

  5. 在cmd窗口中查询android的sqlite3数据库表之步骤

    本文主要是写了一个android程序对sqlite3中数据库的employee表的插入.删除的操作,然后在cmd窗口中用sql命令查询employee表的操作过程. 1.第一步:首先把程序写好. 1. ...

  6. Flask:操作SQLite3(0.1)

    Windows 10家庭中文版,Python 3.6.4,Flask 1.0.2 本文介绍了第一次在Flask框架中操作SQLite3数据库的测试,参考了官网的文档Using SQLite 3 wit ...

  7. IOS数据库操作SQLite3使用详解(转)

    iPhone中支持通过sqlite3来访问iPhone本地的数据库.具体使用方法如下1:添加开发包libsqlite3.0.dylib首先是设置项目文件,在项目中添加iPhone版的sqlite3的数 ...

  8. python sqlite3 数据库操作

    python sqlite3 数据库操作 SQLite3是python的内置模块,是一款非常小巧的嵌入式开源数据库软件. 1. 导入Python SQLite数据库模块 import sqlite3 ...

  9. 【Android】Android连接SQLite3数据库的操作

    在前面使用SQLite3的时候,并没有留意到有SQLiteOpenHelper这个类,所以只好在Activity里面去创建和维护数据库跟数据表的创建. 但是,现在有了SQLiteOpenHelper这 ...

随机推荐

  1. PHP array_diff 计算数组的差集

    array_diff (PHP 4 >= 4.0.1, PHP 5) array_diff — 计算数组的差集 说明 array array_diff ( array $array1 , arr ...

  2. hdu 1085 有num1个 1 ,num2个 2 ,num3个 5 (母函数)

    有num1个 1 ,num2个 2 ,num3个 5问它们不能组成的最小正整数是谁 样例的母函数 (1+X)(1+X2)(1+X5+X10+X15)展开后 X4的系数为0 Sample Input1 ...

  3. snmp常见操作

    常用snmp OID说明下面这些值可以手动连接进行获取数据: 用zabbix监控交换机和路由器需要一款能够获取网络设备OID的工具,可用getif来获得OID 也可以使用snmpwalk 配置交换机的 ...

  4. USACO 5.4 Canada Tour

    Canada Tour You have won a contest sponsored by an airline. The prize is a ticket to travel around C ...

  5. 前端-JS-BOM和DOM

    前戏 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的一些交互,我们需要继续学习BOM和DO ...

  6. Adobe Photoshop CC 2017-18.0安装教程

    Adobe Photoshop CC 2017-18.0安装教程 注:下载链接在文章后面 第一步:首先请将电脑的网络断开,很简单:禁用本地连接或者拔掉网线,这样就可以免除登录Creative Clou ...

  7. Linux信号量同步共享内存实验.

    Linux信号量同步共享内存实验. Linux信号量同步共享内存实验. 简述 程序流程 信号量和共享内存的系统函数 信号量系统函数及接口 共享内存系统函数及接口 写程序 读程序 简述 本文主要内容是自 ...

  8. 【基础知识】Dom基础

    [学习日记]Dom基础 1.   内容:使用JavaScript操作Dom进行DHTML开发 2.   目标:能共使用JavaScript操作Dom实现常见的DHTML效果 3.   DHTML= C ...

  9. 日报 18/07/15 Java 性能优化

    尽量指定类和方法的final修饰符 带有final修饰符的类是不可派生的 在java核心api中 有许多应用final的例子 例如 java.lang.string整个类都是final的 为类指定fi ...

  10. [ 转载 ] Mysql 数据库常用命令

    完整的创建数据库例子: >create database db_test default character set utf8 collate utf8_general_ci; >use ...