在IOS开发中遇到数据库保存很正常。但是要保存的对象中如果不全是基本数据类型,或者不是数据库是支持的类型,是不是瞬间石化了。
例如:
在一个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库,跑一下试试会有想不到的效果哦。