在IOS開發中遇到資料庫儲存很正常。
但是要儲存的對象中如果不全是基礎資料型別 (Elementary Data Type),或者不是資料庫是支援的類型,是不是瞬間石化了。
例如:
在一個Message對象中包含一個Attatchment對象,現在要求是每個Message要儲存起來,選擇sqlite資料庫儲存是不是很正常(反正我是用的他),但是sqlite中並不支援Attatchment類型,怎麼搞???
法一:
把Attatchment中的所有屬性放到Message中,沒有問題,也是一個很好的辦法,但是這樣是不是有點牽強呢?
法二:
也是本人今天要介紹的方法,就是對象歸檔成NSData後在儲存。查詢出來的時候在解檔一下就OK了。
廢話少說,直接給出代碼(資料庫儲存用到了第三方庫FMDB):
源碼
給出Attatchment實體類(只是做一個簡單的測試就不給出詳細定義了):
//
// Attatchment.h
// Demo
//
#import <Foundation/Foundation.h>
@interface Attatchment : NSObject <NSCoding>{
NSString *_localPath;
}
@property (nonatomic , copy) NSString *localPath;
@end
//
// Attatchment.m
// Demo
//
#import "Attatchment.h"
@implementation Attatchment
@synthesize localPath = _localPath;
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:_localPath forKey:@"localPath"];
}
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if(self != nil){
_localPath = [[aDecoder decodeObjectForKey:@"localPath"] retain];
}
return self;
}
@end
在給出Message實體(也是一個簡單的定義):
//
// Message.h
// Demo
//
#import <Foundation/Foundation.h>
#import "Attatchment.h"
@interface Message : NSObject{
NSString *_messageId;
Attatchment *_att;
}
@property (nonatomic , copy) NSString *messageId;
@property (nonatomic , retain) Attatchment *att;
@end
//
// Message.m
// Demo
//
#import "Message.h"
@implementation Message
@synthesize messageId = _messageId;
@synthesize att = _att;
@end
這樣兩個實體就定義完了。
重要的過程就是儲存了:
這裡給出了自己寫的一個DBManager(寫的粗糙,見諒!)
//
// DBManager.h
// FetionHD
//
#import <Foundation/Foundation.h>
#import "FMDatabase.h"
#import "Message.h"
#define dataBasePath [[(NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)) lastObject]stringByAppendingPathComponent:dataBaseName]
#define dataBaseName @"dataBase.sqlite"
@interface DBManager : NSObject
/****/
/**
* @brief 資料庫物件單例方法
*
* @return 返回FMDateBase資料庫操作對象
*/
+ (FMDatabase *)createDataBase;
/**
* @brief 關閉資料庫
*/
+ (void)closeDataBase;
/**
* @brief 清空資料庫內容
*/
+ (void)deleteDataBase;
/**
* @brief 判斷表是否存在
*
* @param tableName 表明
*
* @return 建立是否成功
*/
+ (BOOL) isTableExist:(NSString *)tableName;
/**
* @brief 建立所有表
*
* @return
*/
+ (BOOL)createTable;
/**
* @brief 添加chatdata 如果主鍵重複就更新
*
* @param chatData 要儲存的chatdata
*
* @return 返回是否儲存或者更新成功
*/
+ (BOOL) saveOrUpdataMessage:(Message*)chatData;
+ (Message *) selectMessageByMessageId:(NSString*)messageId;
@end
//
// DBManager.m
// FetionHD
#import <Foundation/Foundation.h>
#import "DBManager.h"
#define debugMethod(...) NSLog((@"In %s,%s [Line %d] "), __PRETTY_FUNCTION__,__FILE__,__LINE__,##__VA_ARGS__)
static FMDatabase *shareDataBase = nil;
@implementation DBManager
/**
建立資料庫類的單例對象
**/
//+ (FMDatabase *)createDataBase {
// //debugMethod();
// @synchronized (self) {
// if (shareDataBase == nil) {
//
// shareDataBase = [[FMDatabase databaseWithPath:dataBasePath] retain];
// }
// return shareDataBase;
// }
//}
//這種方法可以達到安全執行緒,但多次調用時會導致效能顯著下降
+ (FMDatabase *)createDataBase {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareDataBase = [[FMDatabase databaseWithPath:dataBasePath] retain];
});
return shareDataBase;
}
/**
判斷資料庫中表是否存在
**/
+ (BOOL) isTableExist:(NSString *)tableName
{
FMResultSet *rs = [shareDataBase executeQuery:@"select count(*) as 'count' from sqlite_master where type ='table' and name = ?", tableName];
while ([rs next])
{
// just print out what we've got in a number of formats.
NSInteger count = [rs intForColumn:@"count"];
NSLog(@"%@ isOK %d", tableName,count);
if (0 == count)
{
return NO;
}
else
{
return YES;
}
}
return NO;
}
/**
建立表
**/
+ (BOOL)createTable {
debugMethod();
NSLog(@"%@",dataBasePath);
if (1){
{
shareDataBase = [DBManager createDataBase];
if ([shareDataBase open]) {
if (![DBManager isTableExist:@"message_table"]) {
NSString *sql = @"CREATE TABLE \"message_table\" (\"message_id\" TEXT PRIMARY KEY NOT NULL check(typeof(\"message_id\") = 'text') , \"att\" BLOB)";
NSLog(@"no Medicine ");
[shareDataBase executeUpdate:sql];
}
[shareDataBase close];
}
}
}
return YES;
}
/**
關閉資料庫
**/
+ (void)closeDataBase {
if(![shareDataBase close]) {
NSLog(@"資料庫關閉異常,請檢查");
return;
}
}
/**
刪除資料庫
**/
+ (void)deleteDataBase {
if (shareDataBase != nil) {
//這裡進行資料庫表的刪除工作
}
}
+ (BOOL) saveOrUpdataMessage:(Message*)message
{
BOOL isOk = NO;
shareDataBase = [DBManager createDataBase];
if ([shareDataBase open]) {
isOk = [shareDataBase executeUpdate:
@"INSERT INTO \"message_table\" (\"message_id\",\"att\") VALUES(?,?)",message.messageId,[NSKeyedArchiver archivedDataWithRootObject:message.att]];
[shareDataBase close];
}
return isOk;
}
+ (Message *) selectMessageByMessageId:(NSString*)messageId
{
Message *m = nil;
shareDataBase = [DBManager createDataBase];
if ([shareDataBase open]) {
FMResultSet *s = [shareDataBase executeQuery:[NSString stringWithFormat:@"SELECT * FROM \"message_table\" WHERE \"message_id\" = '%@'",messageId]];
if ([s next]) {
m = [[Message alloc] init];
m.messageId = [s stringForColumn:@"message_id"];
m.att = [NSKeyedUnarchiver unarchiveObjectWithData:[s dataForColumn:@"att"]];
}
[shareDataBase close];
}
return m;
}
@end
剩下的工作就是搞個介面調用一下@_@
- (IBAction)addAction:(id)sender{
Message *message = [[Message alloc] init];
Attatchment *att = [[Attatchment alloc] init];
message.messageId = @"11";
message.att = att;
att.localPath = @"a.png";
[DBManager createTable];
[DBManager saveOrUpdataMessage:message];
[att release];
[message release];
}
- (IBAction)selectAction:(id)sender{
Message *message = [DBManager selectMessageByMessageId:@"11"];
NSLog(@"%@",message.att.localPath);
NSLog(@"%@",message.messageId);
}
記得在工程中添加FMDB庫和sqlite3庫,跑一下試試會有想不到的效果哦。
這是本人的第一篇博文,歡迎吐槽@_@。