数据库版本迁移顾名思义就是在原有的数据库中更新数据库,数据库中的数据保持不变对表的增、删、该、查。

数据持久化存储:

  • plist文件(属性列表)

  • preference(偏好设置)

  • NSKeyedArchiver(归档)

  • SQLite 3

  • CoreData

这几种方式,我就不介绍其他的了,你可以自己去查询其功能用处,主要SQLite 3对数据的存储。

一、手动更新迭代

由于开发需要需要对消息进行存储,第三方的数据库局限所以我们公司要求对聊天记录、聊天对像、好友进行查询(说白了就是和微信搜索做的一模一样就好了)。

首先你要明白一点为了避免在不通的线程中对相同的数据库进行操作fmdb已经对数据库加锁了。我在数据库中使用了单例,保证工程全局都可以使用。

+ (MyFMDB *)sharedMyFMDB {

static dispatch_once_t once;

static id instance;

dispatch_once(&once, ^{

instance = [self new];

});

return instance;

}

在本篇文章中先说一下大致的数据库迁移的思路,在数据库中存储一个记录版本号的表

Version表。本地写一个记录工程需要不要更新的版本静态变量

#define kCurrentSqliteVersion 0;发布新版本的时候就更改本地的数据库版本号 0 ->1 然后从数据库中取出数据库中的版本号0。

if (oldSqliteVer < kCurrentSqliteVersion) {//sqlite版本小于当前要创建的版本,需要更新

}

依次类推

[self upgrade:oldSqliteVer];    //更新数据库内容

[self insertSqliteVersion:kCurrentSqliteVersion];   //版本号更新需要放在更新数据库内容之后,在没有版本号的数据库版本中,需要在upgrade的地方去创建version表

然后用递归的方式更新

- (void)upgrade:(NSInteger)oldVersion {

if (oldVersion >= kCurrentSqliteVersion) {

return;

}

switch (oldVersion) {

case 0:

[self upgradeFrom0To1];

break;

case 1: //从1版本升级到2版本

[self upgradeFrom1To2];

break;

case 2: //版本拓展:以后若有增加则持续增加

[self upgradeFrom2To3];

break;

case 3: //版本拓展:以后若有增加则持续增加

[self upgradeFrom3To4];

break;

default:

break;

}

oldVersion ++;

// 递归判断是否需要升级:保证老版本从最低升级到当前

[self upgrade:oldVersion];

}

举个例子,在对应的方法里边写自己更新的内容就可以了。

- (void)upgradeFrom1To2 {

//这里执行Sql语句 执行版本1到版本2的更新

FMDatabase * db = [FMDatabase databaseWithPath:self.dbPath];

NSNumber *userId = [NSNumber numberWithLongLong:[UserManeger shareInstance].currentUser.uid];

if ([db open]) {

NSString* SysMessageSql = [NSString stringWithFormat:@"alter table SysMessage add userId int default %@",userId];

NSString* importSql = [NSString stringWithFormat:@"alter table User add userId int default %@",userId];

NSString* importChatSql = [NSString stringWithFormat:@"alter table UserChatMessage add userId int default %@",userId];

BOOL resSysMessage = [db executeUpdate:SysMessageSql];

BOOL res = [db executeUpdate:importSql];

BOOL resChat = [db executeUpdate:importChatSql];

if (res&&resSysMessage&&resChat) {

NSLog(@"更新User,UserChatMessage,SysMessage字段成功");

}else{

NSLog(@"更新User,UserChatMessage,SysMessage字段失败");

}

[db close];

}

}

一、数据库的自动更新迭代

说到自动更新迭代不得不提及runtime,扫盲一下--因为runtime可以获得属性的类型和属性。利用这一点特性进行数据库的自动更新迭代。(由于公司要做数据缓存,才写了这个自动更新换代的数据库)

首先和手动更新迭代一样创建单例数据库对象,全局调用

//创建CacheFMDB类的对象

static CacheFMDB* _instance = nil;

+ (instancetype)sharedCacheFMDB

{

static dispatch_once_t onceToken ;

dispatch_once(&onceToken, ^{

_instance = [[self alloc] init] ;

}) ;

return _instance ;

}

