(写动画库有点烦,等我写好了,会放到git上,到时候放网址。)
SQLite是嵌入式的和轻量级的sql数据库。广泛用于包括浏览器、ios、android以及一些便携需求的小型web应用系统。
但是它也是关系型数据库,所以在功能上是要强于coreData的。
一、离线缓存
在项目开发中,通常都需要对数据进行离线缓存的处理,就像我上一篇说的,新闻类就很需要这样做。
离线缓存一般都是把数据保存到项目的沙盒中。有以下几种方式
(1)归档:NSCodeing、NSKeyedArchiver(我上一篇说过了)
(2)偏好设置:NSUserDefaults
(3)Plist存储:writeToFile
所以,这种情况下,上面的三种就没法应付了。
好了 想用它 自然先导库
libsqlite3.dylib, 3是第三个版本的意思。推荐一个博文你们阅读下
由于原生是C语言格式,所以我们先讲原生,然后给大家讲个三方库,很强大,开发时经常用到。
先引用库。#import "sqlite3.h"
然后创建数据库,如果没有就创建(sqlite3_open):
为了避免重复创建,所以在创建前先判断,不过说到这里我想起来了,强调一下,之前一直说 SQLite3不是线程安全,所以所以 一般情况都是加上锁。但是最近我看了一篇博文,之后发现其实是是支持的。等文章最后再说。
<span style="font-size:18px;">sqlite3* database_; -(BOOL) open{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *path = [documentsDirectory stringByAppendingPathComponent:@"mydb.sql"]; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL find = [fileManager fileExistsAtPath:path]; //找到数据库文件mydb.sql if (find) { NSLog(@"Database file have already existed."); if(sqlite3_open([path UTF8String], &database_) != SQLITE_OK) { sqlite3_close(database_); NSLog(@"Error: open database file."); return NO; } return YES; } if(sqlite3_open([path UTF8String], &database_) == SQLITE_OK) { bFirstCreate_ = YES; [self createChannelsTable:database_];//在后面实现函数createChannelsTable return YES; } else { sqlite3_close(database_); NSLog(@"Error: open database file."); return NO; } return NO; }</span>
创建表格:
<span style="font-size:18px;">//创建表格,假设有五个字段,(id,cid,title,imageData ,imageLen ) //说明一下,id为表格的主键,必须有。 //cid,和title都是字符串,imageData是二进制数据,imageLen 是该二进制数据的长度。 - (BOOL) createChannelsTable:(sqlite3*)db { char *sql = "CREATE TABLE channels (id integer primary key, \ cid text, \ title text, \ imageData BLOB, \ imageLen integer)"; sqlite3_stmt *statement; if(sqlite3_prepare_v2(db, sql, -1, &statement, nil) != SQLITE_OK) { NSLog(@"Error: failed to prepare statement:create channels table"); return NO; } int success = sqlite3_step(statement); sqlite3_finalize(statement); if ( success != SQLITE_DONE) { NSLog(@"Error: failed to dehydrate:CREATE TABLE channels"); return NO; } NSLog(@"Create table 'channels' successed."); return YES; }</span>
插入数据:
<span style="font-size:18px;">- (BOOL) insertOneChannel:(Channel*)channel { NSData* ImageData = UIImagePNGRepresentation( channel.image_); NSInteger Imagelen = [ImageData length]; sqlite3_stmt *statement; static char *sql = "INSERT INTO channels (cid,title,imageData,imageLen)\ VALUES(?,?,?,?)"; //问号的个数要和(cid,title,imageData,imageLen)里面字段的个数匹配,代表未知的值,将在下面将值和字段关联。 int success = sqlite3_prepare_v2(database_, sql, -1, &statement, NULL); if (success != SQLITE_OK) { NSLog(@"Error: failed to insert:channels"); return NO; } //这里的数字1,2,3,4代表第几个问号 sqlite3_bind_text(statement, 1, [channel.id_ UTF8String], -1, SQLITE_TRANSIENT); sqlite3_bind_text(statement, 2, [channel.title_ UTF8String], -1, SQLITE_TRANSIENT); sqlite3_bind_blob(statement, 3, [ImageData bytes], Imagelen, SQLITE_TRANSIENT); sqlite3_bind_int(statement, 4, Imagelen); success = sqlite3_step(statement); sqlite3_finalize(statement); if (success == SQLITE_ERROR) { NSLog(@"Error: failed to insert into the database with message."); return NO; } NSLog(@"Insert One Channel#############:id = %@",channel.id_); return YES; }</span>
- (void) getChannels:(NSMutableArray*)fChannels
{
sqlite3_stmt *statement = nil;
char *sql = "SELECT * FROM channels";
if (sqlite3_prepare_v2(database_, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(@"Error: failed to prepare statement with message:get channels.");
}
//查询结 果集中一条一条的遍历所有的记录,这里的数字对应的是列值。 while (sqlite3_step(statement) == SQLITE_ROW)
{
char* cid = (char*)sqlite3_column_text(statement, 1);
char* title = (char*)sqlite3_column_text(statement, 2);
Byte* imageData = (Byte*)sqlite3_column_blob(statement, 3);
int imageLen = sqlite3_column_int(statement, 4);
Channel* channel = [[Channel alloc] init];
if(cid)
channel.id_ = [NSString stringWithUTF8String:cid];
if(title)
channel.title_ = [NSString stringWithUTF8String:title];
if(imageData)
{
UIImage* image = [UIImage imageWithData:[NSData dataWithBytes:imageData length:imageLen]];
channel.image_ = image;
}
[fChannels addObject:channel];
[channel release];
}
sqlite3_finalize(statement);
}
上面的方法不用说,就是看着不舒服,所以,我给大家推荐个第三方库,FMDB
毕竟第三方库,做的真的很好。FMDB下载
通过FMDB,你可以定制你自己的DBManager,我做了一个简单的,直接上代码,很容易懂。
这是我的.h,自己定的一些接口,完成增删查改的功能。
<span style="font-size:18px;">// // DBManager.h // FMDBDemo // // Created by JackYang on 15/9/8. // Copyright (c) 2015年 JackYang. All rights reserved. // #import <Foundation/Foundation.h> #import "PersonModel.h" @interface DBManager : NSObject +(instancetype)sharedInstance; - (void)addPerson:(PersonModel*)model; - (void)deletePerson:(PersonModel*)model; - (void)updatePerson:(PersonModel*)model; - (NSArray*)allPerson; - (BOOL)isPersonExists:(PersonModel*)model; @end</span>
然后是.m的实现,由于我只是写了一个Demo,所以只是做了一个PersonModel,实现就是增加,删除person,你们可以自己创建自己想要的表,定制自己的sql语句,w3school 有sql教程。不会的可以去看看。
<span style="font-size:18px;">// // DBManager.m // FMDBDemo // // Created by JackYang on 15/9/8. // Copyright (c) 2015年 JackYang. All rights reserved. // #import "DBManager.h" #import "FMDatabase.h" @interface DBManager () //定义数据库,FMDatabase 来描述操作数据库 @property(nonatomic)FMDatabase *db; @end @implementation DBManager //数据库本质也是一个文件,DBMS 来进行管理 +(instancetype)sharedInstance { static DBManager *s_dbManager = nil; // //@synchronized(self) 内部实现时加锁,防止多线程不安全 // @synchronized(self){ // if (s_dbManager == nil) { // s_dbManager = [[DBManager alloc]init]; // } // return s_dbManager; // } //两种方式可以创建单例。但是这是共享单例,你可以选择吧init隐藏,就是换个方法名,不对外公开。 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ s_dbManager = [[DBManager alloc]init]; }); return s_dbManager; } - (id)init { if (self = [super init]) { //创建数据库,在数据库中创建表 NSString *dbFilePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Person.db"]; NSLog(@"DB Path = %@",dbFilePath); //使用数据库文件路径,初始化db实例 self.db = [[FMDatabase alloc]initWithPath:dbFilePath]; //打开数据库,如果数据库文件不存在,FMDB自动创建一个,并打开数据库 if(![self.db open]){ NSLog(@"打开数据库失败"); }else{ [self createPersonTable]; } } return self; } - (void)createPersonTable { NSString *sql = @"CREATE TABLE IF NOT EXISTS Persons(PersonId INTEGER PRIMARY KEY AUTOINCREMENT,Age INTEGER,FirstName TEXT,LastName TEXT,Address TEXT,City varchar(255),HeadImage BLOB)"; //使用FMDB时,对sql语句的操作 //executeQuery 针对select 语句 //[self.db executeQuery:<#(NSString *), ...#>]; //update ,delete,insert 使用executeUpdate if (![self.db executeUpdate:sql]) { NSLog(@"创建Person表失败"); [self.db close]; }; } - (void)addPerson:(PersonModel*)model { //?代表是占位符,该位置会填充相应的值,注意:传入的值一定是id类型,如果是整数,浮点数等,需要转成NSNumber NSString *sql = @"INSERT INTO Persons(Age,FirstName,LastName,Address,City,HeadImage) VALUES(?,?,?,?,?,?)"; //UIImagePNGRepresentation 把图片转成NSData的数据 NSData *imageData = UIImagePNGRepresentation(model.headImage); BOOL result = [self.db executeUpdate:sql,@(model.age),model.firstName,model.lastName,model.address,model.city,imageData]; if(result == NO){ NSLog(@"插入数据失败"); } } - (void)deletePerson:(PersonModel*)model { NSString *sql = @"DELETE FROM Persons WHERE PersonId = ?"; BOOL result = [self.db executeUpdate:sql,@(model.personId)]; if(result == NO){ NSLog(@"插入数据失败"); } } - (void)updatePerson:(PersonModel*)model { NSString *sql = @"UPDATE Persons SET Age = ?,FirstName = ?,LastName = ?,Address = ?,City = ? WHERE PersonId = ?"; BOOL result = [self.db executeUpdate:sql,@(model.age),model.firstName,model.lastName,model.address,model.city,@(model.personId)]; if (!result) { NSLog(@"更新记录失败"); } } - (NSArray*)allPerson { NSMutableArray *resultArray = [NSMutableArray array]; NSString *sql = @"SELECT * FROM Persons"; FMResultSet *resultSet = [self.db executeQuery:sql]; //resultSet.next 指向的是一条记录 while (resultSet.next) { PersonModel *person = [[PersonModel alloc]init]; person.personId = [resultSet intForColumn:@"personId"]; person.age = [resultSet intForColumn:@"Age"]; person.firstName = [resultSet stringForColumn:@"FirstName"]; person.lastName = [resultSet stringForColumn:@"LastName"]; person.address = [resultSet stringForColumn:@"Address"]; person.city = [resultSet stringForColumn:@"City"]; NSData *imageData = [resultSet dataForColumn:@"HeadImage"]; person.headImage = [UIImage imageWithData:imageData]; [resultArray addObject:person]; } [resultSet close]; return resultArray; } - (BOOL)isPersonExists:(PersonModel*)model { BOOL result = NO; NSString *sql = @"SELECT * FROM Persons WHERE personId = ?"; FMResultSet *resultSet = [self.db executeQuery:sql,@(model.personId)]; if (resultSet.next) { result = YES; } [resultSet close]; return result; } @end</span>
而且,在查的时候 也是和原生一样 使用结果集,进行遍历。
剩下的是就是使用你的工具类,存储你的数据了
<span style="font-size:18px;">- (IBAction)getAllPerson:(id)sender { self.personList = [[DBManager sharedInstance] allPerson]; [self.tableView reloadData]; } - (IBAction)addPerson:(id)sender { PersonModel *person = [[PersonModel alloc]init]; person.age = 20; person.firstName = @"123"; person.lastName = @"456"; person.address = @"china"; person.city = @"HeFei"; person.headImage = [UIImage imageNamed:@"[email protected]"]; [[DBManager sharedInstance] addPerson:person]; } - (IBAction)deletePerson:(id)sender { PersonModel *person = [self.personList objectAtIndex:0]; [[DBManager sharedInstance] deletePerson:person]; } - (IBAction)updatePerson:(id)sender { PersonModel *person = [self.personList objectAtIndex:0]; person.firstName = @"aaa"; person.lastName = @"bbb"; [[DBManager sharedInstance] updatePerson:person]; } </span>
没错,就是原生sql线程安全问题。
这里我就不搬别人的博文了,你们自己去看
好吧,就这些了,啥不懂得 可以问我。