在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库,跑一下试试会有想不到的效果哦。