我们首先要想怎么拼接数据库语句?

CREATE TABLE PositionTable (id integer PRIMARY KEY NOT NULL , isCollection int , isMyPosition int , isPublicPosition int , newAnswer int , statusId int , importantPos int , matchRate float , urgency int , positionType int , grabOrderPositionStatus int , productType int , lastUpdateTime double , uid double , annualSalary double , areaName text , cityId double , companyId double , createTime double , positionLogo text , companyName text , positionId double , maxShowAnnualSalary double , minShowAnnualSalary double , modifyTime double , publishTime double , suitableTalentCount double , positionTitle text , updateTime double , positionAveFeedBackDay text )

像上边这一个创建数据库语句的方法,

REATE TABLE %@ (id integer PRIMARY KEY NOT NULL

这些是固定的然后呢后边是不通的属性和对应的类型拼接而成。

那么c语言对应的oc语言的类型

static NSString *intType     = @"i"; // int_32t,int

static NSString *longlongType = @"q"; // long,或者longlong

static NSString *floatType   = @"f"; // float

static NSString *doubleType  = @"d"; // double

static NSString *boolType    = @"B"; // bool

static NSString *imageType   = @"UIImage"; // UIImage 类型

static NSString *stringType  = @"NSString"; // NSString 类型

static NSString *numberType  = @"NSNumber"; // NSNumber 类型

用model便利每一个属性的变量名

Ivar *ivars = class_copyIvarList([model class], &count);

我们发现变量名字都多余了一个"_"。包括oc对应的储存的数据库语句。所以我进行进一步处理

for (int i = 0; i < count; i++) {

Ivar ivar = ivars[i];

//根据ivar获得其成员变量的名称

const char *name = ivar_getName(ivar);

//C的字符串转OC的字符串

NSString *key = [NSString stringWithUTF8String:name];

//放入数组

NSString *keyString = [key stringByReplacingOccurrencesOfString:@"_" withString:@""];

[_ivarsArray addObject:keyString];

// 获取变量类型,c字符串

const char *cType = ivar_getTypeEncoding(ivar);

//C的字符串转OC的字符串

NSString *Type = [NSString stringWithUTF8String:cType];

//基本类型数组库类型转化

NSLog(@"======%@",Type);

NSString *repleaceString = [self repleaceStringWithCSting:Type];

//放入数组

[_typeArray addObject:repleaceString];

}

/*****属性和数据库数据的类型相互转换*****/

- (NSString *)repleaceStringWithCSting:(NSString *)cSting{

if (![cSting isEqualToString:@""]) {

if ([cSting isEqualToString:@"i"]) {

return @"int";

}else if([cSting isEqualToString:@"q"]){

return @"double";

}else if([cSting isEqualToString:@"f"]){

return @"float";

}else if([cSting isEqualToString:@"d"]){

return @"double";

}else if([cSting isEqualToString:@"B"]){

return @"int";

}else if([cSting containsString:@"NSString"]){

return @"text";

}else if([cSting containsString:@"NSNumber"]){

return @"long";

}

NSAssert(1, @"handleSqliteTable类中 model的属性状态不对导致数据库状态不对,请核对后再拨");

return @"未知";

}else return nil;

}

准备工作就绪

我在处理数据库语句的时候写了个外部调用方法

//

//  handleSqliteTable.h

//  RuntimeDemo

//

//  Created by peter on 16/3/18.

//  Copyright © 2016年 hunteron All rights reserved.

//

#import <Foundation/Foundation.h>

@interface handleSqliteTable : NSObject

/**

*  获取 model 中的所有属性数组

*  model      需要缓存的对象

*/

- (NSArray *)ivarsArrayWithModel:(NSObject *)model;

/**

*  返回 数据库表的语句

*  tableName  表名字

*  model      需要缓存的对象

*/

- (NSString *)sqliteStingWithTableName:(NSString *)tableName model:(NSObject *)model;

/*

*获取属性的类型,并转化为c的类型,并进行拼接

*/

- (NSArray *)attribleArray:(NSArray *)attribleArray model:(NSObject *)model;

@end

-----------------------数据库语句的拼接.m文件

//数据库拼接

- (NSString *)sqliteStingWithTableName:(NSString *)tableName model:(NSObject *)model{

[self test1:model];

return  [self complatSqiteAttribiteA:_ivarsArray typeA:_typeArray tableName:tableName];

}

- (NSString *)complatSqiteAttribiteA:(NSArray *)attribiteA typeA:(NSArray *)typeA tableName:(NSString *)tableName{

NSString *string = [NSString stringWithFormat:@"CREATE TABLE %@ (id integer PRIMARY KEY NOT NULL",tableName];

NSString *beginString = @"";

for (int i = 0; i < attribiteA.count;i ++) {

NSString *atAndType = [self sqiteStringAttribite:(NSString *)attribiteA[i] type:(NSString *)typeA[i]];

beginString = [beginString stringByAppendingString:atAndType];

}

return [NSString stringWithFormat:@"%@ %@)",string,beginString];

}

/**

*  数据库语句拼接

*/

- (NSString *)sqiteStringAttribite:(NSString *)attribite type:(NSString *)type{

return [NSString stringWithFormat:@", %@ %@ ",attribite,type];

}

你把要转化的model通过

/**

*  返回 数据库表的语句

*  tableName  表名字

*  model      需要缓存的对象

*/

- (NSString *)sqliteStingWithTableName:(NSString *)tableName model:(NSObject *)model;

传过来,例如:

NSString * talentSql = [handel sqliteStingWithTableName:NSStringFromClass([talent class]) model:talent];

就会生成对应的数据库语句。

CREATE TABLE PositionTable (id integer PRIMARY KEY NOT NULL , isCollection int , isMyPosition int , isPublicPosition int , newAnswer int , statusId int , importantPos int , matchRate float , urgency int , positionType int , grabOrderPositionStatus int , productType int , lastUpdateTime double , uid double , annualSalary double , areaName text , cityId double , companyId double , createTime double , positionLogo text , companyName text , positionId double , maxShowAnnualSalary double , minShowAnnualSalary double , modifyTime double , publishTime double , suitableTalentCount double , positionTitle text , updateTime double , positionAveFeedBackDay text )

这才是第一步的数据库语句的拼接,是不是到这一步感觉已经好麻烦了??????没事不要着急

数据库的迁移无非是:

1.新增数据库(没有路径的情况)

2.新增表

3.增加字段

4.删除字段(sqlit3不支持字段的删除)

一、判断数据库路径是否存在

NSFileManager * fileManager = [NSFileManager defaultManager];

if (![fileManager fileExistsAtPath:kCacheDBPath]) {

这里我就不说了

}else if([self needUpdateTabelArray:_modelArray]) {

NSLog(@"数据库已经存在路径,但需要新增数据库表:%@",kCacheDBPath);

//需要更新的表的名字

NSArray *tabelName = [self needUpdateTable:_modelArray];

//新建数据表

[self createNewTable:tabelName];

}else{

NSLog(@"数据库已经存在路径,不需要新增数据库表:%@",kCacheDBPath);

}

二、新增表

我会创建一个数组去记录表名字

_modelArray = [NSMutableArray array];

handleSqliteTable *handel = [[handleSqliteTable alloc]init];

//职位列表

PositionTable *position = [[PositionTable alloc]init];

[_modelArray addObject:position];

NSString * positionSql = [handel sqliteStingWithTableName:NSStringFromClass([position class]) model:position];

//人才列表

TalentTable *talent = [[TalentTable alloc]init];

[_modelArray addObject:talent];

NSString * talentSql = [handel sqliteStingWithTableName:NSStringFromClass([talent class]) model:talent];

判断有没有这个表数据库中

//多个表是不是要更新

- (BOOL)needUpdateTabelArray:(NSArray *)array{

for (NSObject *obj in array) {

NSString *string = NSStringFromClass([obj class]);

NSLog(@"=====%d",[self needUpdateTabel:string]);

if(![self needUpdateTabel:string]) {

return YES;

}

}

return NO;

}

//检查数据库的表是否存在

- (BOOL)needUpdateTabel:(NSString *)tableName{

FMDatabase * db = [FMDatabase databaseWithPath:kCacheDBPath];

BOOL need = NO;

if ([db open]) {

//得到所有的表表名

FMResultSet *rs = [db 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(@"isTableOK %ld", (long)count);

if (0 == count)

{

need = NO;

}

else

{

need = YES;

}

}

[rs close];

[db close];

}

return need;

}

表不存在就

//新增表

- (void)createNewTable:(NSArray *)array{

for (NSObject *obj in _modelArray) {

NSString *string = NSStringFromClass([obj class]);

if ([array containsObject:string]) {

handleSqliteTable *handel = [[handleSqliteTable alloc]init];

NSString * tableSqilet = [handel sqliteStingWithTableName:NSStringFromClass([obj class]) model:obj];

[self createATable:tableSqilet];

}

}

}

检查表里边的字段需不需要更新,去现在有的model的属性和数据库中的比较找出需要更新的字段

//自动更新机制 表的对应的model变化,需要对表做相应的增加和删除操做

for (NSObject *obj in _modelArray) {

//runtime 获取现有model的所有属性string

NSArray *array = [handel ivarsArrayWithModel:obj];

//对比数据库和现有的属性sting

[self checkAndUpdateTable:obj newAttribe:array];

}

//判断新老表中有没有新增字段

- (void)checkAndUpdateTable:(NSObject*)objName newAttribe:(NSArray *)newAttribe{

//数据库中现有的字段

NSMutableArray *sqliteArray = [NSMutableArray array];

NSString *tableName = NSStringFromClass([objName class]);

FMDatabase * db = [FMDatabase databaseWithPath:kCacheDBPath];

if ([db open]) {

NSString * sql = [NSString stringWithFormat:@"select * from %@",tableName] ;

FMResultSet * rs = [db executeQuery:sql];

NSDictionary * dict =   [rs columnNameToIndexMap];

[sqliteArray addObjectsFromArray:[dict allKeys]];

[db close];

}

//需要更新的字段

NSMutableArray *needUpdateName =[NSMutableArray array];

for (NSString *string in newAttribe) {

NSString * lowercaseString = [string lowercaseString];

if (![sqliteArray containsObject:lowercaseString]) {

[needUpdateName addObject:string];

}

}

handleSqliteTable *handel = [[handleSqliteTable alloc]init];

if (needUpdateName.count > 0) {

NSArray *array = [handel attribleArray:needUpdateName model:objName];

//更新

[self updateTabelupdateString:array tableName:tableName];

}

}

//增加新的表字段

- (void)updateTabelupdateString:(NSArray *)updateArray tableName:(NSString *)tableName{

FMDatabase * db = [FMDatabase databaseWithPath:kCacheDBPath];

if ([db open]) {

for (NSString *updateString in updateArray) {

NSString* SysMessageSql = [NSString stringWithFormat:@"alter table %@ add %@",tableName,updateString];

BOOL resSysMessage = [db executeUpdate:SysMessageSql];

if (resSysMessage) {

NSLog(@"新增%@表字段%@成功",tableName,updateString);

}else{

NSLog(@"新增%@表字段%@失败",tableName,updateString);

}

}

[db close];

}

}

这就完成了9成的更新功能。

还有一成更新表的增删改查语句。同样的道理更新数据库语句.......

ios开发 数据库版本迁移手动更新迭代和自动更新迭代的更多相关文章

  1. ios开发数据库版本迁移手动更新迭代和自动更新迭代艺术(二)

    由于大家都热衷于对ios开发数据库版本迁移手动更新迭代和自动更新迭代艺术(一)的浏览下面我分享下我的源文件git仓库: 用法(这边我是对缓存的一些操作不需要可以省去):https://github.c ...

  2. iOS 开发之版本上线更新流程

    iOS 开发之版本上线更新流程   把自己app上线的流程记录下来,希望能够让自己加深印象,也能帮助到一些人便是极好的! 证书和描述文件的配置就不写了,直接配置工程吧. 大致把我自己上线的流程写一下: ...

  3. iOS开发数据库篇—SQLite简单介绍

    iOS开发数据库篇—SQLite简单介绍 一.离线缓存 在项目开发中,通常都需要对数据进行离线缓存的处理,如新闻数据的离线缓存等. 说明:离线缓存一般都是把数据保存到项目的沙盒中.有以下几种方式 (1 ...

  4. iOS开发数据库篇—SQL

    iOS开发数据库篇—SQL 一.SQL语句 如果要在程序运行过程中操作数据库中的数据,那得先学会使用SQL语句 1.什么是SQL SQL(structured query language):结构化查 ...

  5. iOS开发数据库篇—SQLite常用的函数

    iOS开发数据库篇—SQLite常用的函数 一.简单说明 1.打开数据库 int sqlite3_open( const char *filename,   // 数据库的文件路径 sqlite3 * ...

  6. iOS开发数据库篇—FMDB简单介绍

    iOS开发数据库篇—FMDB简单介绍 一.简单说明 1.什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的方式封装了SQLite的C语言API 2.FMDB的优点 使用起来 ...

  7. iOS开发数据库篇—FMDB数据库队列

    iOS开发数据库篇—FMDB数据库队列 一.代码示例 1.需要先导入FMDB框架和头文件,由于该框架依赖于libsqlite库,所以还应该导入该库. 2.代码如下: // // YYViewContr ...

  8. 【转】 iOS开发数据库篇—SQLite简单介绍

    开始学SQLite啦, 原文: http://www.cnblogs.com/wendingding/p/3868893.html iOS开发数据库篇—SQLite简单介绍 一.离线缓存 在项目开发中 ...

  9. iOS开发数据库-FMDB

    前言 FMDB是以OC的方式封装了SQLite的C语言API,使用起来更加面向对象,省去了很多麻烦.冗余的C语言代码:对比苹果自带的Core Data框架,更加轻量级和灵活:提供了多线程安全的数据库操 ...

随机推荐

  1. Android tabhost下的activity怎样获取传来的值

    android tabhost下的activity怎样获取传来的值,具体解决方案如下: 解决方案: 其他activity设置intent:Intent intent=new Intent(); int ...

  2. nyist 510昂贵的聘礼

    /* 好好的图论题啊,最短路的应用,dijkstra算法 */ #include <iostream> using namespace std; const int INF=100000; ...

  3. js构造函数式编程

    1.函数式编程 //创建和初始化地图函数: function initMap(){ createMap();//创建地图 setMapEvent();//设置地图事件 addMapControl(); ...

  4. PLSQL Developer调试 存储过程和触发器

    1. 打开PL/SQL Developer如果 在机器上安装了PL/SQL Developer的话,打开PL/SQL Developer界面输入 用户名,密码和host名字,这个跟在程序中web.co ...

  5. uva753 A Plug for UNIX

    最大流. 流可以对应一种分配方式. 显然最大流就可以表示最多匹配数 #include<cstdio> #include<algorithm> #include<cstri ...

  6. 【 D3.js 高级系列 — 1.0 】 文本的换行

    在 SVG 中添加文本是使用 text 元素.但是,这个元素不能够自动换行,超出的部分就显示不出来了,怎么办呢? 高级系列开篇前言 从今天开始写高级系列教程.还是那句话,由于本人实力有限,不一定保证入 ...

  7. Java中的字符串驻留(String Interning)

    1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. ne ...

  8. iOS使用WSDL2ObjC工具调用Webservice接口

    1. 下载 WSDL2ObjC.app https://code.google.com/archive/p/wsdl2objc/downloads 2:下载WSDL文件 2.1一般情况下, 你会得到这 ...

  9. java中volatitle关键字的作用

    用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致 的情况.volatile就是用 ...

  10. linux中的设备名称和设备号

    看赵炯博士的<linux 0.11 源代码注释>已经两三周了,从今天起开始将一些个人总结和感悟分小标题写出来,聊作记忆以供后来查看.在linux0.11源码的 /linux/boot/bo ...