1.table.h
#import
#import "DatabaseManager.h"
@interface DataTableManager : NSObject
@property(nonatomic, copy, readonly) NSString *pk;
+ (NSRecursiveLock *)recursiveLock;
/**
* 初始化DB
*
*/
+ (instancetype)dataTable;
/**
* 动态创建类
*
*/
+ (Class)establishClassName:(NSString *)className superClass:(Class)superClass;
/**
* 初始化BD
*
*/
- (instancetype)init;
/**
* 编码 (处理特殊字符与汉字)
*
*/
+ (NSString *)encoded:(NSString *)string;
/**
* 解码 (处理特殊字符与汉字)
*
*/
+ (NSString *)decoded:(NSString *)string;
/**
* 结果查询
*
*/
- (instancetype)initWithResultSet:(FMResultSet *)resultSet;
/**
* 保存表
*/
- (BOOL)saveDataTable;
/**
* 删除表
*/
- (BOOL)deleteDataTable;
/**
* 查找表
*
*/
+ (NSArray
/**
* 查找表
*
*/
+ (id)findSpecificDataTableByProperty:(NSString *)property value:(id)value;
/**
* 查找表
*
*/
+ (NSArray
/**
* 删除表
*
*/
+ (BOOL)deleteDataTableByProperty:(NSString *)property value:(id)value;
/**
* 删除表
*
*/
+ (BOOL)deleteDataTableBySQL:(NSString *)sql;
@end
2.table.m
#import "DataTableManager.h"
#import
#import
@interface DataTableManager ()
@property(nonatomic, copy) NSString *pk;
@end
@implementation DataTableManager
static NSRecursiveLock *_NSRecursiveLock;
+ (NSRecursiveLock *)recursiveLock
{
static dispatch_once_t once;
dispatch_once(&once, ^{
_NSRecursiveLock = [[NSRecursiveLock alloc] init];
});
return _NSRecursiveLock;
}
+ (instancetype)dataTable
{
return [[self alloc] init];
}
+ (Class)establishClassName:(NSString *)className superClass:(Class)superClass
{
const char *classNameChar = [className UTF8String];
Class kclass = objc_getClass(classNameChar);
if (!kclass) {
NSAssert(![superClass isKindOfClass:[DataTableManager class]], @"DataTableManager establishClassName :: superClass ! isKindOfClass : [DataTableManager class]");
kclass = objc_allocateClassPair(superClass, classNameChar, 0);
objc_registerClassPair(kclass);
}
return kclass;
}
+ (NSString *)encoded:(NSString *)string
{
if (!string) {
return nil;
}
return [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
}
+ (NSString *)decoded:(NSString *)string
{
if (!string) {
return nil;
}
return [string stringByRemovingPercentEncoding];
}
- (instancetype)init
{
self = [super init];
if (self) {
[[DataTableManager recursiveLock] lock];
FMResultSet *resultSet = [[DatabaseManager manager].database executeQuery:@"select count (*) from sqlite_master where type = 'table' and name = ?", NSStringFromClass([self class])];
[resultSet next];
int set = [resultSet intForColumnIndex:0];
[resultSet close];
if (set) {
FMResultSet *key = [[DatabaseManager manager].database executeQuery:[NSString stringWithFormat:@"PRAGMA table_info([%@])", NSStringFromClass([self class])]];
NSMutableDictionary *keyType = [NSMutableDictionary dictionary];
while ([key next]) {
NSString *name = [key stringForColumn:@"name"];
NSString *type = [key stringForColumn:@"type"];
[keyType setObject:type forKey:name];
}
[key close];
NSDictionary *types = [self propertiesWithEncodedTypes];
for (NSString *oneProp in [types allKeys]) {
if (oneProp && ![oneProp isEqualToString:@"pk"]) {
if (![keyType objectForKey:oneProp]) {
NSMutableString *createSQL = [NSMutableString string];
NSString *propType = [types objectForKey:oneProp];
// Integer Types
if ([propType isEqualToString:@"i"] || // int
[propType isEqualToString:@"I"] || // unsigned int
[propType isEqualToString:@"l"] || // long
[propType isEqualToString:@"L"] || // usigned long
[propType isEqualToString:@"q"] || // long long
[propType isEqualToString:@"Q"] || // unsigned long long
[propType isEqualToString:@"s"] || // short
[propType isEqualToString:@"S"] || // unsigned short
[propType isEqualToString:@"B"]) { // bool or _Bool
[createSQL appendFormat:@"%@ INTEGER", oneProp];
}
// Character Types
else if ([propType isEqualToString:@"c"] || // char
[propType isEqualToString:@"C"]) { // unsigned char
[createSQL appendFormat:@"%@ TEXT", oneProp];
} else if ([propType isEqualToString:@"f"] || // float
[propType isEqualToString:@"d"]) { // double
[createSQL appendFormat:@"%@ REAL", oneProp];
} else if ([propType hasPrefix:@"@"]) { // Object
[createSQL appendFormat:@"%@ TEXT", oneProp];
}
if (createSQL.length) {
if (![[DatabaseManager manager].database executeUpdate:[NSString stringWithFormat:@"ALTER TABLE %@ ADD %@", NSStringFromClass([self class]), createSQL]]) {
NSLog(@"ALTER TABLE %@ :: %@ ERROR", NSStringFromClass([self class]), oneProp);
} else {
NSLog(@"ALTER TABLE %@ :: %@ Succeed", NSStringFromClass([self class]), oneProp);
}
}
}
}
}
} else {
NSString *createSql = [NSString stringWithFormat:@"create table %@ ('pk' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL %@)", NSStringFromClass([self class]), [self tableKey]];
if (![[DatabaseManager manager].database executeUpdate:createSql]) {
NSLog(@"DataTableManager Create %@ ERROR", NSStringFromClass([self class]));
} else {
NSLog(@"DataTableManager Create %@ Succeed", NSStringFromClass([self class]));
}
}
[[DataTableManager recursiveLock] unlock];
}
return self;
}
- (NSString *)tableKey
{
NSDictionary *property = [self propertiesWithEncodedTypes];
NSMutableString *createSQL = [NSMutableString stringWithString:@""];
for (NSString *oneProp in property) {
NSString *propType = property[oneProp];
// Integer Types
if ([propType isEqualToString:@"i"] || // int
[propType isEqualToString:@"I"] || // unsigned int
[propType isEqualToString:@"l"] || // long
[propType isEqualToString:@"L"] || // usigned long
[propType isEqualToString:@"q"] || // long long
[propType isEqualToString:@"Q"] || // unsigned long long
[propType isEqualToString:@"s"] || // short
[propType isEqualToString:@"S"] || // unsigned short
[propType isEqualToString:@"B"]) { // bool or _Bool
[createSQL appendFormat:@", %@ INTEGER", oneProp];
}
// Character Types
else if ([propType isEqualToString:@"c"] || // char
[propType isEqualToString:@"C"]) { // unsigned char
[createSQL appendFormat:@", %@ TEXT", oneProp];
} else if ([propType isEqualToString:@"f"] || // float
[propType isEqualToString:@"d"]) { // double
[createSQL appendFormat:@", %@ REAL", oneProp];
} else if ([propType hasPrefix:@"@"]) { // Object
[createSQL appendFormat:@", %@ TEXT", oneProp];
}
}
return createSQL;
}
- (NSDictionary *)propertiesWithEncodedTypes
{
// Recurse up the classes, but stop at NSObject. Each class only reports its own properties, not those inherited from its superclass
NSMutableDictionary *theProps = [NSMutableDictionary dictionary];
unsigned int outCount;
objc_property_t *propList = class_copyPropertyList([self class], &outCount);
if (!outCount) {
propList = class_copyPropertyList([self superclass], &outCount);
}
int i;
// Loop through properties and add declarations for the create
for (i = 0; i < outCount; i++) {
objc_property_t oneProp = propList[i];
NSString *propName = [NSString stringWithUTF8String:property_getName(oneProp)];
NSString *attrs = [NSString stringWithUTF8String:property_getAttributes(oneProp)];
// Read only attributes are assumed to be derived or calculated
if ([attrs rangeOfString:@",R,"].location == NSNotFound) {
NSArray *attrParts = [attrs componentsSeparatedByString:@","];
if (attrParts != nil) {
if ([attrParts count] > 0) {
NSString *propType = [[attrParts objectAtIndex:0] substringFromIndex:1];
[theProps setObject:propType forKey:propName];
}
}
}
}
free(propList);
return theProps;
}
- (instancetype)initWithResultSet:(FMResultSet *)resultSet
{
self = [super init];
if (self) {
NSDictionary *property = [self propertiesWithEncodedTypes];
u_int outCount;
objc_property_t *methods = class_copyPropertyList([self class], &outCount);
if (!outCount) {
methods = class_copyPropertyList([self superclass], &outCount);
}
for (int i = 0; i < outCount; i++) {
objc_property_t objc_oneProp = methods[i];
NSString *strName = [NSString stringWithCString:property_getName(objc_oneProp) encoding:NSUTF8StringEncoding];
id v = [resultSet objectForColumn:strName];
if (v) {
NSString *properType = property[strName];
if ([properType isEqualToString:@"c"]) {
[self setValue:[NSNumber numberWithBool:(BOOL)v] forKey:strName];
} else {
[self setValue:[self jsonToObject:v] forKey:strName];
}
}
}
NSInteger pk = [resultSet intForColumn:@"pk"];
[self setValue:[NSNumber numberWithInteger:pk] forKey:@"pk"];
}
return self;
}
- (id)jsonToObject:(id)json
{
id copyJson = [json copy];
if ([copyJson isKindOfClass:[NSString class]]) {
copyJson = [DataTableManager decoded:copyJson];
if (!copyJson) {
return json;
}
NSData *jsonData = [copyJson dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
id data = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
if (err) {
return copyJson;
}
return data;
}
if ([copyJson isKindOfClass:[NSNull class]]) {
return @"";
}
return copyJson;
}
- (NSString *)parameterToJson:(id)parameter
{
NSError *parseError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameter options:NSJSONWritingPrettyPrinted error:&parseError];
return [DataTableManager encoded:[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]];
}
- (NSString *)insert
{
NSString *createSQL = [NSString stringWithFormat:@"INSERT INTO %@ ", NSStringFromClass([self class])];
u_int outCount;
objc_property_t *methods = class_copyPropertyList([self class], &outCount);
if (!outCount) {
methods = class_copyPropertyList([self superclass], &outCount);
}
NSString *key = @"(";
NSString *value = @" VALUES(";
for (int i = 0; i < outCount; i++) {
objc_property_t objc_oneProp = methods[i];
NSString *strName = [NSString stringWithCString:property_getName(objc_oneProp) encoding:NSUTF8StringEncoding];
key = [[key stringByAppendingString:strName] stringByAppendingString:@","];
id v = [self valueForKey:strName];
NSString *vClass = NSStringFromClass([v class]);
if (v && ![vClass isEqualToString:NSStringFromClass([NSNull class])]) {
if ([v isKindOfClass:[NSString class]]) {
NSString *str = [NSString stringWithFormat:@"'%@'", [DataTableManager encoded:v]];
value = [[value stringByAppendingString:str] stringByAppendingString:@","];
} else if ([v isKindOfClass:[NSArray class]]) {
NSString *str = [NSString stringWithFormat:@"'%@'", [self parameterToJson:v]];
value = [[value stringByAppendingString:str] stringByAppendingString:@","];
} else if ([v isKindOfClass:[NSDictionary class]]) {
NSString *str = [NSString stringWithFormat:@"'%@'", [self parameterToJson:v]];
value = [[value stringByAppendingString:str] stringByAppendingString:@","];
} else {
value = [[value stringByAppendingString:[NSString stringWithFormat:@"%@", v]] stringByAppendingString:@","];
}
} else {
value = [[value stringByAppendingString:@"NULL"] stringByAppendingString:@","];
}
}
if ([key hasSuffix:@","]) {
key = [[key substringToIndex:key.length - 1] stringByAppendingString:@")"];
}
if ([value hasSuffix:@","]) {
value = [[value substringToIndex:value.length - 1] stringByAppendingString:@")"];
}
createSQL = [[createSQL stringByAppendingString:key] stringByAppendingString:value];
return createSQL;
}
- (NSString *)update
{
NSString *createSQL = [NSString stringWithFormat:@"UPDATE %@ SET ", NSStringFromClass([self class])];
u_int outCount;
objc_property_t *methods = class_copyPropertyList([self class], &outCount);
if (!outCount) {
methods = class_copyPropertyList([self superclass], &outCount);
}
for (int i = 0; i < outCount; i++) {
objc_property_t objc_oneProp = methods[i];
NSString *strName = [NSString stringWithCString:property_getName(objc_oneProp) encoding:NSUTF8StringEncoding];
id v = [self valueForKey:strName];
strName = [[@" " stringByAppendingString:strName] stringByAppendingString:@" = "];
NSString *vClass = NSStringFromClass([v class]);
if (v && ![vClass isEqualToString:NSStringFromClass([NSNull class])]) {
if ([v isKindOfClass:[NSString class]]) {
NSString *str = [NSString stringWithFormat:@"'%@'", [DataTableManager encoded:v]];
strName = [[strName stringByAppendingString:str] stringByAppendingString:@","];
} else if ([v isKindOfClass:[NSArray class]]) {
NSString *str = [NSString stringWithFormat:@"'%@'", [self parameterToJson:v]];
strName = [[strName stringByAppendingString:str] stringByAppendingString:@","];
} else if ([v isKindOfClass:[NSDictionary class]]) {
NSString *str = [NSString stringWithFormat:@"'%@'", [self parameterToJson:v]];
strName = [[strName stringByAppendingString:str] stringByAppendingString:@","];
} else {
strName = [[strName stringByAppendingString:[NSString stringWithFormat:@"%@", v]] stringByAppendingString:@","];
}
} else {
strName = [[strName stringByAppendingString:@"NULL"] stringByAppendingString:@","];
}
createSQL = [createSQL stringByAppendingString:strName];
}
if ([createSQL hasSuffix:@","]) {
createSQL = [createSQL substringToIndex:createSQL.length - 1];
}
objc_property_t objc_pk = class_getProperty([self superclass], [@"pk" UTF8String]);
NSString *obj_pk = [self valueForKey:[NSString stringWithCString:property_getName(objc_pk) encoding:NSUTF8StringEncoding]];
createSQL = [createSQL stringByAppendingString:[NSString stringWithFormat:@" WHERE pk = %@", obj_pk]];
return createSQL;
}
- (BOOL)saveDataTable
{
[[DataTableManager recursiveLock] lock];
if (self.pk) {
if ([[DatabaseManager manager].database executeUpdate:[self update]]) {
[[DataTableManager recursiveLock] unlock];
return YES;
} else {
NSLog(@"%@ :: updateDataTable Failure", NSStringFromClass([self class]));
[[DataTableManager recursiveLock] unlock];
return NO;
}
} else {
if ([[DatabaseManager manager].database executeUpdate:[self insert]]) {
self.pk = @([[DatabaseManager manager].database lastInsertRowId]).stringValue;
[[DataTableManager recursiveLock] unlock];
return YES;
} else {
NSLog(@"%@ :: saveDataTable Failure", NSStringFromClass([self class]));
[[DataTableManager recursiveLock] unlock];
return NO;
}
}
}
- (BOOL)deleteDataTable
{
[[DataTableManager recursiveLock] lock];
if ([[DatabaseManager manager].database executeUpdate:[NSString stringWithFormat:@"DELETE FROM %@ WHERE pk = %@", NSStringFromClass([self class]), self.pk]]) {
[[DataTableManager recursiveLock] unlock];
return YES;
} else {
NSLog(@"%@ :: deleteDataTableByProperty :: %@ Failure", NSStringFromClass([self class]), self.pk);
[[DataTableManager recursiveLock] unlock];
return NO;
}
}
+ (NSArray
{
NSLog(@"DB findDataTableBySql : %@", sql);
[[DataTableManager recursiveLock] lock];
FMResultSet *resultSet = [[DatabaseManager manager].database executeQuery:sql];
NSMutableArray *dataTableManagers = [NSMutableArray array];
while ([resultSet next]) {
DataTableManager *dataTableManager = [[self alloc] initWithResultSet:resultSet];
[dataTableManagers addObject:dataTableManager];
}
[resultSet close];
[[DataTableManager recursiveLock] unlock];
return dataTableManagers;
}
+ (NSArray
{
NSString *values = [NSString string];
if ([value isKindOfClass:[NSString class]]) {
values = (NSString *)value;
} else if ([value isKindOfClass:[NSNumber class]]) {
values = ((NSNumber *)value).stringValue;
}
NSString *sql = [NSString string];
if (property) {
sql = [NSString stringWithFormat:@"SELECT * FROM %@ where %@ = %@", NSStringFromClass([self class]), property, values];
} else {
sql = [NSString stringWithFormat:@"SELECT * FROM %@", NSStringFromClass([self class])];
}
[[DataTableManager recursiveLock] lock];
FMResultSet *resultSet = [[DatabaseManager manager].database executeQuery:sql];
NSMutableArray *dataTableManagers = [NSMutableArray array];
while ([resultSet next]) {
DataTableManager *dataTableManager = [[self alloc] initWithResultSet:resultSet];
[dataTableManagers addObject:dataTableManager];
}
[resultSet close];
[[DataTableManager recursiveLock] unlock];
return dataTableManagers;
}
+ (id)findSpecificDataTableByProperty:(NSString *)property value:(id)value
{
NSString *values = [NSString string];
if ([value isKindOfClass:[NSString class]]) {
values = (NSString *)value;
} else if ([value isKindOfClass:[NSNumber class]]) {
values = ((NSNumber *)value).stringValue;
}
NSString *sql = [NSString string];
if (property) {
sql = [NSString stringWithFormat:@"SELECT * FROM %@ where %@ = %@", NSStringFromClass([self class]), property, values];
} else {
sql = [NSString stringWithFormat:@"SELECT * FROM %@", NSStringFromClass([self class])];
}
[[DataTableManager recursiveLock] lock];
FMResultSet *resultSet = [[DatabaseManager manager].database executeQuery:sql];
NSMutableArray *dataTableManagers = [NSMutableArray array];
while ([resultSet next]) {
DataTableManager *dataTableManager = [[self alloc] initWithResultSet:resultSet];
[dataTableManagers addObject:dataTableManager];
}
[resultSet close];
[[DataTableManager recursiveLock] unlock];
return (DataTableManager *)[dataTableManagers firstObject];
}
+ (BOOL)deleteDataTableByProperty:(NSString *)property value:(id)value
{
if (property == nil) {
NSLog(@"%@ :: deleteDataTableByProperty :: property == NULL", NSStringFromClass([self class]));
return NO;
}
[[DataTableManager recursiveLock] lock];
if ([[DatabaseManager manager].database executeUpdate:[NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = %@", NSStringFromClass([self class]), property, value]]) {
[[DataTableManager recursiveLock] unlock];
return YES;
} else {
[[DataTableManager recursiveLock] unlock];
NSLog(@"%@ :: deleteDataTableByProperty :: %@ Failure", NSStringFromClass([self class]), property);
return NO;
}
}
+ (BOOL)deleteDataTableBySQL:(NSString *)sql
{
if (sql == nil) {
NSLog(@"%@ :: deleteDataTableByProperty :: property == NULL", NSStringFromClass([self class]));
return NO;
}
[[DataTableManager recursiveLock] lock];
if ([[DatabaseManager manager].database executeUpdate:sql]) {
[[DataTableManager recursiveLock] unlock];
return YES;
} else {
[[DataTableManager recursiveLock] unlock];
NSLog(@"%@ :: deleteDataTableByProperty :: %@ Failure", NSStringFromClass([self class]), sql);
return NO;
}
}
- (id)copyWithZone:(nullable NSZone *)zone
{
return [DatabaseManager allocWithZone:zone];
}
@end
3.fmd.h
#import
#import "FMDB.h"
//#ifdef __OPTIMIZE__
//# define NSLog(...) {}
//#else
//# define NSLog(...) NSLog(__VA_ARGS__)
//#endif
@interface DatabaseManager : NSObject
@property (nonatomic, readonly) FMDatabase *database;
+ (DatabaseManager *)manager;
/**
* 打开数据库
*
* @param dataBaseName 数据库名
*/
- (void)openDataBase:(NSString *)dataBaseName withKey:(NSString *)key;
/**
* 关闭数据库
*/
- (BOOL)closeDataBase;
/**
* 删除数据库
*
* @param dataBaseName 数据库名
*/
- (void)deleteDataBase:(NSString *)dataBaseName;
@end
4.fmd.m
#import "DatabaseManager.h"
@interface DatabaseManager () {
NSLock *dataBaseLock;
}
@property (nonatomic, strong) FMDatabase *database;
@end
@implementation DatabaseManager
static DatabaseManager *_databaseManager;
+ (DatabaseManager *)manager
{
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
_databaseManager = [[self alloc] init];
});
return _databaseManager;
}
- (void)openDataBase:(NSString *)dataBaseName withKey:(NSString *)key
{
NSLog(@"DatabaseManager: openDataBase dataBaseName = %@ begin", dataBaseName);
if (!dataBaseName || [dataBaseName isEqualToString:@""]) {
NSLog(@"DatabaseManager: openDataBase dataBaseName = %@ begin", dataBaseName);
return;
}
// 设置数据库的文件夹
NSString *sqliteFilePath = [[NSString alloc] initWithFormat:[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/%@"], dataBaseName];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:sqliteFilePath] == NO) {
[fileManager createDirectoryAtPath:sqliteFilePath withIntermediateDirectories:YES attributes:nil error:nil];
}
// 设置数据库的全路径
NSString *filePath = [[NSString alloc] initWithFormat:@"%@/%@.db", sqliteFilePath, dataBaseName];
self.database = [FMDatabase databaseWithPath:filePath];
if ([self.database open]) {
if (key) {
[self.database setKey:key];
}
NSLog(@"FMDatabase Encryption Success");
} else {
NSLog(@"FMDatabase Encryption Failure");
}
}
- (BOOL)closeDataBase
{
return [self.database close];
}
- (void)deleteDataBase:(NSString *)dataBaseName
{
if (dataBaseName == nil) {
return;
}
NSString *sqliteFilePath = [[NSString alloc] initWithFormat:[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/%@"], dataBaseName];
// 设置数据库的全路径
NSString *filePath = [[NSString alloc] initWithFormat:@"%@/%@.db", sqliteFilePath, dataBaseName];
NSFileManager *filemanager = [NSFileManager defaultManager];
if ([filemanager removeItemAtPath:filePath error:nil]) {
NSLog(@"DatabaseManager: deleteDataBase success dataBasePath = %@", filePath);
} else {
NSLog(@"DatabaseManager: deleteDataBase failure dataBasePath = %@", filePath);
}
}
@end